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