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