• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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