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
degenerate_vector(const SkVector & v)24 static inline bool degenerate_vector(const SkVector& v) {
25 return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY);
26 }
27
degenerate_line(const SkPoint & a,const SkPoint & b,SkScalar tolerance=SK_ScalarNearlyZero)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
normals_too_curvy(const SkVector & norm0,SkVector & norm1)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
normals_too_pinchy(const SkVector & norm0,SkVector & norm1)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
set_normal_unitnormal(const SkPoint & before,const SkPoint & after,SkScalar radius,SkVector * normal,SkVector * unitNormal)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
set_normal_unitnormal(const SkVector & vec,SkScalar radius,SkVector * normal,SkVector * unitNormal)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&);
close(bool isLine)86 void close(bool isLine) { this->finishContour(true, isLine); }
87
done(SkPath * dst,bool isLine)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
preJoinTo(const SkPoint & currPt,SkVector * normal,SkVector * unitNormal,bool currIsLine)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
postJoinTo(const SkPoint & currPt,const SkVector & normal,const SkVector & unitNormal)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
finishContour(bool close,bool currIsLine)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
SkPathStroker(SkScalar radius,SkScalar miterLimit,SkPaint::Cap cap,SkPaint::Join join)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
moveTo(const SkPoint & pt)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
line_to(const SkPoint & currPt,const SkVector & normal)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
lineTo(const SkPoint & currPt)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
quad_to(const SkPoint pts[3],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalBC,SkVector * unitNormalBC,int subDivide)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
cubic_to(const SkPoint pts[4],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalCD,SkVector * unitNormalCD,int subDivide)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 (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
306 normals_too_curvy(unitNormalBC, *unitNormalCD)) {
307 // subdivide if we can
308 if (--subDivide < 0) {
309 goto DRAW_LINE;
310 }
311 SkPoint tmp[7];
312 SkVector norm, unit, dummy, unitDummy;
313
314 SkChopCubicAtHalf(pts, tmp);
315 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
316 subDivide);
317 // we use dummys since we already have a valid (and more accurate)
318 // normals for CD
319 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
320 } else {
321 SkVector normalB, normalC;
322
323 // need normals to inset/outset the off-curve pts B and C
324
325 if (0) { // this is normal to the line between our adjacent pts
326 normalB = pts[2] - pts[0];
327 normalB.rotateCCW();
328 SkAssertResult(normalB.setLength(fRadius));
329
330 normalC = pts[3] - pts[1];
331 normalC.rotateCCW();
332 SkAssertResult(normalC.setLength(fRadius));
333 } else { // miter-join
334 SkVector unitBC = pts[2] - pts[1];
335 unitBC.normalize();
336 unitBC.rotateCCW();
337
338 normalB = unitNormalAB + unitBC;
339 normalC = *unitNormalCD + unitBC;
340
341 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
342 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
343 SkScalarSqrt((SK_Scalar1 + dot)/2))));
344 dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
345 SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
346 SkScalarSqrt((SK_Scalar1 + dot)/2))));
347 }
348
349 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
350 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
351 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
352
353 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
354 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
355 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
356 }
357 }
358
quadTo(const SkPoint & pt1,const SkPoint & pt2)359 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
360 bool degenerateAB = degenerate_line(fPrevPt, pt1);
361 bool degenerateBC = degenerate_line(pt1, pt2);
362
363 if (degenerateAB | degenerateBC) {
364 if (degenerateAB ^ degenerateBC) {
365 this->lineTo(pt2);
366 }
367 return;
368 }
369
370 SkVector normalAB, unitAB, normalBC, unitBC;
371
372 this->preJoinTo(pt1, &normalAB, &unitAB, false);
373
374 {
375 SkPoint pts[3], tmp[5];
376 pts[0] = fPrevPt;
377 pts[1] = pt1;
378 pts[2] = pt2;
379
380 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
381 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
382 unitBC.rotateCCW();
383 if (normals_too_pinchy(unitAB, unitBC)) {
384 normalBC = unitBC;
385 normalBC.scale(fRadius);
386
387 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
388 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
389 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
390
391 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
392 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
393 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
394
395 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
396 SkPath::kCW_Direction);
397 } else {
398 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
399 kMaxQuadSubdivide);
400 SkVector n = normalBC;
401 SkVector u = unitBC;
402 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
403 kMaxQuadSubdivide);
404 }
405 } else {
406 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
407 kMaxQuadSubdivide);
408 }
409 }
410
411 this->postJoinTo(pt2, normalBC, unitBC);
412 }
413
cubicTo(const SkPoint & pt1,const SkPoint & pt2,const SkPoint & pt3)414 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
415 const SkPoint& pt3) {
416 bool degenerateAB = degenerate_line(fPrevPt, pt1);
417 bool degenerateBC = degenerate_line(pt1, pt2);
418 bool degenerateCD = degenerate_line(pt2, pt3);
419
420 if (degenerateAB + degenerateBC + degenerateCD >= 2) {
421 this->lineTo(pt3);
422 return;
423 }
424
425 SkVector normalAB, unitAB, normalCD, unitCD;
426
427 // find the first tangent (which might be pt1 or pt2
428 {
429 const SkPoint* nextPt = &pt1;
430 if (degenerateAB)
431 nextPt = &pt2;
432 this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
433 }
434
435 {
436 SkPoint pts[4], tmp[13];
437 int i, count;
438 SkVector n, u;
439 SkScalar tValues[3];
440
441 pts[0] = fPrevPt;
442 pts[1] = pt1;
443 pts[2] = pt2;
444 pts[3] = pt3;
445
446 #if 1
447 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
448 #else
449 count = 1;
450 memcpy(tmp, pts, 4 * sizeof(SkPoint));
451 #endif
452 n = normalAB;
453 u = unitAB;
454 for (i = 0; i < count; i++) {
455 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
456 kMaxCubicSubdivide);
457 if (i == count - 1) {
458 break;
459 }
460 n = normalCD;
461 u = unitCD;
462
463 }
464
465 // check for too pinchy
466 for (i = 1; i < count; i++) {
467 SkPoint p;
468 SkVector v, c;
469
470 SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
471
472 SkScalar dot = SkPoint::DotProduct(c, c);
473 v.scale(SkScalarInvert(dot));
474
475 if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
476 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
477 }
478 }
479
480 }
481
482 this->postJoinTo(pt3, normalCD, unitCD);
483 }
484
485 ///////////////////////////////////////////////////////////////////////////////
486 ///////////////////////////////////////////////////////////////////////////////
487
488 #include "SkPaint.h"
489
SkStroke()490 SkStroke::SkStroke() {
491 fWidth = SK_DefaultStrokeWidth;
492 fMiterLimit = SK_DefaultMiterLimit;
493 fCap = SkPaint::kDefault_Cap;
494 fJoin = SkPaint::kDefault_Join;
495 fDoFill = false;
496 }
497
SkStroke(const SkPaint & p)498 SkStroke::SkStroke(const SkPaint& p) {
499 fWidth = p.getStrokeWidth();
500 fMiterLimit = p.getStrokeMiter();
501 fCap = (uint8_t)p.getStrokeCap();
502 fJoin = (uint8_t)p.getStrokeJoin();
503 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
504 }
505
SkStroke(const SkPaint & p,SkScalar width)506 SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
507 fWidth = width;
508 fMiterLimit = p.getStrokeMiter();
509 fCap = (uint8_t)p.getStrokeCap();
510 fJoin = (uint8_t)p.getStrokeJoin();
511 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
512 }
513
setWidth(SkScalar width)514 void SkStroke::setWidth(SkScalar width) {
515 SkASSERT(width >= 0);
516 fWidth = width;
517 }
518
setMiterLimit(SkScalar miterLimit)519 void SkStroke::setMiterLimit(SkScalar miterLimit) {
520 SkASSERT(miterLimit >= 0);
521 fMiterLimit = miterLimit;
522 }
523
setCap(SkPaint::Cap cap)524 void SkStroke::setCap(SkPaint::Cap cap) {
525 SkASSERT((unsigned)cap < SkPaint::kCapCount);
526 fCap = SkToU8(cap);
527 }
528
setJoin(SkPaint::Join join)529 void SkStroke::setJoin(SkPaint::Join join) {
530 SkASSERT((unsigned)join < SkPaint::kJoinCount);
531 fJoin = SkToU8(join);
532 }
533
534 ///////////////////////////////////////////////////////////////////////////////
535
536 #ifdef SK_SCALAR_IS_FIXED
537 /* return non-zero if the path is too big, and should be shrunk to avoid
538 overflows during intermediate calculations. Note that we compute the
539 bounds for this. If we had a custom callback/walker for paths, we could
540 perhaps go faster by using that, and just perform the abs | in that
541 routine
542 */
needs_to_shrink(const SkPath & path)543 static int needs_to_shrink(const SkPath& path) {
544 const SkRect& r = path.getBounds();
545 SkFixed mask = SkAbs32(r.fLeft);
546 mask |= SkAbs32(r.fTop);
547 mask |= SkAbs32(r.fRight);
548 mask |= SkAbs32(r.fBottom);
549 // we need the top 3 bits clear (after abs) to avoid overflow
550 return mask >> 29;
551 }
552
identity_proc(SkPoint pts[],int count)553 static void identity_proc(SkPoint pts[], int count) {}
shift_down_2_proc(SkPoint pts[],int count)554 static void shift_down_2_proc(SkPoint pts[], int count) {
555 for (int i = 0; i < count; i++) {
556 pts->fX >>= 2;
557 pts->fY >>= 2;
558 pts += 1;
559 }
560 }
561 #define APPLY_PROC(proc, pts, count) proc(pts, count)
562 #else // float does need any of this
563 #define APPLY_PROC(proc, pts, count)
564 #endif
565
strokePath(const SkPath & src,SkPath * dst) const566 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
567 SkASSERT(&src != NULL && dst != NULL);
568
569 SkScalar radius = SkScalarHalf(fWidth);
570
571 dst->reset();
572 if (radius <= 0) {
573 return;
574 }
575
576 #ifdef SK_SCALAR_IS_FIXED
577 void (*proc)(SkPoint pts[], int count) = identity_proc;
578 if (needs_to_shrink(src)) {
579 proc = shift_down_2_proc;
580 radius >>= 2;
581 if (radius == 0) {
582 return;
583 }
584 }
585 #endif
586
587 SkPathStroker stroker(radius, fMiterLimit, this->getCap(),
588 this->getJoin());
589
590 SkPath::Iter iter(src, false);
591 SkPoint pts[4];
592 SkPath::Verb verb, lastSegment = SkPath::kMove_Verb;
593
594 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
595 switch (verb) {
596 case SkPath::kMove_Verb:
597 APPLY_PROC(proc, &pts[0], 1);
598 stroker.moveTo(pts[0]);
599 break;
600 case SkPath::kLine_Verb:
601 APPLY_PROC(proc, &pts[1], 1);
602 stroker.lineTo(pts[1]);
603 lastSegment = verb;
604 break;
605 case SkPath::kQuad_Verb:
606 APPLY_PROC(proc, &pts[1], 2);
607 stroker.quadTo(pts[1], pts[2]);
608 lastSegment = verb;
609 break;
610 case SkPath::kCubic_Verb:
611 APPLY_PROC(proc, &pts[1], 3);
612 stroker.cubicTo(pts[1], pts[2], pts[3]);
613 lastSegment = verb;
614 break;
615 case SkPath::kClose_Verb:
616 stroker.close(lastSegment == SkPath::kLine_Verb);
617 break;
618 default:
619 break;
620 }
621 }
622 stroker.done(dst, lastSegment == SkPath::kLine_Verb);
623
624 #ifdef SK_SCALAR_IS_FIXED
625 // undo our previous down_shift
626 if (shift_down_2_proc == proc) {
627 // need a real shift methid on path. antialias paths could use this too
628 SkMatrix matrix;
629 matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
630 dst->transform(matrix);
631 }
632 #endif
633
634 if (fDoFill) {
635 dst->addPath(src);
636 }
637 }
638
strokeLine(const SkPoint & p0,const SkPoint & p1,SkPath * dst) const639 void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
640 SkPath* dst) const {
641 SkPath tmp;
642
643 tmp.moveTo(p0);
644 tmp.lineTo(p1);
645 this->strokePath(tmp, dst);
646 }
647
648