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 since they are in our 2-bit field
16 : enum {
17 : kLine_SegType,
18 : kCloseLine_SegType,
19 : kQuad_SegType,
20 : kCubic_SegType
21 : };
22 :
23 : #define kMaxTValue 32767
24 :
25 0 : static inline SkScalar tValue2Scalar(int t) {
26 0 : SkASSERT((unsigned)t <= kMaxTValue);
27 :
28 : #ifdef SK_SCALAR_IS_FLOAT
29 0 : return t * 3.05185e-5f; // t / 32767
30 : #else
31 : return (t + (t >> 14)) << 1;
32 : #endif
33 : }
34 :
35 0 : SkScalar SkPathMeasure::Segment::getScalarT() const {
36 0 : return tValue2Scalar(fTValue);
37 : }
38 :
39 0 : const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) {
40 0 : unsigned ptIndex = seg->fPtIndex;
41 :
42 0 : do {
43 0 : ++seg;
44 : } while (seg->fPtIndex == ptIndex);
45 0 : return seg;
46 : }
47 :
48 : ///////////////////////////////////////////////////////////////////////////////
49 :
50 0 : static inline int tspan_big_enough(int tspan) {
51 0 : SkASSERT((unsigned)tspan <= kMaxTValue);
52 0 : return tspan >> 10;
53 : }
54 :
55 : #if 0
56 : static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) {
57 : static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100;
58 :
59 : SkASSERT(kFlatEnoughTangentDotProd > 0 &&
60 : kFlatEnoughTangentDotProd < SK_Scalar1);
61 :
62 : return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd;
63 : }
64 : #endif
65 :
66 : // can't use tangents, since we need [0..1..................2] to be seen
67 : // as definitely not a line (it is when drawn, but not parametrically)
68 : // so we compare midpoints
69 : #define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up
70 :
71 0 : static bool quad_too_curvy(const SkPoint pts[3]) {
72 : // diff = (a/4 + b/2 + c/4) - (a/2 + c/2)
73 : // diff = -a/4 + b/2 - c/4
74 0 : SkScalar dx = SkScalarHalf(pts[1].fX) -
75 0 : SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX));
76 0 : SkScalar dy = SkScalarHalf(pts[1].fY) -
77 0 : SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY));
78 :
79 0 : SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy));
80 0 : return dist > CHEAP_DIST_LIMIT;
81 : }
82 :
83 0 : static bool cheap_dist_exceeds_limit(const SkPoint& pt,
84 : SkScalar x, SkScalar y) {
85 0 : SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
86 : // just made up the 1/2
87 0 : return dist > CHEAP_DIST_LIMIT;
88 : }
89 :
90 0 : static bool cubic_too_curvy(const SkPoint pts[4]) {
91 0 : return cheap_dist_exceeds_limit(pts[1],
92 0 : SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
93 0 : SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
94 : ||
95 0 : cheap_dist_exceeds_limit(pts[2],
96 0 : SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
97 0 : SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
98 : }
99 :
100 0 : SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
101 : SkScalar distance, int mint, int maxt, int ptIndex) {
102 0 : if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
103 : SkPoint tmp[5];
104 0 : int halft = (mint + maxt) >> 1;
105 :
106 0 : SkChopQuadAtHalf(pts, tmp);
107 0 : distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
108 0 : distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
109 : } else {
110 0 : SkScalar d = SkPoint::Distance(pts[0], pts[2]);
111 0 : SkASSERT(d >= 0);
112 0 : if (!SkScalarNearlyZero(d)) {
113 0 : distance += d;
114 0 : Segment* seg = fSegments.append();
115 0 : seg->fDistance = distance;
116 0 : seg->fPtIndex = ptIndex;
117 0 : seg->fType = kQuad_SegType;
118 0 : seg->fTValue = maxt;
119 : }
120 : }
121 0 : return distance;
122 : }
123 :
124 0 : SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
125 : SkScalar distance, int mint, int maxt, int ptIndex) {
126 0 : if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) {
127 : SkPoint tmp[7];
128 0 : int halft = (mint + maxt) >> 1;
129 :
130 0 : SkChopCubicAtHalf(pts, tmp);
131 0 : distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
132 0 : distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
133 : } else {
134 0 : SkScalar d = SkPoint::Distance(pts[0], pts[3]);
135 0 : SkASSERT(d >= 0);
136 0 : if (!SkScalarNearlyZero(d)) {
137 0 : distance += d;
138 0 : Segment* seg = fSegments.append();
139 0 : seg->fDistance = distance;
140 0 : seg->fPtIndex = ptIndex;
141 0 : seg->fType = kCubic_SegType;
142 0 : seg->fTValue = maxt;
143 : }
144 : }
145 0 : return distance;
146 : }
147 :
148 0 : void SkPathMeasure::buildSegments() {
149 : SkPoint pts[4];
150 0 : int ptIndex = fFirstPtIndex;
151 0 : SkScalar d, distance = 0;
152 0 : bool isClosed = fForceClosed;
153 0 : bool firstMoveTo = ptIndex < 0;
154 : Segment* seg;
155 :
156 0 : fSegments.reset();
157 0 : for (;;) {
158 0 : switch (fIter.next(pts)) {
159 : case SkPath::kMove_Verb:
160 0 : if (!firstMoveTo) {
161 0 : goto DONE;
162 : }
163 0 : ptIndex += 1;
164 0 : firstMoveTo = false;
165 0 : break;
166 :
167 : case SkPath::kLine_Verb:
168 0 : d = SkPoint::Distance(pts[0], pts[1]);
169 0 : SkASSERT(d >= 0);
170 0 : if (!SkScalarNearlyZero(d)) {
171 0 : distance += d;
172 0 : seg = fSegments.append();
173 0 : seg->fDistance = distance;
174 0 : seg->fPtIndex = ptIndex;
175 0 : seg->fType = fIter.isCloseLine() ?
176 0 : kCloseLine_SegType : kLine_SegType;
177 0 : seg->fTValue = kMaxTValue;
178 : }
179 0 : ptIndex += !fIter.isCloseLine();
180 0 : break;
181 :
182 : case SkPath::kQuad_Verb:
183 : distance = this->compute_quad_segs(pts, distance, 0,
184 0 : kMaxTValue, ptIndex);
185 0 : ptIndex += 2;
186 0 : break;
187 :
188 : case SkPath::kCubic_Verb:
189 : distance = this->compute_cubic_segs(pts, distance, 0,
190 0 : kMaxTValue, ptIndex);
191 0 : ptIndex += 3;
192 0 : break;
193 :
194 : case SkPath::kClose_Verb:
195 0 : isClosed = true;
196 0 : break;
197 :
198 : case SkPath::kDone_Verb:
199 0 : goto DONE;
200 : }
201 : }
202 : DONE:
203 0 : fLength = distance;
204 0 : fIsClosed = isClosed;
205 0 : fFirstPtIndex = ptIndex + 1;
206 :
207 : #ifdef SK_DEBUG
208 : {
209 0 : const Segment* seg = fSegments.begin();
210 0 : const Segment* stop = fSegments.end();
211 0 : unsigned ptIndex = 0;
212 0 : SkScalar distance = 0;
213 :
214 0 : while (seg < stop) {
215 0 : SkASSERT(seg->fDistance > distance);
216 0 : SkASSERT(seg->fPtIndex >= ptIndex);
217 0 : SkASSERT(seg->fTValue > 0);
218 :
219 0 : const Segment* s = seg;
220 0 : while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) {
221 0 : SkASSERT(s[0].fType == s[1].fType);
222 0 : SkASSERT(s[0].fTValue < s[1].fTValue);
223 0 : s += 1;
224 : }
225 :
226 0 : distance = seg->fDistance;
227 0 : ptIndex = seg->fPtIndex;
228 0 : seg += 1;
229 : }
230 : // SkDebugf("\n");
231 : }
232 : #endif
233 0 : }
234 :
235 : // marked as a friend in SkPath.h
236 0 : const SkPoint* sk_get_path_points(const SkPath& path, int index) {
237 0 : return &path.fPts[index];
238 : }
239 :
240 0 : static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex,
241 : int segType, SkScalar t, SkPoint* pos, SkVector* tangent) {
242 0 : const SkPoint* pts = sk_get_path_points(path, ptIndex);
243 :
244 0 : switch (segType) {
245 : case kLine_SegType:
246 : case kCloseLine_SegType: {
247 : const SkPoint* endp = (segType == kLine_SegType) ?
248 : &pts[1] :
249 0 : sk_get_path_points(path, firstPtIndex);
250 :
251 0 : if (pos) {
252 : pos->set(SkScalarInterp(pts[0].fX, endp->fX, t),
253 0 : SkScalarInterp(pts[0].fY, endp->fY, t));
254 : }
255 0 : if (tangent) {
256 0 : tangent->setNormalize(endp->fX - pts[0].fX, endp->fY - pts[0].fY);
257 : }
258 0 : break;
259 : }
260 : case kQuad_SegType:
261 0 : SkEvalQuadAt(pts, t, pos, tangent);
262 0 : if (tangent) {
263 0 : tangent->normalize();
264 : }
265 0 : break;
266 : case kCubic_SegType:
267 0 : SkEvalCubicAt(pts, t, pos, tangent, NULL);
268 0 : if (tangent) {
269 0 : tangent->normalize();
270 : }
271 0 : break;
272 : default:
273 0 : SkDEBUGFAIL("unknown segType");
274 : }
275 0 : }
276 :
277 0 : static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex,
278 : int segType, SkScalar startT, SkScalar stopT, SkPath* dst) {
279 0 : SkASSERT(startT >= 0 && startT <= SK_Scalar1);
280 0 : SkASSERT(stopT >= 0 && stopT <= SK_Scalar1);
281 0 : SkASSERT(startT <= stopT);
282 :
283 0 : if (SkScalarNearlyZero(stopT - startT)) {
284 0 : return;
285 : }
286 :
287 0 : const SkPoint* pts = sk_get_path_points(src, ptIndex);
288 : SkPoint tmp0[7], tmp1[7];
289 :
290 0 : switch (segType) {
291 : case kLine_SegType:
292 : case kCloseLine_SegType: {
293 : const SkPoint* endp = (segType == kLine_SegType) ?
294 : &pts[1] :
295 0 : sk_get_path_points(src, firstPtIndex);
296 :
297 0 : if (stopT == kMaxTValue) {
298 0 : dst->lineTo(*endp);
299 : } else {
300 : dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT),
301 0 : SkScalarInterp(pts[0].fY, endp->fY, stopT));
302 : }
303 0 : break;
304 : }
305 : case kQuad_SegType:
306 0 : if (startT == 0) {
307 0 : if (stopT == SK_Scalar1) {
308 0 : dst->quadTo(pts[1], pts[2]);
309 : } else {
310 0 : SkChopQuadAt(pts, tmp0, stopT);
311 0 : dst->quadTo(tmp0[1], tmp0[2]);
312 : }
313 : } else {
314 0 : SkChopQuadAt(pts, tmp0, startT);
315 0 : if (stopT == SK_Scalar1) {
316 0 : dst->quadTo(tmp0[3], tmp0[4]);
317 : } else {
318 : SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT,
319 0 : SK_Scalar1 - startT));
320 0 : dst->quadTo(tmp1[1], tmp1[2]);
321 : }
322 : }
323 0 : break;
324 : case kCubic_SegType:
325 0 : if (startT == 0) {
326 0 : if (stopT == SK_Scalar1) {
327 0 : dst->cubicTo(pts[1], pts[2], pts[3]);
328 : } else {
329 0 : SkChopCubicAt(pts, tmp0, stopT);
330 0 : dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]);
331 : }
332 : } else {
333 0 : SkChopCubicAt(pts, tmp0, startT);
334 0 : if (stopT == SK_Scalar1) {
335 0 : dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
336 : } else {
337 : SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT,
338 0 : SK_Scalar1 - startT));
339 0 : dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
340 : }
341 : }
342 0 : break;
343 : default:
344 0 : SkDEBUGFAIL("unknown segType");
345 0 : sk_throw();
346 : }
347 : }
348 :
349 : ////////////////////////////////////////////////////////////////////////////////
350 : ////////////////////////////////////////////////////////////////////////////////
351 :
352 0 : SkPathMeasure::SkPathMeasure() {
353 0 : fPath = NULL;
354 0 : fLength = -1; // signal we need to compute it
355 0 : fForceClosed = false;
356 0 : fFirstPtIndex = -1;
357 0 : }
358 :
359 0 : SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
360 0 : fPath = &path;
361 0 : fLength = -1; // signal we need to compute it
362 0 : fForceClosed = forceClosed;
363 0 : fFirstPtIndex = -1;
364 :
365 0 : fIter.setPath(path, forceClosed);
366 0 : }
367 :
368 0 : SkPathMeasure::~SkPathMeasure() {}
369 :
370 : /** Assign a new path, or null to have none.
371 : */
372 0 : void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) {
373 0 : fPath = path;
374 0 : fLength = -1; // signal we need to compute it
375 0 : fForceClosed = forceClosed;
376 0 : fFirstPtIndex = -1;
377 :
378 0 : if (path) {
379 0 : fIter.setPath(*path, forceClosed);
380 : }
381 0 : fSegments.reset();
382 0 : }
383 :
384 0 : SkScalar SkPathMeasure::getLength() {
385 0 : if (fPath == NULL) {
386 0 : return 0;
387 : }
388 0 : if (fLength < 0) {
389 0 : this->buildSegments();
390 : }
391 0 : SkASSERT(fLength >= 0);
392 0 : return fLength;
393 : }
394 :
395 0 : const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
396 : SkScalar distance, SkScalar* t) {
397 0 : SkDEBUGCODE(SkScalar length = ) this->getLength();
398 0 : SkASSERT(distance >= 0 && distance <= length);
399 :
400 0 : const Segment* seg = fSegments.begin();
401 0 : int count = fSegments.count();
402 :
403 : int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance,
404 0 : sizeof(Segment));
405 : // don't care if we hit an exact match or not, so we xor index if it is negative
406 0 : index ^= (index >> 31);
407 0 : seg = &seg[index];
408 :
409 : // now interpolate t-values with the prev segment (if possible)
410 0 : SkScalar startT = 0, startD = 0;
411 : // check if the prev segment is legal, and references the same set of points
412 0 : if (index > 0) {
413 0 : startD = seg[-1].fDistance;
414 0 : if (seg[-1].fPtIndex == seg->fPtIndex) {
415 0 : SkASSERT(seg[-1].fType == seg->fType);
416 0 : startT = seg[-1].getScalarT();
417 : }
418 : }
419 :
420 0 : SkASSERT(seg->getScalarT() > startT);
421 0 : SkASSERT(distance >= startD);
422 0 : SkASSERT(seg->fDistance > startD);
423 :
424 0 : *t = startT + SkScalarMulDiv(seg->getScalarT() - startT,
425 : distance - startD,
426 0 : seg->fDistance - startD);
427 0 : return seg;
428 : }
429 :
430 0 : bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos,
431 : SkVector* tangent) {
432 0 : SkASSERT(fPath);
433 0 : if (fPath == NULL) {
434 : EMPTY:
435 0 : return false;
436 : }
437 :
438 0 : SkScalar length = this->getLength(); // call this to force computing it
439 0 : int count = fSegments.count();
440 :
441 0 : if (count == 0 || length == 0) {
442 : goto EMPTY;
443 : }
444 :
445 : // pin the distance to a legal range
446 0 : if (distance < 0) {
447 0 : distance = 0;
448 0 : } else if (distance > length) {
449 0 : distance = length;
450 : }
451 :
452 : SkScalar t;
453 0 : const Segment* seg = this->distanceToSegment(distance, &t);
454 :
455 0 : compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
456 0 : t, pos, tangent);
457 0 : return true;
458 : }
459 :
460 0 : bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
461 : MatrixFlags flags) {
462 : SkPoint position;
463 : SkVector tangent;
464 :
465 0 : if (this->getPosTan(distance, &position, &tangent)) {
466 0 : if (matrix) {
467 0 : if (flags & kGetTangent_MatrixFlag) {
468 0 : matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
469 : } else {
470 0 : matrix->reset();
471 : }
472 0 : if (flags & kGetPosition_MatrixFlag) {
473 0 : matrix->postTranslate(position.fX, position.fY);
474 : }
475 : }
476 0 : return true;
477 : }
478 0 : return false;
479 : }
480 :
481 0 : bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
482 : bool startWithMoveTo) {
483 0 : SkASSERT(dst);
484 :
485 0 : SkScalar length = this->getLength(); // ensure we have built our segments
486 :
487 0 : if (startD < 0) {
488 0 : startD = 0;
489 : }
490 0 : if (stopD > length) {
491 0 : stopD = length;
492 : }
493 0 : if (startD >= stopD) {
494 0 : return false;
495 : }
496 :
497 : SkPoint p;
498 : SkScalar startT, stopT;
499 0 : const Segment* seg = this->distanceToSegment(startD, &startT);
500 0 : const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
501 0 : SkASSERT(seg <= stopSeg);
502 :
503 0 : if (startWithMoveTo) {
504 0 : compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex,
505 0 : seg->fType, startT, &p, NULL);
506 0 : dst->moveTo(p);
507 : }
508 :
509 0 : if (seg->fPtIndex == stopSeg->fPtIndex) {
510 0 : seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
511 0 : startT, stopT, dst);
512 : } else {
513 0 : do {
514 0 : seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
515 0 : startT, SK_Scalar1, dst);
516 0 : seg = SkPathMeasure::NextSegment(seg);
517 0 : startT = 0;
518 : } while (seg->fPtIndex < stopSeg->fPtIndex);
519 0 : seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
520 0 : 0, stopT, dst);
521 : }
522 0 : return true;
523 : }
524 :
525 0 : bool SkPathMeasure::isClosed() {
526 0 : (void)this->getLength();
527 0 : return fIsClosed;
528 : }
529 :
530 : /** Move to the next contour in the path. Return true if one exists, or false if
531 : we're done with the path.
532 : */
533 0 : bool SkPathMeasure::nextContour() {
534 0 : fLength = -1;
535 0 : return this->getLength() > 0;
536 : }
537 :
538 : ///////////////////////////////////////////////////////////////////////////////
539 : ///////////////////////////////////////////////////////////////////////////////
540 :
541 : #ifdef SK_DEBUG
542 :
543 0 : void SkPathMeasure::dump() {
544 0 : SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count());
545 :
546 0 : for (int i = 0; i < fSegments.count(); i++) {
547 0 : const Segment* seg = &fSegments[i];
548 : SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n",
549 0 : i, seg->fDistance, seg->fPtIndex, seg->getScalarT(),
550 0 : seg->fType);
551 : }
552 0 : }
553 :
554 : #endif
|