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