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