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