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