Home | History | Annotate | Download | only in core
      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 
     17 static inline bool degenerate_vector(const SkVector& v) {
     18     return !SkPoint::CanNormalize(v.fX, v.fY);
     19 }
     20 
     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 
     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 
     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 
     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&);
     73     void close(bool isLine) { this->finishContour(true, isLine); }
     74 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    501 void SkStroke::setWidth(SkScalar width) {
    502     SkASSERT(width >= 0);
    503     fWidth = width;
    504 }
    505 
    506 void SkStroke::setMiterLimit(SkScalar miterLimit) {
    507     SkASSERT(miterLimit >= 0);
    508     fMiterLimit = miterLimit;
    509 }
    510 
    511 void SkStroke::setCap(SkPaint::Cap cap) {
    512     SkASSERT((unsigned)cap < SkPaint::kCapCount);
    513     fCap = SkToU8(cap);
    514 }
    515 
    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     */
    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 
    540     static void identity_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 
    553 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 
    656 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