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