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