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