• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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