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 "SkStrokerPriv.h" | |
18 #include "SkGeometry.h" | |
19 #include "SkPath.h" | |
20 | |
21 #define kMaxQuadSubdivide 5 | |
22 #define kMaxCubicSubdivide 4 | |
23 | |
24 static inline bool degenerate_vector(const SkVector& v) { | |
25 return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY); | |
26 } | |
27 | |
28 static inline bool degenerate_line(const SkPoint& a, const SkPoint& b, | |
29 SkScalar tolerance = SK_ScalarNearlyZero) { | |
30 return SkScalarNearlyZero(a.fX - b.fX, tolerance) && | |
31 SkScalarNearlyZero(a.fY - b.fY, tolerance); | |
32 } | |
33 | |
34 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { | |
35 /* root2/2 is a 45-degree angle | |
36 make this constant bigger for more subdivisions (but not >= 1) | |
37 */ | |
38 static const SkScalar kFlatEnoughNormalDotProd = | |
39 SK_ScalarSqrt2/2 + SK_Scalar1/10; | |
40 | |
41 SkASSERT(kFlatEnoughNormalDotProd > 0 && | |
42 kFlatEnoughNormalDotProd < SK_Scalar1); | |
43 | |
44 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; | |
45 } | |
46 | |
47 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { | |
48 static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000; | |
49 | |
50 return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd; | |
51 } | |
52 | |
53 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, | |
54 SkScalar radius, | |
55 SkVector* normal, SkVector* unitNormal) { | |
56 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { | |
57 return false; | |
58 } | |
59 unitNormal->rotateCCW(); | |
60 unitNormal->scale(radius, normal); | |
61 return true; | |
62 } | |
63 | |
64 static bool set_normal_unitnormal(const SkVector& vec, | |
65 SkScalar radius, | |
66 SkVector* normal, SkVector* unitNormal) { | |
67 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { | |
68 return false; | |
69 } | |
70 unitNormal->rotateCCW(); | |
71 unitNormal->scale(radius, normal); | |
72 return true; | |
73 } | |
74 | |
75 /////////////////////////////////////////////////////////////////////////////// | |
76 | |
77 class SkPathStroker { | |
78 public: | |
79 SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, | |
80 SkPaint::Join join); | |
81 | |
82 void moveTo(const SkPoint&); | |
83 void lineTo(const SkPoint&); | |
84 void quadTo(const SkPoint&, const SkPoint&); | |
85 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); | |
86 void close(bool isLine) { this->finishContour(true, isLine); } | |
87 | |
88 void done(SkPath* dst, bool isLine) { | |
89 this->finishContour(false, isLine); | |
90 fOuter.addPath(fExtra); | |
91 dst->swap(fOuter); | |
92 } | |
93 | |
94 private: | |
95 SkScalar fRadius; | |
96 SkScalar fInvMiterLimit; | |
97 | |
98 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; | |
99 SkPoint fFirstPt, fPrevPt; // on original path | |
100 SkPoint fFirstOuterPt; | |
101 int fSegmentCount; | |
102 bool fPrevIsLine; | |
103 | |
104 SkStrokerPriv::CapProc fCapper; | |
105 SkStrokerPriv::JoinProc fJoiner; | |
106 | |
107 SkPath fInner, fOuter; // outer is our working answer, inner is temp | |
108 SkPath fExtra; // added as extra complete contours | |
109 | |
110 void finishContour(bool close, bool isLine); | |
111 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, | |
112 bool isLine); | |
113 void postJoinTo(const SkPoint&, const SkVector& normal, | |
114 const SkVector& unitNormal); | |
115 | |
116 void line_to(const SkPoint& currPt, const SkVector& normal); | |
117 void quad_to(const SkPoint pts[3], | |
118 const SkVector& normalAB, const SkVector& unitNormalAB, | |
119 SkVector* normalBC, SkVector* unitNormalBC, | |
120 int subDivide); | |
121 void cubic_to(const SkPoint pts[4], | |
122 const SkVector& normalAB, const SkVector& unitNormalAB, | |
123 SkVector* normalCD, SkVector* unitNormalCD, | |
124 int subDivide); | |
125 }; | |
126 | |
127 /////////////////////////////////////////////////////////////////////////////// | |
128 | |
129 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, | |
130 SkVector* unitNormal, bool currIsLine) { | |
131 SkASSERT(fSegmentCount >= 0); | |
132 | |
133 SkScalar prevX = fPrevPt.fX; | |
134 SkScalar prevY = fPrevPt.fY; | |
135 | |
136 SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, | |
137 unitNormal)); | |
138 | |
139 if (fSegmentCount == 0) { | |
140 fFirstNormal = *normal; | |
141 fFirstUnitNormal = *unitNormal; | |
142 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); | |
143 | |
144 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); | |
145 fInner.moveTo(prevX - normal->fX, prevY - normal->fY); | |
146 } else { // we have a previous segment | |
147 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, | |
148 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); | |
149 } | |
150 fPrevIsLine = currIsLine; | |
151 } | |
152 | |
153 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, | |
154 const SkVector& unitNormal) { | |
155 fPrevPt = currPt; | |
156 fPrevUnitNormal = unitNormal; | |
157 fPrevNormal = normal; | |
158 fSegmentCount += 1; | |
159 } | |
160 | |
161 void SkPathStroker::finishContour(bool close, bool currIsLine) { | |
162 if (fSegmentCount > 0) { | |
163 SkPoint pt; | |
164 | |
165 if (close) { | |
166 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, | |
167 fFirstUnitNormal, fRadius, fInvMiterLimit, | |
168 fPrevIsLine, currIsLine); | |
169 fOuter.close(); | |
170 // now add fInner as its own contour | |
171 fInner.getLastPt(&pt); | |
172 fOuter.moveTo(pt.fX, pt.fY); | |
173 fOuter.reversePathTo(fInner); | |
174 fOuter.close(); | |
175 } else { // add caps to start and end | |
176 // cap the end | |
177 fInner.getLastPt(&pt); | |
178 fCapper(&fOuter, fPrevPt, fPrevNormal, pt, | |
179 currIsLine ? &fInner : NULL); | |
180 fOuter.reversePathTo(fInner); | |
181 // cap the start | |
182 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, | |
183 fPrevIsLine ? &fInner : NULL); | |
184 fOuter.close(); | |
185 } | |
186 } | |
187 fInner.reset(); | |
188 fSegmentCount = -1; | |
189 } | |
190 | |
191 /////////////////////////////////////////////////////////////////////////////// | |
192 | |
193 SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit, | |
194 SkPaint::Cap cap, SkPaint::Join join) | |
195 : fRadius(radius) { | |
196 | |
197 /* This is only used when join is miter_join, but we initialize it here | |
198 so that it is always defined, to fis valgrind warnings. | |
199 */ | |
200 fInvMiterLimit = 0; | |
201 | |
202 if (join == SkPaint::kMiter_Join) { | |
203 if (miterLimit <= SK_Scalar1) { | |
204 join = SkPaint::kBevel_Join; | |
205 } else { | |
206 fInvMiterLimit = SkScalarInvert(miterLimit); | |
207 } | |
208 } | |
209 fCapper = SkStrokerPriv::CapFactory(cap); | |
210 fJoiner = SkStrokerPriv::JoinFactory(join); | |
211 fSegmentCount = -1; | |
212 fPrevIsLine = false; | |
213 } | |
214 | |
215 void SkPathStroker::moveTo(const SkPoint& pt) { | |
216 if (fSegmentCount > 0) { | |
217 this->finishContour(false, false); | |
218 } | |
219 fSegmentCount = 0; | |
220 fFirstPt = fPrevPt = pt; | |
221 } | |
222 | |
223 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { | |
224 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); | |
225 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); | |
226 } | |
227 | |
228 void SkPathStroker::lineTo(const SkPoint& currPt) { | |
229 if (degenerate_line(fPrevPt, currPt)) { | |
230 return; | |
231 } | |
232 SkVector normal, unitNormal; | |
233 | |
234 this->preJoinTo(currPt, &normal, &unitNormal, true); | |
235 this->line_to(currPt, normal); | |
236 this->postJoinTo(currPt, normal, unitNormal); | |
237 } | |
238 | |
239 void SkPathStroker::quad_to(const SkPoint pts[3], | |
240 const SkVector& normalAB, const SkVector& unitNormalAB, | |
241 SkVector* normalBC, SkVector* unitNormalBC, | |
242 int subDivide) { | |
243 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, | |
244 normalBC, unitNormalBC)) { | |
245 // pts[1] nearly equals pts[2], so just draw a line to pts[2] | |
246 this->line_to(pts[2], normalAB); | |
247 *normalBC = normalAB; | |
248 *unitNormalBC = unitNormalAB; | |
249 return; | |
250 } | |
251 | |
252 if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) { | |
253 SkPoint tmp[5]; | |
254 SkVector norm, unit; | |
255 | |
256 SkChopQuadAtHalf(pts, tmp); | |
257 this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); | |
258 this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); | |
259 } else { | |
260 SkVector normalB, unitB; | |
261 SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius, | |
262 &normalB, &unitB)); | |
263 | |
264 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | |
265 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); | |
266 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | |
267 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); | |
268 } | |
269 } | |
270 | |
271 void SkPathStroker::cubic_to(const SkPoint pts[4], | |
272 const SkVector& normalAB, const SkVector& unitNormalAB, | |
273 SkVector* normalCD, SkVector* unitNormalCD, | |
274 int subDivide) { | |
275 SkVector ab = pts[1] - pts[0]; | |
276 SkVector cd = pts[3] - pts[2]; | |
277 SkVector normalBC, unitNormalBC; | |
278 | |
279 bool degenerateAB = degenerate_vector(ab); | |
280 bool degenerateCD = degenerate_vector(cd); | |
281 | |
282 if (degenerateAB && degenerateCD) { | |
283 DRAW_LINE: | |
284 this->line_to(pts[3], normalAB); | |
285 *normalCD = normalAB; | |
286 *unitNormalCD = unitNormalAB; | |
287 return; | |
288 } | |
289 | |
290 if (degenerateAB) { | |
291 ab = pts[2] - pts[0]; | |
292 degenerateAB = degenerate_vector(ab); | |
293 } | |
294 if (degenerateCD) { | |
295 cd = pts[3] - pts[1]; | |
296 degenerateCD = degenerate_vector(cd); | |
297 } | |
298 if (degenerateAB || degenerateCD) { | |
299 goto DRAW_LINE; | |
300 } | |
301 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); | |
302 bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, | |
303 &normalBC, &unitNormalBC); | |
304 | |
305 if (--subDivide >= 0 && | |
306 (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || | |
307 normals_too_curvy(unitNormalBC, *unitNormalCD))) { | |
308 SkPoint tmp[7]; | |
309 SkVector norm, unit, dummy, unitDummy; | |
310 | |
311 SkChopCubicAtHalf(pts, tmp); | |
312 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, | |
313 subDivide); | |
314 // we use dummys since we already have a valid (and more accurate) | |
315 // normals for CD | |
316 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); | |
317 } else { | |
318 SkVector normalB, normalC; | |
319 | |
320 // need normals to inset/outset the off-curve pts B and C | |
321 | |
322 if (0) { // this is normal to the line between our adjacent pts | |
323 normalB = pts[2] - pts[0]; | |
324 normalB.rotateCCW(); | |
325 SkAssertResult(normalB.setLength(fRadius)); | |
326 | |
327 normalC = pts[3] - pts[1]; | |
328 normalC.rotateCCW(); | |
329 SkAssertResult(normalC.setLength(fRadius)); | |
330 } else { // miter-join | |
331 SkVector unitBC = pts[2] - pts[1]; | |
332 unitBC.normalize(); | |
333 unitBC.rotateCCW(); | |
334 | |
335 normalB = unitNormalAB + unitBC; | |
336 normalC = *unitNormalCD + unitBC; | |
337 | |
338 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); | |
339 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, | |
340 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | |
341 dot = SkPoint::DotProduct(*unitNormalCD, unitBC); | |
342 SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, | |
343 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | |
344 } | |
345 | |
346 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | |
347 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, | |
348 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); | |
349 | |
350 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | |
351 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, | |
352 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); | |
353 } | |
354 } | |
355 | |
356 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { | |
357 bool degenerateAB = degenerate_line(fPrevPt, pt1); | |
358 bool degenerateBC = degenerate_line(pt1, pt2); | |
359 | |
360 if (degenerateAB | degenerateBC) { | |
361 if (degenerateAB ^ degenerateBC) { | |
362 this->lineTo(pt2); | |
363 } | |
364 return; | |
365 } | |
366 | |
367 SkVector normalAB, unitAB, normalBC, unitBC; | |
368 | |
369 this->preJoinTo(pt1, &normalAB, &unitAB, false); | |
370 | |
371 { | |
372 SkPoint pts[3], tmp[5]; | |
373 pts[0] = fPrevPt; | |
374 pts[1] = pt1; | |
375 pts[2] = pt2; | |
376 | |
377 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) { | |
378 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY); | |
379 unitBC.rotateCCW(); | |
380 if (normals_too_pinchy(unitAB, unitBC)) { | |
381 normalBC = unitBC; | |
382 normalBC.scale(fRadius); | |
383 | |
384 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY); | |
385 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY); | |
386 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY); | |
387 | |
388 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY); | |
389 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY); | |
390 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY); | |
391 | |
392 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius, | |
393 SkPath::kCW_Direction); | |
394 } else { | |
395 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC, | |
396 kMaxQuadSubdivide); | |
397 SkVector n = normalBC; | |
398 SkVector u = unitBC; | |
399 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, | |
400 kMaxQuadSubdivide); | |
401 } | |
402 } else { | |
403 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, | |
404 kMaxQuadSubdivide); | |
405 } | |
406 } | |
407 | |
408 this->postJoinTo(pt2, normalBC, unitBC); | |
409 } | |
410 | |
411 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, | |
412 const SkPoint& pt3) { | |
413 bool degenerateAB = degenerate_line(fPrevPt, pt1); | |
414 bool degenerateBC = degenerate_line(pt1, pt2); | |
415 bool degenerateCD = degenerate_line(pt2, pt3); | |
416 | |
417 if (degenerateAB + degenerateBC + degenerateCD >= 2) { | |
418 this->lineTo(pt3); | |
419 return; | |
420 } | |
421 | |
422 SkVector normalAB, unitAB, normalCD, unitCD; | |
423 | |
424 // find the first tangent (which might be pt1 or pt2 | |
425 { | |
426 const SkPoint* nextPt = &pt1; | |
427 if (degenerateAB) | |
428 nextPt = &pt2; | |
429 this->preJoinTo(*nextPt, &normalAB, &unitAB, false); | |
430 } | |
431 | |
432 { | |
433 SkPoint pts[4], tmp[13]; | |
434 int i, count; | |
435 SkVector n, u; | |
436 SkScalar tValues[3]; | |
437 | |
438 pts[0] = fPrevPt; | |
439 pts[1] = pt1; | |
440 pts[2] = pt2; | |
441 pts[3] = pt3; | |
442 | |
443 #if 1 | |
444 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); | |
445 #else | |
446 count = 1; | |
447 memcpy(tmp, pts, 4 * sizeof(SkPoint)); | |
448 #endif | |
449 n = normalAB; | |
450 u = unitAB; | |
451 for (i = 0; i < count; i++) { | |
452 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, | |
453 kMaxCubicSubdivide); | |
454 if (i == count - 1) { | |
455 break; | |
456 } | |
457 n = normalCD; | |
458 u = unitCD; | |
459 | |
460 } | |
461 | |
462 // check for too pinchy | |
463 for (i = 1; i < count; i++) { | |
464 SkPoint p; | |
465 SkVector v, c; | |
466 | |
467 SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); | |
468 | |
469 SkScalar dot = SkPoint::DotProduct(c, c); | |
470 v.scale(SkScalarInvert(dot)); | |
471 | |
472 if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) { | |
473 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); | |
474 } | |
475 } | |
476 | |
477 } | |
478 | |
479 this->postJoinTo(pt3, normalCD, unitCD); | |
480 } | |
481 | |
482 /////////////////////////////////////////////////////////////////////////////// | |
483 /////////////////////////////////////////////////////////////////////////////// | |
484 | |
485 #include "SkPaint.h" | |
486 | |
487 SkStroke::SkStroke() { | |
488 fWidth = SK_DefaultStrokeWidth; | |
489 fMiterLimit = SK_DefaultMiterLimit; | |
490 fCap = SkPaint::kDefault_Cap; | |
491 fJoin = SkPaint::kDefault_Join; | |
492 fDoFill = false; | |
493 } | |
494 | |
495 SkStroke::SkStroke(const SkPaint& p) { | |
496 fWidth = p.getStrokeWidth(); | |
497 fMiterLimit = p.getStrokeMiter(); | |
498 fCap = (uint8_t)p.getStrokeCap(); | |
499 fJoin = (uint8_t)p.getStrokeJoin(); | |
500 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | |
501 } | |
502 | |
503 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { | |
504 fWidth = width; | |
505 fMiterLimit = p.getStrokeMiter(); | |
506 fCap = (uint8_t)p.getStrokeCap(); | |
507 fJoin = (uint8_t)p.getStrokeJoin(); | |
508 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | |
509 } | |
510 | |
511 void SkStroke::setWidth(SkScalar width) { | |
512 SkASSERT(width >= 0); | |
513 fWidth = width; | |
514 } | |
515 | |
516 void SkStroke::setMiterLimit(SkScalar miterLimit) { | |
517 SkASSERT(miterLimit >= 0); | |
518 fMiterLimit = miterLimit; | |
519 } | |
520 | |
521 void SkStroke::setCap(SkPaint::Cap cap) { | |
522 SkASSERT((unsigned)cap < SkPaint::kCapCount); | |
523 fCap = SkToU8(cap); | |
524 } | |
525 | |
526 void SkStroke::setJoin(SkPaint::Join join) { | |
527 SkASSERT((unsigned)join < SkPaint::kJoinCount); | |
528 fJoin = SkToU8(join); | |
529 } | |
530 | |
531 /////////////////////////////////////////////////////////////////////////////// | |
532 | |
533 #ifdef SK_SCALAR_IS_FIXED | |
534 /* return non-zero if the path is too big, and should be shrunk to avoid | |
535 overflows during intermediate calculations. Note that we compute the | |
536 bounds for this. If we had a custom callback/walker for paths, we could | |
537 perhaps go faster by using that, and just perform the abs | in that | |
538 routine | |
539 */ | |
540 static int needs_to_shrink(const SkPath& path) { | |
541 SkRect r; | |
542 path.computeBounds(&r, SkPath::kFast_BoundsType); | |
543 SkFixed mask = SkAbs32(r.fLeft); | |
544 mask |= SkAbs32(r.fTop); | |
545 mask |= SkAbs32(r.fRight); | |
546 mask |= SkAbs32(r.fBottom); | |
547 // we need the top 3 bits clear (after abs) to avoid overflow | |
548 return mask >> 29; | |
549 } | |
550 | |
551 static void identity_proc(SkPoint pts[], int count) {} | |
552 static void shift_down_2_proc(SkPoint pts[], int count) { | |
553 for (int i = 0; i < count; i++) { | |
554 pts->fX >>= 2; | |
555 pts->fY >>= 2; | |
556 pts += 1; | |
557 } | |
558 } | |
559 #define APPLY_PROC(proc, pts, count) proc(pts, count) | |
560 #else // float does need any of this | |
561 #define APPLY_PROC(proc, pts, count) | |
562 #endif | |
563 | |
564 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { | |
565 SkASSERT(&src != NULL && dst != NULL); | |
566 | |
567 SkScalar radius = SkScalarHalf(fWidth); | |
568 | |
569 dst->reset(); | |
570 if (radius <= 0) { | |
571 return; | |
572 } | |
573 | |
574 #ifdef SK_SCALAR_IS_FIXED | |
575 void (*proc)(SkPoint pts[], int count) = identity_proc; | |
576 if (needs_to_shrink(src)) { | |
577 proc = shift_down_2_proc; | |
578 radius >>= 2; | |
579 if (radius == 0) { | |
580 return; | |
581 } | |
582 } | |
583 #endif | |
584 | |
585 SkPathStroker stroker(radius, fMiterLimit, this->getCap(), | |
586 this->getJoin()); | |
587 | |
588 SkPath::Iter iter(src, false); | |
589 SkPoint pts[4]; | |
590 SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; | |
591 | |
592 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | |
593 switch (verb) { | |
594 case SkPath::kMove_Verb: | |
595 APPLY_PROC(proc, &pts[0], 1); | |
596 stroker.moveTo(pts[0]); | |
597 break; | |
598 case SkPath::kLine_Verb: | |
599 APPLY_PROC(proc, &pts[1], 1); | |
600 stroker.lineTo(pts[1]); | |
601 lastSegment = verb; | |
602 break; | |
603 case SkPath::kQuad_Verb: | |
604 APPLY_PROC(proc, &pts[1], 2); | |
605 stroker.quadTo(pts[1], pts[2]); | |
606 lastSegment = verb; | |
607 break; | |
608 case SkPath::kCubic_Verb: | |
609 APPLY_PROC(proc, &pts[1], 3); | |
610 stroker.cubicTo(pts[1], pts[2], pts[3]); | |
611 lastSegment = verb; | |
612 break; | |
613 case SkPath::kClose_Verb: | |
614 stroker.close(lastSegment == SkPath::kLine_Verb); | |
615 break; | |
616 default: | |
617 break; | |
618 } | |
619 } | |
620 stroker.done(dst, lastSegment == SkPath::kLine_Verb); | |
621 | |
622 #ifdef SK_SCALAR_IS_FIXED | |
623 // undo our previous down_shift | |
624 if (shift_down_2_proc == proc) { | |
625 // need a real shift methid on path. antialias paths could use this too | |
626 SkMatrix matrix; | |
627 matrix.setScale(SkIntToScalar(4), SkIntToScalar(4)); | |
628 dst->transform(matrix); | |
629 } | |
630 #endif | |
631 | |
632 if (fDoFill) { | |
633 dst->addPath(src); | |
634 } | |
635 } | |
636 | |
637 void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1, | |
638 SkPath* dst) const { | |
639 SkPath tmp; | |
640 | |
641 tmp.moveTo(p0); | |
642 tmp.lineTo(p1); | |
643 this->strokePath(tmp, dst); | |
644 } | |
645 | |
OLD | NEW |