Home | History | Annotate | Download | only in core
      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 
     24 static inline bool degenerate_vector(const SkVector& v) {
     25     return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY);
     26 }
     27 
     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 
     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 
     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 
     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 
     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&);
     86     void close(bool isLine) { this->finishContour(true, isLine); }
     87 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    514 void SkStroke::setWidth(SkScalar width) {
    515     SkASSERT(width >= 0);
    516     fWidth = width;
    517 }
    518 
    519 void SkStroke::setMiterLimit(SkScalar miterLimit) {
    520     SkASSERT(miterLimit >= 0);
    521     fMiterLimit = miterLimit;
    522 }
    523 
    524 void SkStroke::setCap(SkPaint::Cap cap) {
    525     SkASSERT((unsigned)cap < SkPaint::kCapCount);
    526     fCap = SkToU8(cap);
    527 }
    528 
    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     */
    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 
    553     static void identity_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 
    566 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     } else {
    637         if (src.countPoints() == 2) {
    638             dst->setIsConvex(true);
    639         }
    640     }
    641 }
    642 
    643 void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
    644                           SkPath* dst) const {
    645     SkPath  tmp;
    646 
    647     tmp.moveTo(p0);
    648     tmp.lineTo(p1);
    649     this->strokePath(tmp, dst);
    650 }
    651 
    652