OLD | NEW |
| (Empty) |
1 /* | |
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 | |
23 enum { | |
24 kLine_SegType, | |
25 kCloseLine_SegType, | |
26 kQuad_SegType, | |
27 kCubic_SegType | |
28 }; | |
29 | |
30 #define kMaxTValue 32767 | |
31 | |
32 static 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 | |
42 SkScalar SkPathMeasure::Segment::getScalarT() const { | |
43 return tValue2Scalar(fTValue); | |
44 } | |
45 | |
46 const 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 | |
57 static inline int tspan_big_enough(int tspan) { | |
58 SkASSERT((unsigned)tspan <= kMaxTValue); | |
59 return tspan >> 10; | |
60 } | |
61 | |
62 #if 0 | |
63 static 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 | |
78 static 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 | |
90 static 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 | |
97 static 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 | |
107 SkScalar 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, ptInd
ex); | |
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 | |
131 SkScalar 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, ptIn
dex); | |
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 | |
155 void 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 } | |
209 DONE: | |
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 | |
243 const SkPoint* sk_get_path_points(const SkPath& path, int index) { | |
244 return &path.fPts[index]; | |
245 } | |
246 | |
247 static 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 | |
284 static 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 | |
359 SkPathMeasure::SkPathMeasure() { | |
360 fPath = NULL; | |
361 fLength = -1; // signal we need to compute it | |
362 fForceClosed = false; | |
363 fFirstPtIndex = -1; | |
364 } | |
365 | |
366 SkPathMeasure::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 | |
375 SkPathMeasure::~SkPathMeasure() {} | |
376 | |
377 /** Assign a new path, or null to have none. | |
378 */ | |
379 void 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 | |
391 SkScalar 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 | |
402 const 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 nega
tive | |
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 | |
437 bool 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 | |
467 bool 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 | |
488 bool 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 | |
532 bool 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 */ | |
540 bool SkPathMeasure::nextContour() { | |
541 fLength = -1; | |
542 return this->getLength() > 0; | |
543 } | |
544 | |
545 /////////////////////////////////////////////////////////////////////////////// | |
546 /////////////////////////////////////////////////////////////////////////////// | |
547 | |
548 #ifdef SK_DEBUG | |
549 | |
550 void 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 | |
561 void 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 | |
OLD | NEW |