1 /*
2 * Copyright 2008 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9 #include "SkPathMeasure.h"
10 #include "SkPathMeasurePriv.h"
11 #include "SkGeometry.h"
12 #include "SkPath.h"
13 #include "SkTSearch.h"
14
15 #define kMaxTValue 0x3FFFFFFF
16
tValue2Scalar(int t)17 static inline SkScalar tValue2Scalar(int t) {
18 SkASSERT((unsigned)t <= kMaxTValue);
19 const SkScalar kMaxTReciprocal = 1.0f / kMaxTValue;
20 return t * kMaxTReciprocal;
21 }
22
getScalarT() const23 SkScalar SkPathMeasure::Segment::getScalarT() const {
24 return tValue2Scalar(fTValue);
25 }
26
NextSegment(const Segment * seg)27 const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) {
28 unsigned ptIndex = seg->fPtIndex;
29
30 do {
31 ++seg;
32 } while (seg->fPtIndex == ptIndex);
33 return seg;
34 }
35
SkPathMeasure_segTo(const SkPoint pts[],unsigned segType,SkScalar startT,SkScalar stopT,SkPath * dst)36 void SkPathMeasure_segTo(const SkPoint pts[], unsigned segType,
37 SkScalar startT, SkScalar stopT, SkPath* dst) {
38 SkASSERT(startT >= 0 && startT <= SK_Scalar1);
39 SkASSERT(stopT >= 0 && stopT <= SK_Scalar1);
40 SkASSERT(startT <= stopT);
41
42 if (startT == stopT) {
43 /* if the dash as a zero-length on segment, add a corresponding zero-length line.
44 The stroke code will add end caps to zero length lines as appropriate */
45 SkPoint lastPt;
46 SkAssertResult(dst->getLastPt(&lastPt));
47 dst->lineTo(lastPt);
48 return;
49 }
50
51 SkPoint tmp0[7], tmp1[7];
52
53 switch (segType) {
54 case kLine_SegType:
55 if (SK_Scalar1 == stopT) {
56 dst->lineTo(pts[1]);
57 } else {
58 dst->lineTo(SkScalarInterp(pts[0].fX, pts[1].fX, stopT),
59 SkScalarInterp(pts[0].fY, pts[1].fY, stopT));
60 }
61 break;
62 case kQuad_SegType:
63 if (0 == startT) {
64 if (SK_Scalar1 == stopT) {
65 dst->quadTo(pts[1], pts[2]);
66 } else {
67 SkChopQuadAt(pts, tmp0, stopT);
68 dst->quadTo(tmp0[1], tmp0[2]);
69 }
70 } else {
71 SkChopQuadAt(pts, tmp0, startT);
72 if (SK_Scalar1 == stopT) {
73 dst->quadTo(tmp0[3], tmp0[4]);
74 } else {
75 SkChopQuadAt(&tmp0[2], tmp1, (stopT - startT) / (1 - startT));
76 dst->quadTo(tmp1[1], tmp1[2]);
77 }
78 }
79 break;
80 case kConic_SegType: {
81 SkConic conic(pts[0], pts[2], pts[3], pts[1].fX);
82
83 if (0 == startT) {
84 if (SK_Scalar1 == stopT) {
85 dst->conicTo(conic.fPts[1], conic.fPts[2], conic.fW);
86 } else {
87 SkConic tmp[2];
88 if (conic.chopAt(stopT, tmp)) {
89 dst->conicTo(tmp[0].fPts[1], tmp[0].fPts[2], tmp[0].fW);
90 }
91 }
92 } else {
93 if (SK_Scalar1 == stopT) {
94 SkConic tmp1[2];
95 if (conic.chopAt(startT, tmp1)) {
96 dst->conicTo(tmp1[1].fPts[1], tmp1[1].fPts[2], tmp1[1].fW);
97 }
98 } else {
99 SkConic tmp;
100 conic.chopAt(startT, stopT, &tmp);
101 dst->conicTo(tmp.fPts[1], tmp.fPts[2], tmp.fW);
102 }
103 }
104 } break;
105 case kCubic_SegType:
106 if (0 == startT) {
107 if (SK_Scalar1 == stopT) {
108 dst->cubicTo(pts[1], pts[2], pts[3]);
109 } else {
110 SkChopCubicAt(pts, tmp0, stopT);
111 dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]);
112 }
113 } else {
114 SkChopCubicAt(pts, tmp0, startT);
115 if (SK_Scalar1 == stopT) {
116 dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
117 } else {
118 SkChopCubicAt(&tmp0[3], tmp1, (stopT - startT) / (1 - startT));
119 dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
120 }
121 }
122 break;
123 default:
124 SK_ABORT("unknown segType");
125 }
126 }
127
128 ///////////////////////////////////////////////////////////////////////////////
129
tspan_big_enough(int tspan)130 static inline int tspan_big_enough(int tspan) {
131 SkASSERT((unsigned)tspan <= kMaxTValue);
132 return tspan >> 10;
133 }
134
135 // can't use tangents, since we need [0..1..................2] to be seen
136 // as definitely not a line (it is when drawn, but not parametrically)
137 // so we compare midpoints
138 #define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up
139
quad_too_curvy(const SkPoint pts[3])140 bool SkPathMeasure::quad_too_curvy(const SkPoint pts[3]) {
141 // diff = (a/4 + b/2 + c/4) - (a/2 + c/2)
142 // diff = -a/4 + b/2 - c/4
143 SkScalar dx = SkScalarHalf(pts[1].fX) -
144 SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX));
145 SkScalar dy = SkScalarHalf(pts[1].fY) -
146 SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY));
147
148 SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy));
149 return dist > fTolerance;
150 }
151
conic_too_curvy(const SkPoint & firstPt,const SkPoint & midTPt,const SkPoint & lastPt)152 bool SkPathMeasure::conic_too_curvy(const SkPoint& firstPt, const SkPoint& midTPt,
153 const SkPoint& lastPt) {
154 SkPoint midEnds = firstPt + lastPt;
155 midEnds *= 0.5f;
156 SkVector dxy = midTPt - midEnds;
157 SkScalar dist = SkMaxScalar(SkScalarAbs(dxy.fX), SkScalarAbs(dxy.fY));
158 return dist > fTolerance;
159 }
160
cheap_dist_exceeds_limit(const SkPoint & pt,SkScalar x,SkScalar y)161 bool SkPathMeasure::cheap_dist_exceeds_limit(const SkPoint& pt,
162 SkScalar x, SkScalar y) {
163 SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
164 // just made up the 1/2
165 return dist > fTolerance;
166 }
167
cubic_too_curvy(const SkPoint pts[4])168 bool SkPathMeasure::cubic_too_curvy(const SkPoint pts[4]) {
169 return cheap_dist_exceeds_limit(pts[1],
170 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
171 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
172 ||
173 cheap_dist_exceeds_limit(pts[2],
174 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
175 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
176 }
177
quad_folded_len(const SkPoint pts[3])178 static SkScalar quad_folded_len(const SkPoint pts[3]) {
179 SkScalar t = SkFindQuadMaxCurvature(pts);
180 SkPoint pt = SkEvalQuadAt(pts, t);
181 SkVector a = pts[2] - pt;
182 SkScalar result = a.length();
183 if (0 != t) {
184 SkVector b = pts[0] - pt;
185 result += b.length();
186 }
187 SkASSERT(SkScalarIsFinite(result));
188 return result;
189 }
190
191 /* from http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/ */
192 /* This works -- more needs to be done to see if it is performant on all platforms.
193 To use this to measure parts of quads requires recomputing everything -- perhaps
194 a chop-like interface can start from a larger measurement and get two new measurements
195 with one call here.
196 */
compute_quad_len(const SkPoint pts[3])197 static SkScalar compute_quad_len(const SkPoint pts[3]) {
198 SkPoint a,b;
199 a.fX = pts[0].fX - 2 * pts[1].fX + pts[2].fX;
200 a.fY = pts[0].fY - 2 * pts[1].fY + pts[2].fY;
201 SkScalar A = 4 * (a.fX * a.fX + a.fY * a.fY);
202 if (0 == A) {
203 a = pts[2] - pts[0];
204 return a.length();
205 }
206 b.fX = 2 * (pts[1].fX - pts[0].fX);
207 b.fY = 2 * (pts[1].fY - pts[0].fY);
208 SkScalar B = 4 * (a.fX * b.fX + a.fY * b.fY);
209 SkScalar C = b.fX * b.fX + b.fY * b.fY;
210 SkScalar Sabc = 2 * SkScalarSqrt(A + B + C);
211 SkScalar A_2 = SkScalarSqrt(A);
212 SkScalar A_32 = 2 * A * A_2;
213 SkScalar C_2 = 2 * SkScalarSqrt(C);
214 SkScalar BA = B / A_2;
215 if (0 == BA + C_2) {
216 return quad_folded_len(pts);
217 }
218 SkScalar J = A_32 * Sabc + A_2 * B * (Sabc - C_2);
219 SkScalar K = 4 * C * A - B * B;
220 SkScalar L = (2 * A_2 + BA + Sabc) / (BA + C_2);
221 if (L <= 0) {
222 return quad_folded_len(pts);
223 }
224 SkScalar M = SkScalarLog(L);
225 SkScalar result = (J + K * M) / (4 * A_32);
226 SkASSERT(SkScalarIsFinite(result));
227 return result;
228 }
229
compute_quad_segs(const SkPoint pts[3],SkScalar distance,int mint,int maxt,int ptIndex)230 SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
231 SkScalar distance, int mint, int maxt, int ptIndex) {
232 if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
233 SkPoint tmp[5];
234 int halft = (mint + maxt) >> 1;
235
236 SkChopQuadAtHalf(pts, tmp);
237 distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
238 distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
239 } else {
240 SkScalar d = SkPoint::Distance(pts[0], pts[2]);
241 SkScalar prevD = distance;
242 distance += d;
243 if (distance > prevD) {
244 Segment* seg = fSegments.append();
245 seg->fDistance = distance;
246 seg->fPtIndex = ptIndex;
247 seg->fType = kQuad_SegType;
248 seg->fTValue = maxt;
249 }
250 }
251 return distance;
252 }
253
compute_conic_segs(const SkConic & conic,SkScalar distance,int mint,const SkPoint & minPt,int maxt,const SkPoint & maxPt,int ptIndex)254 SkScalar SkPathMeasure::compute_conic_segs(const SkConic& conic, SkScalar distance,
255 int mint, const SkPoint& minPt,
256 int maxt, const SkPoint& maxPt, int ptIndex) {
257 int halft = (mint + maxt) >> 1;
258 SkPoint halfPt = conic.evalAt(tValue2Scalar(halft));
259 if (tspan_big_enough(maxt - mint) && conic_too_curvy(minPt, halfPt, maxPt)) {
260 distance = this->compute_conic_segs(conic, distance, mint, minPt, halft, halfPt, ptIndex);
261 distance = this->compute_conic_segs(conic, distance, halft, halfPt, maxt, maxPt, ptIndex);
262 } else {
263 SkScalar d = SkPoint::Distance(minPt, maxPt);
264 SkScalar prevD = distance;
265 distance += d;
266 if (distance > prevD) {
267 Segment* seg = fSegments.append();
268 seg->fDistance = distance;
269 seg->fPtIndex = ptIndex;
270 seg->fType = kConic_SegType;
271 seg->fTValue = maxt;
272 }
273 }
274 return distance;
275 }
276
compute_cubic_segs(const SkPoint pts[4],SkScalar distance,int mint,int maxt,int ptIndex)277 SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
278 SkScalar distance, int mint, int maxt, int ptIndex) {
279 if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) {
280 SkPoint tmp[7];
281 int halft = (mint + maxt) >> 1;
282
283 SkChopCubicAtHalf(pts, tmp);
284 distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
285 distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
286 } else {
287 SkScalar d = SkPoint::Distance(pts[0], pts[3]);
288 SkScalar prevD = distance;
289 distance += d;
290 if (distance > prevD) {
291 Segment* seg = fSegments.append();
292 seg->fDistance = distance;
293 seg->fPtIndex = ptIndex;
294 seg->fType = kCubic_SegType;
295 seg->fTValue = maxt;
296 }
297 }
298 return distance;
299 }
300
buildSegments()301 void SkPathMeasure::buildSegments() {
302 SkPoint pts[4];
303 int ptIndex = fFirstPtIndex;
304 SkScalar distance = 0;
305 bool isClosed = fForceClosed;
306 bool firstMoveTo = ptIndex < 0;
307 Segment* seg;
308
309 /* Note:
310 * as we accumulate distance, we have to check that the result of +=
311 * actually made it larger, since a very small delta might be > 0, but
312 * still have no effect on distance (if distance >>> delta).
313 *
314 * We do this check below, and in compute_quad_segs and compute_cubic_segs
315 */
316 fSegments.reset();
317 bool done = false;
318 do {
319 switch (fIter.next(pts)) {
320 case SkPath::kMove_Verb:
321 ptIndex += 1;
322 fPts.append(1, pts);
323 if (!firstMoveTo) {
324 done = true;
325 break;
326 }
327 firstMoveTo = false;
328 break;
329
330 case SkPath::kLine_Verb: {
331 SkScalar d = SkPoint::Distance(pts[0], pts[1]);
332 SkASSERT(d >= 0);
333 SkScalar prevD = distance;
334 distance += d;
335 if (distance > prevD) {
336 seg = fSegments.append();
337 seg->fDistance = distance;
338 seg->fPtIndex = ptIndex;
339 seg->fType = kLine_SegType;
340 seg->fTValue = kMaxTValue;
341 fPts.append(1, pts + 1);
342 ptIndex++;
343 }
344 } break;
345
346 case SkPath::kQuad_Verb: {
347 SkScalar prevD = distance;
348 if (false) {
349 SkScalar length = compute_quad_len(pts);
350 if (length) {
351 distance += length;
352 Segment* seg = fSegments.append();
353 seg->fDistance = distance;
354 seg->fPtIndex = ptIndex;
355 seg->fType = kQuad_SegType;
356 seg->fTValue = kMaxTValue;
357 }
358 } else {
359 distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex);
360 }
361 if (distance > prevD) {
362 fPts.append(2, pts + 1);
363 ptIndex += 2;
364 }
365 } break;
366
367 case SkPath::kConic_Verb: {
368 const SkConic conic(pts, fIter.conicWeight());
369 SkScalar prevD = distance;
370 distance = this->compute_conic_segs(conic, distance, 0, conic.fPts[0],
371 kMaxTValue, conic.fPts[2], ptIndex);
372 if (distance > prevD) {
373 // we store the conic weight in our next point, followed by the last 2 pts
374 // thus to reconstitue a conic, you'd need to say
375 // SkConic(pts[0], pts[2], pts[3], weight = pts[1].fX)
376 fPts.append()->set(conic.fW, 0);
377 fPts.append(2, pts + 1);
378 ptIndex += 3;
379 }
380 } break;
381
382 case SkPath::kCubic_Verb: {
383 SkScalar prevD = distance;
384 distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex);
385 if (distance > prevD) {
386 fPts.append(3, pts + 1);
387 ptIndex += 3;
388 }
389 } break;
390
391 case SkPath::kClose_Verb:
392 isClosed = true;
393 break;
394
395 case SkPath::kDone_Verb:
396 done = true;
397 break;
398 }
399 } while (!done);
400
401 fLength = distance;
402 fIsClosed = isClosed;
403 fFirstPtIndex = ptIndex;
404
405 #ifdef SK_DEBUG
406 {
407 const Segment* seg = fSegments.begin();
408 const Segment* stop = fSegments.end();
409 unsigned ptIndex = 0;
410 SkScalar distance = 0;
411 // limit the loop to a reasonable number; pathological cases can run for minutes
412 int maxChecks = 10000000; // set to INT_MAX to defeat the check
413 while (seg < stop) {
414 SkASSERT(seg->fDistance > distance);
415 SkASSERT(seg->fPtIndex >= ptIndex);
416 SkASSERT(seg->fTValue > 0);
417
418 const Segment* s = seg;
419 while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex && --maxChecks > 0) {
420 SkASSERT(s[0].fType == s[1].fType);
421 SkASSERT(s[0].fTValue < s[1].fTValue);
422 s += 1;
423 }
424
425 distance = seg->fDistance;
426 ptIndex = seg->fPtIndex;
427 seg += 1;
428 }
429 // SkDebugf("\n");
430 }
431 #endif
432 }
433
compute_pos_tan(const SkPoint pts[],unsigned segType,SkScalar t,SkPoint * pos,SkVector * tangent)434 static void compute_pos_tan(const SkPoint pts[], unsigned segType,
435 SkScalar t, SkPoint* pos, SkVector* tangent) {
436 switch (segType) {
437 case kLine_SegType:
438 if (pos) {
439 pos->set(SkScalarInterp(pts[0].fX, pts[1].fX, t),
440 SkScalarInterp(pts[0].fY, pts[1].fY, t));
441 }
442 if (tangent) {
443 tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY);
444 }
445 break;
446 case kQuad_SegType:
447 SkEvalQuadAt(pts, t, pos, tangent);
448 if (tangent) {
449 tangent->normalize();
450 }
451 break;
452 case kConic_SegType: {
453 SkConic(pts[0], pts[2], pts[3], pts[1].fX).evalAt(t, pos, tangent);
454 if (tangent) {
455 tangent->normalize();
456 }
457 } break;
458 case kCubic_SegType:
459 SkEvalCubicAt(pts, t, pos, tangent, nullptr);
460 if (tangent) {
461 tangent->normalize();
462 }
463 break;
464 default:
465 SkDEBUGFAIL("unknown segType");
466 }
467 }
468
469
470 ////////////////////////////////////////////////////////////////////////////////
471 ////////////////////////////////////////////////////////////////////////////////
472
SkPathMeasure()473 SkPathMeasure::SkPathMeasure() {
474 fPath = nullptr;
475 fTolerance = CHEAP_DIST_LIMIT;
476 fLength = -1; // signal we need to compute it
477 fForceClosed = false;
478 fFirstPtIndex = -1;
479 }
480
SkPathMeasure(const SkPath & path,bool forceClosed,SkScalar resScale)481 SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed, SkScalar resScale) {
482 fPath = &path;
483 fTolerance = CHEAP_DIST_LIMIT * SkScalarInvert(resScale);
484 fLength = -1; // signal we need to compute it
485 fForceClosed = forceClosed;
486 fFirstPtIndex = -1;
487
488 fIter.setPath(path, forceClosed);
489 }
490
~SkPathMeasure()491 SkPathMeasure::~SkPathMeasure() {}
492
493 /** Assign a new path, or null to have none.
494 */
setPath(const SkPath * path,bool forceClosed)495 void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) {
496 fPath = path;
497 fLength = -1; // signal we need to compute it
498 fForceClosed = forceClosed;
499 fFirstPtIndex = -1;
500
501 if (path) {
502 fIter.setPath(*path, forceClosed);
503 }
504 fSegments.reset();
505 fPts.reset();
506 }
507
getLength()508 SkScalar SkPathMeasure::getLength() {
509 if (fPath == nullptr) {
510 return 0;
511 }
512 if (fLength < 0) {
513 this->buildSegments();
514 }
515 if (SkScalarIsNaN(fLength)) {
516 fLength = 0;
517 }
518 SkASSERT(fLength >= 0);
519 return fLength;
520 }
521
522 template <typename T, typename K>
SkTKSearch(const T base[],int count,const K & key)523 int SkTKSearch(const T base[], int count, const K& key) {
524 SkASSERT(count >= 0);
525 if (count <= 0) {
526 return ~0;
527 }
528
529 SkASSERT(base != nullptr); // base may be nullptr if count is zero
530
531 int lo = 0;
532 int hi = count - 1;
533
534 while (lo < hi) {
535 int mid = (hi + lo) >> 1;
536 if (base[mid].fDistance < key) {
537 lo = mid + 1;
538 } else {
539 hi = mid;
540 }
541 }
542
543 if (base[hi].fDistance < key) {
544 hi += 1;
545 hi = ~hi;
546 } else if (key < base[hi].fDistance) {
547 hi = ~hi;
548 }
549 return hi;
550 }
551
distanceToSegment(SkScalar distance,SkScalar * t)552 const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
553 SkScalar distance, SkScalar* t) {
554 SkDEBUGCODE(SkScalar length = ) this->getLength();
555 SkASSERT(distance >= 0 && distance <= length);
556
557 const Segment* seg = fSegments.begin();
558 int count = fSegments.count();
559
560 int index = SkTKSearch<Segment, SkScalar>(seg, count, distance);
561 // don't care if we hit an exact match or not, so we xor index if it is negative
562 index ^= (index >> 31);
563 seg = &seg[index];
564
565 // now interpolate t-values with the prev segment (if possible)
566 SkScalar startT = 0, startD = 0;
567 // check if the prev segment is legal, and references the same set of points
568 if (index > 0) {
569 startD = seg[-1].fDistance;
570 if (seg[-1].fPtIndex == seg->fPtIndex) {
571 SkASSERT(seg[-1].fType == seg->fType);
572 startT = seg[-1].getScalarT();
573 }
574 }
575
576 SkASSERT(seg->getScalarT() > startT);
577 SkASSERT(distance >= startD);
578 SkASSERT(seg->fDistance > startD);
579
580 *t = startT + (seg->getScalarT() - startT) * (distance - startD) / (seg->fDistance - startD);
581 return seg;
582 }
583
getPosTan(SkScalar distance,SkPoint * pos,SkVector * tangent)584 bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos, SkVector* tangent) {
585 if (nullptr == fPath) {
586 return false;
587 }
588
589 SkScalar length = this->getLength(); // call this to force computing it
590 int count = fSegments.count();
591
592 if (count == 0 || length == 0) {
593 return false;
594 }
595
596 // pin the distance to a legal range
597 if (distance < 0) {
598 distance = 0;
599 } else if (distance > length) {
600 distance = length;
601 }
602
603 SkScalar t;
604 const Segment* seg = this->distanceToSegment(distance, &t);
605
606 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, t, pos, tangent);
607 return true;
608 }
609
getMatrix(SkScalar distance,SkMatrix * matrix,MatrixFlags flags)610 bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
611 MatrixFlags flags) {
612 if (nullptr == fPath) {
613 return false;
614 }
615
616 SkPoint position;
617 SkVector tangent;
618
619 if (this->getPosTan(distance, &position, &tangent)) {
620 if (matrix) {
621 if (flags & kGetTangent_MatrixFlag) {
622 matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
623 } else {
624 matrix->reset();
625 }
626 if (flags & kGetPosition_MatrixFlag) {
627 matrix->postTranslate(position.fX, position.fY);
628 }
629 }
630 return true;
631 }
632 return false;
633 }
634
getSegment(SkScalar startD,SkScalar stopD,SkPath * dst,bool startWithMoveTo)635 bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
636 bool startWithMoveTo) {
637 SkASSERT(dst);
638
639 SkScalar length = this->getLength(); // ensure we have built our segments
640
641 if (startD < 0) {
642 startD = 0;
643 }
644 if (stopD > length) {
645 stopD = length;
646 }
647 if (startD > stopD) {
648 return false;
649 }
650 if (!fSegments.count()) {
651 return false;
652 }
653
654 SkPoint p;
655 SkScalar startT, stopT;
656 const Segment* seg = this->distanceToSegment(startD, &startT);
657 const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
658 SkASSERT(seg <= stopSeg);
659
660 if (startWithMoveTo) {
661 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, startT, &p, nullptr);
662 dst->moveTo(p);
663 }
664
665 if (seg->fPtIndex == stopSeg->fPtIndex) {
666 SkPathMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, stopT, dst);
667 } else {
668 do {
669 SkPathMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, SK_Scalar1, dst);
670 seg = SkPathMeasure::NextSegment(seg);
671 startT = 0;
672 } while (seg->fPtIndex < stopSeg->fPtIndex);
673 SkPathMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, 0, stopT, dst);
674 }
675 return true;
676 }
677
isClosed()678 bool SkPathMeasure::isClosed() {
679 (void)this->getLength(); // make sure we measure the current contour
680 return fIsClosed;
681 }
682
683 /** Move to the next contour in the path. Return true if one exists, or false if
684 we're done with the path.
685 */
nextContour()686 bool SkPathMeasure::nextContour() {
687 (void)this->getLength(); // make sure we measure the current contour
688 fLength = -1; // now signal that we should build the next set of segments
689 return this->getLength() > 0;
690 }
691
692 ///////////////////////////////////////////////////////////////////////////////
693 ///////////////////////////////////////////////////////////////////////////////
694
695 #ifdef SK_DEBUG
696
dump()697 void SkPathMeasure::dump() {
698 SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count());
699
700 for (int i = 0; i < fSegments.count(); i++) {
701 const Segment* seg = &fSegments[i];
702 SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n",
703 i, seg->fDistance, seg->fPtIndex, seg->getScalarT(),
704 seg->fType);
705 }
706 }
707
708 #endif
709