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