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