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