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