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
tValue2Scalar(int t)17 constexpr 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
24 static_assert(0.0f == tValue2Scalar( 0), "Lower limit should be exact.");
25 static_assert(1.0f == tValue2Scalar(kMaxTValue), "Upper limit should be exact.");
26
getScalarT() const27 SkScalar SkContourMeasure::Segment::getScalarT() const {
28 return tValue2Scalar(fTValue);
29 }
30
SkContourMeasure_segTo(const SkPoint pts[],unsigned segType,SkScalar startT,SkScalar stopT,SkPath * dst)31 void 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 tmp[2];
92 if (conic.chopAt(startT, tmp)) {
93 dst->conicTo(tmp[1].fPts[1], tmp[1].fPts[2], tmp[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
tspan_big_enough(int tspan)127 static 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
quad_too_curvy(const SkPoint pts[3],SkScalar tolerance)137 static 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
conic_too_curvy(const SkPoint & firstPt,const SkPoint & midTPt,const SkPoint & lastPt,SkScalar tolerance)149 static 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
cheap_dist_exceeds_limit(const SkPoint & pt,SkScalar x,SkScalar y,SkScalar tolerance)158 static 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
cubic_too_curvy(const SkPoint pts[4],SkScalar tolerance)165 static 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
175 class SkContourMeasureIter::Impl {
176 public:
Impl(const SkPath & path,bool forceClosed,SkScalar resScale)177 Impl(const SkPath& path, bool forceClosed, SkScalar resScale)
178 : fPath(path)
179 , fIter(SkPathPriv::Iterate(fPath).begin())
180 , fTolerance(CHEAP_DIST_LIMIT * SkScalarInvert(resScale))
181 , fForceClosed(forceClosed) {}
182
hasNextSegments() const183 bool hasNextSegments() const { return fIter != SkPathPriv::Iterate(fPath).end(); }
184 SkContourMeasure* buildSegments();
185
186 private:
187 SkPath fPath;
188 SkPathPriv::RangeIter fIter;
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 SkDEBUGCODE(void validate() const;)
197 SkScalar compute_line_seg(SkPoint p0, SkPoint p1, SkScalar distance, unsigned ptIndex);
198 SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance,
199 int mint, int maxt, unsigned ptIndex);
200 SkScalar compute_conic_segs(const SkConic& conic, SkScalar distance,
201 int mint, const SkPoint& minPt,
202 int maxt, const SkPoint& maxPt,
203 unsigned ptIndex);
204 SkScalar compute_cubic_segs(const SkPoint pts[4], SkScalar distance,
205 int mint, int maxt, unsigned ptIndex);
206 };
207
compute_quad_segs(const SkPoint pts[3],SkScalar distance,int mint,int maxt,unsigned ptIndex)208 SkScalar SkContourMeasureIter::Impl::compute_quad_segs(const SkPoint pts[3], SkScalar distance,
209 int mint, int maxt, unsigned ptIndex) {
210 if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts, fTolerance)) {
211 SkPoint tmp[5];
212 int halft = (mint + maxt) >> 1;
213
214 SkChopQuadAtHalf(pts, tmp);
215 distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
216 distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
217 } else {
218 SkScalar d = SkPoint::Distance(pts[0], pts[2]);
219 SkScalar prevD = distance;
220 distance += d;
221 if (distance > prevD) {
222 SkASSERT(ptIndex < (unsigned)fPts.count());
223 SkContourMeasure::Segment* seg = fSegments.append();
224 seg->fDistance = distance;
225 seg->fPtIndex = ptIndex;
226 seg->fType = kQuad_SegType;
227 seg->fTValue = maxt;
228 }
229 }
230 return distance;
231 }
232
compute_conic_segs(const SkConic & conic,SkScalar distance,int mint,const SkPoint & minPt,int maxt,const SkPoint & maxPt,unsigned ptIndex)233 SkScalar SkContourMeasureIter::Impl::compute_conic_segs(const SkConic& conic, SkScalar distance,
234 int mint, const SkPoint& minPt,
235 int maxt, const SkPoint& maxPt,
236 unsigned ptIndex) {
237 int halft = (mint + maxt) >> 1;
238 SkPoint halfPt = conic.evalAt(tValue2Scalar(halft));
239 if (!halfPt.isFinite()) {
240 return distance;
241 }
242 if (tspan_big_enough(maxt - mint) && conic_too_curvy(minPt, halfPt, maxPt, fTolerance)) {
243 distance = this->compute_conic_segs(conic, distance, mint, minPt, halft, halfPt, ptIndex);
244 distance = this->compute_conic_segs(conic, distance, halft, halfPt, maxt, maxPt, ptIndex);
245 } else {
246 SkScalar d = SkPoint::Distance(minPt, maxPt);
247 SkScalar prevD = distance;
248 distance += d;
249 if (distance > prevD) {
250 SkASSERT(ptIndex < (unsigned)fPts.count());
251 SkContourMeasure::Segment* seg = fSegments.append();
252 seg->fDistance = distance;
253 seg->fPtIndex = ptIndex;
254 seg->fType = kConic_SegType;
255 seg->fTValue = maxt;
256 }
257 }
258 return distance;
259 }
260
compute_cubic_segs(const SkPoint pts[4],SkScalar distance,int mint,int maxt,unsigned ptIndex)261 SkScalar SkContourMeasureIter::Impl::compute_cubic_segs(const SkPoint pts[4], SkScalar distance,
262 int mint, int maxt, unsigned ptIndex) {
263 if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts, fTolerance)) {
264 SkPoint tmp[7];
265 int halft = (mint + maxt) >> 1;
266
267 SkChopCubicAtHalf(pts, tmp);
268 distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
269 distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
270 } else {
271 SkScalar d = SkPoint::Distance(pts[0], pts[3]);
272 SkScalar prevD = distance;
273 distance += d;
274 if (distance > prevD) {
275 SkASSERT(ptIndex < (unsigned)fPts.count());
276 SkContourMeasure::Segment* seg = fSegments.append();
277 seg->fDistance = distance;
278 seg->fPtIndex = ptIndex;
279 seg->fType = kCubic_SegType;
280 seg->fTValue = maxt;
281 }
282 }
283 return distance;
284 }
285
compute_line_seg(SkPoint p0,SkPoint p1,SkScalar distance,unsigned ptIndex)286 SkScalar SkContourMeasureIter::Impl::compute_line_seg(SkPoint p0, SkPoint p1, SkScalar distance,
287 unsigned ptIndex) {
288 SkScalar d = SkPoint::Distance(p0, p1);
289 SkASSERT(d >= 0);
290 SkScalar prevD = distance;
291 distance += d;
292 if (distance > prevD) {
293 SkASSERT((unsigned)ptIndex < (unsigned)fPts.count());
294 SkContourMeasure::Segment* seg = fSegments.append();
295 seg->fDistance = distance;
296 seg->fPtIndex = ptIndex;
297 seg->fType = kLine_SegType;
298 seg->fTValue = kMaxTValue;
299 }
300 return distance;
301 }
302
303 #ifdef SK_DEBUG
validate() const304 void SkContourMeasureIter::Impl::validate() const {
305 const SkContourMeasure::Segment* seg = fSegments.begin();
306 const SkContourMeasure::Segment* stop = fSegments.end();
307 unsigned ptIndex = 0;
308 SkScalar distance = 0;
309 // limit the loop to a reasonable number; pathological cases can run for minutes
310 int maxChecks = 10000000; // set to INT_MAX to defeat the check
311 while (seg < stop) {
312 SkASSERT(seg->fDistance > distance);
313 SkASSERT(seg->fPtIndex >= ptIndex);
314 SkASSERT(seg->fTValue > 0);
315
316 const SkContourMeasure::Segment* s = seg;
317 while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex && --maxChecks > 0) {
318 SkASSERT(s[0].fType == s[1].fType);
319 SkASSERT(s[0].fTValue < s[1].fTValue);
320 s += 1;
321 }
322
323 distance = seg->fDistance;
324 ptIndex = seg->fPtIndex;
325 seg += 1;
326 }
327 }
328 #endif
329
buildSegments()330 SkContourMeasure* SkContourMeasureIter::Impl::buildSegments() {
331 int ptIndex = -1;
332 SkScalar distance = 0;
333 bool haveSeenClose = fForceClosed;
334 bool haveSeenMoveTo = false;
335
336 /* Note:
337 * as we accumulate distance, we have to check that the result of +=
338 * actually made it larger, since a very small delta might be > 0, but
339 * still have no effect on distance (if distance >>> delta).
340 *
341 * We do this check below, and in compute_quad_segs and compute_cubic_segs
342 */
343
344 fSegments.reset();
345 fPts.reset();
346
347 auto end = SkPathPriv::Iterate(fPath).end();
348 for (; fIter != end; ++fIter) {
349 auto [verb, pts, w] = *fIter;
350 if (haveSeenMoveTo && verb == SkPathVerb::kMove) {
351 break;
352 }
353 switch (verb) {
354 case SkPathVerb::kMove:
355 ptIndex += 1;
356 fPts.append(1, pts);
357 SkASSERT(!haveSeenMoveTo);
358 haveSeenMoveTo = true;
359 break;
360
361 case SkPathVerb::kLine: {
362 SkASSERT(haveSeenMoveTo);
363 SkScalar prevD = distance;
364 distance = this->compute_line_seg(pts[0], pts[1], distance, ptIndex);
365 if (distance > prevD) {
366 fPts.append(1, pts + 1);
367 ptIndex++;
368 }
369 } break;
370
371 case SkPathVerb::kQuad: {
372 SkASSERT(haveSeenMoveTo);
373 SkScalar prevD = distance;
374 distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex);
375 if (distance > prevD) {
376 fPts.append(2, pts + 1);
377 ptIndex += 2;
378 }
379 } break;
380
381 case SkPathVerb::kConic: {
382 SkASSERT(haveSeenMoveTo);
383 const SkConic conic(pts, *w);
384 SkScalar prevD = distance;
385 distance = this->compute_conic_segs(conic, distance, 0, conic.fPts[0],
386 kMaxTValue, conic.fPts[2], ptIndex);
387 if (distance > prevD) {
388 // we store the conic weight in our next point, followed by the last 2 pts
389 // thus to reconstitue a conic, you'd need to say
390 // SkConic(pts[0], pts[2], pts[3], weight = pts[1].fX)
391 fPts.append()->set(conic.fW, 0);
392 fPts.append(2, pts + 1);
393 ptIndex += 3;
394 }
395 } break;
396
397 case SkPathVerb::kCubic: {
398 SkASSERT(haveSeenMoveTo);
399 SkScalar prevD = distance;
400 distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex);
401 if (distance > prevD) {
402 fPts.append(3, pts + 1);
403 ptIndex += 3;
404 }
405 } break;
406
407 case SkPathVerb::kClose:
408 haveSeenClose = true;
409 break;
410 }
411
412 }
413
414 if (!SkScalarIsFinite(distance)) {
415 return nullptr;
416 }
417 if (fSegments.count() == 0) {
418 return nullptr;
419 }
420
421 if (haveSeenClose) {
422 SkScalar prevD = distance;
423 SkPoint firstPt = fPts[0];
424 distance = this->compute_line_seg(fPts[ptIndex], firstPt, distance, ptIndex);
425 if (distance > prevD) {
426 *fPts.append() = firstPt;
427 }
428 }
429
430 SkDEBUGCODE(this->validate();)
431
432 return new SkContourMeasure(std::move(fSegments), std::move(fPts), distance, haveSeenClose);
433 }
434
compute_pos_tan(const SkPoint pts[],unsigned segType,SkScalar t,SkPoint * pos,SkVector * tangent)435 static void compute_pos_tan(const SkPoint pts[], unsigned segType,
436 SkScalar t, SkPoint* pos, SkVector* tangent) {
437 switch (segType) {
438 case kLine_SegType:
439 if (pos) {
440 pos->set(SkScalarInterp(pts[0].fX, pts[1].fX, t),
441 SkScalarInterp(pts[0].fY, pts[1].fY, t));
442 }
443 if (tangent) {
444 tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY);
445 }
446 break;
447 case kQuad_SegType:
448 SkEvalQuadAt(pts, t, pos, tangent);
449 if (tangent) {
450 tangent->normalize();
451 }
452 break;
453 case kConic_SegType: {
454 SkConic(pts[0], pts[2], pts[3], pts[1].fX).evalAt(t, pos, tangent);
455 if (tangent) {
456 tangent->normalize();
457 }
458 } break;
459 case kCubic_SegType:
460 SkEvalCubicAt(pts, t, pos, tangent, nullptr);
461 if (tangent) {
462 tangent->normalize();
463 }
464 break;
465 default:
466 SkDEBUGFAIL("unknown segType");
467 }
468 }
469
470
471 ////////////////////////////////////////////////////////////////////////////////
472 ////////////////////////////////////////////////////////////////////////////////
473
SkContourMeasureIter()474 SkContourMeasureIter::SkContourMeasureIter() {
475 }
476
SkContourMeasureIter(const SkPath & path,bool forceClosed,SkScalar resScale)477 SkContourMeasureIter::SkContourMeasureIter(const SkPath& path, bool forceClosed,
478 SkScalar resScale) {
479 this->reset(path, forceClosed, resScale);
480 }
481
~SkContourMeasureIter()482 SkContourMeasureIter::~SkContourMeasureIter() {}
483
484 /** Assign a new path, or null to have none.
485 */
reset(const SkPath & path,bool forceClosed,SkScalar resScale)486 void SkContourMeasureIter::reset(const SkPath& path, bool forceClosed, SkScalar resScale) {
487 if (path.isFinite()) {
488 fImpl = std::make_unique<Impl>(path, forceClosed, resScale);
489 } else {
490 fImpl.reset();
491 }
492 }
493
next()494 sk_sp<SkContourMeasure> SkContourMeasureIter::next() {
495 if (!fImpl) {
496 return nullptr;
497 }
498 while (fImpl->hasNextSegments()) {
499 auto cm = fImpl->buildSegments();
500 if (cm) {
501 return sk_sp<SkContourMeasure>(cm);
502 }
503 }
504 return nullptr;
505 }
506
507 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
508
SkContourMeasure(SkTDArray<Segment> && segs,SkTDArray<SkPoint> && pts,SkScalar length,bool isClosed)509 SkContourMeasure::SkContourMeasure(SkTDArray<Segment>&& segs, SkTDArray<SkPoint>&& pts, SkScalar length, bool isClosed)
510 : fSegments(std::move(segs))
511 , fPts(std::move(pts))
512 , fLength(length)
513 , fIsClosed(isClosed)
514 {}
515
516 template <typename T, typename K>
SkTKSearch(const T base[],int count,const K & key)517 int SkTKSearch(const T base[], int count, const K& key) {
518 SkASSERT(count >= 0);
519 if (count <= 0) {
520 return ~0;
521 }
522
523 SkASSERT(base != nullptr); // base may be nullptr if count is zero
524
525 unsigned lo = 0;
526 unsigned hi = count - 1;
527
528 while (lo < hi) {
529 unsigned mid = (hi + lo) >> 1;
530 if (base[mid].fDistance < key) {
531 lo = mid + 1;
532 } else {
533 hi = mid;
534 }
535 }
536
537 if (base[hi].fDistance < key) {
538 hi += 1;
539 hi = ~hi;
540 } else if (key < base[hi].fDistance) {
541 hi = ~hi;
542 }
543 return hi;
544 }
545
distanceToSegment(SkScalar distance,SkScalar * t) const546 const SkContourMeasure::Segment* SkContourMeasure::distanceToSegment( SkScalar distance,
547 SkScalar* t) const {
548 SkDEBUGCODE(SkScalar length = ) this->length();
549 SkASSERT(distance >= 0 && distance <= length);
550
551 const Segment* seg = fSegments.begin();
552 int count = fSegments.count();
553
554 int index = SkTKSearch<Segment, SkScalar>(seg, count, distance);
555 // don't care if we hit an exact match or not, so we xor index if it is negative
556 index ^= (index >> 31);
557 seg = &seg[index];
558
559 // now interpolate t-values with the prev segment (if possible)
560 SkScalar startT = 0, startD = 0;
561 // check if the prev segment is legal, and references the same set of points
562 if (index > 0) {
563 startD = seg[-1].fDistance;
564 if (seg[-1].fPtIndex == seg->fPtIndex) {
565 SkASSERT(seg[-1].fType == seg->fType);
566 startT = seg[-1].getScalarT();
567 }
568 }
569
570 SkASSERT(seg->getScalarT() > startT);
571 SkASSERT(distance >= startD);
572 SkASSERT(seg->fDistance > startD);
573
574 *t = startT + (seg->getScalarT() - startT) * (distance - startD) / (seg->fDistance - startD);
575 return seg;
576 }
577
getPosTan(SkScalar distance,SkPoint * pos,SkVector * tangent) const578 bool SkContourMeasure::getPosTan(SkScalar distance, SkPoint* pos, SkVector* tangent) const {
579 if (SkScalarIsNaN(distance)) {
580 return false;
581 }
582
583 const SkScalar length = this->length();
584 SkASSERT(length > 0 && fSegments.count() > 0);
585
586 // pin the distance to a legal range
587 if (distance < 0) {
588 distance = 0;
589 } else if (distance > length) {
590 distance = length;
591 }
592
593 SkScalar t;
594 const Segment* seg = this->distanceToSegment(distance, &t);
595 if (SkScalarIsNaN(t)) {
596 return false;
597 }
598
599 SkASSERT((unsigned)seg->fPtIndex < (unsigned)fPts.count());
600 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, t, pos, tangent);
601 return true;
602 }
603
getMatrix(SkScalar distance,SkMatrix * matrix,MatrixFlags flags) const604 bool SkContourMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags) const {
605 SkPoint position;
606 SkVector tangent;
607
608 if (this->getPosTan(distance, &position, &tangent)) {
609 if (matrix) {
610 if (flags & kGetTangent_MatrixFlag) {
611 matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
612 } else {
613 matrix->reset();
614 }
615 if (flags & kGetPosition_MatrixFlag) {
616 matrix->postTranslate(position.fX, position.fY);
617 }
618 }
619 return true;
620 }
621 return false;
622 }
623
getSegment(SkScalar startD,SkScalar stopD,SkPath * dst,bool startWithMoveTo) const624 bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
625 bool startWithMoveTo) const {
626 SkASSERT(dst);
627
628 SkScalar length = this->length(); // ensure we have built our segments
629
630 if (startD < 0) {
631 startD = 0;
632 }
633 if (stopD > length) {
634 stopD = length;
635 }
636 if (!(startD <= stopD)) { // catch NaN values as well
637 return false;
638 }
639 if (!fSegments.count()) {
640 return false;
641 }
642
643 SkPoint p;
644 SkScalar startT, stopT;
645 const Segment* seg = this->distanceToSegment(startD, &startT);
646 if (!SkScalarIsFinite(startT)) {
647 return false;
648 }
649 const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
650 if (!SkScalarIsFinite(stopT)) {
651 return false;
652 }
653 SkASSERT(seg <= stopSeg);
654 if (startWithMoveTo) {
655 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, startT, &p, nullptr);
656 dst->moveTo(p);
657 }
658
659 if (seg->fPtIndex == stopSeg->fPtIndex) {
660 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, stopT, dst);
661 } else {
662 do {
663 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, SK_Scalar1, dst);
664 seg = SkContourMeasure::Segment::Next(seg);
665 startT = 0;
666 } while (seg->fPtIndex < stopSeg->fPtIndex);
667 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, 0, stopT, dst);
668 }
669
670 return true;
671 }
672