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