Home | History | Annotate | Download | only in core
      1 /* libs/graphics/sgl/SkPath.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkPath.h"
     19 #include "SkFlattenable.h"
     20 #include "SkMath.h"
     21 
     22 ////////////////////////////////////////////////////////////////////////////
     23 
     24 /*  This guy's constructor/destructor bracket a path editing operation. It is
     25     used when we know the bounds of the amount we are going to add to the path
     26     (usually a new contour, but not required).
     27 
     28     It captures some state about the path up front (i.e. if it already has a
     29     cached bounds), and the if it can, it updates the cache bounds explicitly,
     30     avoiding the need to revisit all of the points in getBounds().
     31 
     32     It also notes if the path was originally empty, and if so, sets isConvex
     33     to true. Thus it can only be used if the contour being added is convex.
     34  */
     35 class SkAutoPathBoundsUpdate {
     36 public:
     37     SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
     38         this->init(path);
     39     }
     40 
     41     SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
     42                            SkScalar right, SkScalar bottom) {
     43         fRect.set(left, top, right, bottom);
     44         this->init(path);
     45     }
     46 
     47     ~SkAutoPathBoundsUpdate() {
     48         fPath->setIsConvex(fEmpty);
     49         if (fEmpty) {
     50             fPath->fBounds = fRect;
     51             fPath->fBoundsIsDirty = false;
     52         } else if (!fDirty) {
     53             fPath->fBounds.join(fRect);
     54             fPath->fBoundsIsDirty = false;
     55         }
     56     }
     57 
     58 private:
     59     SkPath* fPath;
     60     SkRect  fRect;
     61     bool    fDirty;
     62     bool    fEmpty;
     63 
     64     // returns true if we should proceed
     65     void init(SkPath* path) {
     66         fPath = path;
     67         fDirty = SkToBool(path->fBoundsIsDirty);
     68         fEmpty = path->isEmpty();
     69         // Cannot use fRect for our bounds unless we know it is sorted
     70         fRect.sort();
     71     }
     72 };
     73 
     74 static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
     75     if (pts.count() <= 1) {  // we ignore just 1 point (moveto)
     76         bounds->set(0, 0, 0, 0);
     77     } else {
     78         bounds->set(pts.begin(), pts.count());
     79 //        SkDebugf("------- compute bounds %p %d", &pts, pts.count());
     80     }
     81 }
     82 
     83 ////////////////////////////////////////////////////////////////////////////
     84 
     85 /*
     86     Stores the verbs and points as they are given to us, with exceptions:
     87     - we only record "Close" if it was immediately preceeded by Line | Quad | Cubic
     88     - we insert a Move(0,0) if Line | Quad | Cubic is our first command
     89 
     90     The iterator does more cleanup, especially if forceClose == true
     91     1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
     92     2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1
     93     3. if we encounter Line | Quad | Cubic after Close, cons up a Move
     94 */
     95 
     96 ////////////////////////////////////////////////////////////////////////////
     97 
     98 SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
     99     fIsConvex = false;
    100 }
    101 
    102 SkPath::SkPath(const SkPath& src) {
    103     SkDEBUGCODE(src.validate();)
    104     *this = src;
    105 }
    106 
    107 SkPath::~SkPath() {
    108     SkDEBUGCODE(this->validate();)
    109 }
    110 
    111 SkPath& SkPath::operator=(const SkPath& src) {
    112     SkDEBUGCODE(src.validate();)
    113 
    114     if (this != &src) {
    115         fBounds         = src.fBounds;
    116         fPts            = src.fPts;
    117         fVerbs          = src.fVerbs;
    118         fFillType       = src.fFillType;
    119         fBoundsIsDirty  = src.fBoundsIsDirty;
    120         fIsConvex       = src.fIsConvex;
    121     }
    122     SkDEBUGCODE(this->validate();)
    123     return *this;
    124 }
    125 
    126 bool operator==(const SkPath& a, const SkPath& b) {
    127     // note: don't need to look at isConvex or bounds, since just comparing the
    128     // raw data is sufficient.
    129     return &a == &b ||
    130         (a.fFillType == b.fFillType && a.fVerbs == b.fVerbs && a.fPts == b.fPts);
    131 }
    132 
    133 void SkPath::swap(SkPath& other) {
    134     SkASSERT(&other != NULL);
    135 
    136     if (this != &other) {
    137         SkTSwap<SkRect>(fBounds, other.fBounds);
    138         fPts.swap(other.fPts);
    139         fVerbs.swap(other.fVerbs);
    140         SkTSwap<uint8_t>(fFillType, other.fFillType);
    141         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
    142         SkTSwap<uint8_t>(fIsConvex, other.fIsConvex);
    143     }
    144 }
    145 
    146 void SkPath::reset() {
    147     SkDEBUGCODE(this->validate();)
    148 
    149     fPts.reset();
    150     fVerbs.reset();
    151     fBoundsIsDirty = true;
    152 }
    153 
    154 void SkPath::rewind() {
    155     SkDEBUGCODE(this->validate();)
    156 
    157     fPts.rewind();
    158     fVerbs.rewind();
    159     fBoundsIsDirty = true;
    160 }
    161 
    162 bool SkPath::isEmpty() const {
    163     SkDEBUGCODE(this->validate();)
    164 
    165     int count = fVerbs.count();
    166     return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
    167 }
    168 
    169 bool SkPath::isRect(SkRect*) const {
    170     SkDEBUGCODE(this->validate();)
    171 
    172     SkASSERT(!"unimplemented");
    173     return false;
    174 }
    175 
    176 int SkPath::getPoints(SkPoint copy[], int max) const {
    177     SkDEBUGCODE(this->validate();)
    178 
    179     SkASSERT(max >= 0);
    180     int count = fPts.count();
    181     if (copy && max > 0 && count > 0) {
    182         memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
    183     }
    184     return count;
    185 }
    186 
    187 SkPoint SkPath::getPoint(int index) const {
    188     if ((unsigned)index < (unsigned)fPts.count()) {
    189         return fPts[index];
    190     }
    191     return SkPoint::Make(0, 0);
    192 }
    193 
    194 void SkPath::getLastPt(SkPoint* lastPt) const {
    195     SkDEBUGCODE(this->validate();)
    196 
    197     if (lastPt) {
    198         int count = fPts.count();
    199         if (count == 0) {
    200             lastPt->set(0, 0);
    201         } else {
    202             *lastPt = fPts[count - 1];
    203         }
    204     }
    205 }
    206 
    207 void SkPath::setLastPt(SkScalar x, SkScalar y) {
    208     SkDEBUGCODE(this->validate();)
    209 
    210     int count = fPts.count();
    211     if (count == 0) {
    212         this->moveTo(x, y);
    213     } else {
    214         fPts[count - 1].set(x, y);
    215     }
    216 }
    217 
    218 void SkPath::computeBounds() const {
    219     SkDEBUGCODE(this->validate();)
    220     SkASSERT(fBoundsIsDirty);
    221 
    222     fBoundsIsDirty = false;
    223     compute_pt_bounds(&fBounds, fPts);
    224 }
    225 
    226 //////////////////////////////////////////////////////////////////////////////
    227 //  Construction methods
    228 
    229 void SkPath::incReserve(U16CPU inc) {
    230     SkDEBUGCODE(this->validate();)
    231 
    232     fVerbs.setReserve(fVerbs.count() + inc);
    233     fPts.setReserve(fPts.count() + inc);
    234 
    235     SkDEBUGCODE(this->validate();)
    236 }
    237 
    238 void SkPath::moveTo(SkScalar x, SkScalar y) {
    239     SkDEBUGCODE(this->validate();)
    240 
    241     int      vc = fVerbs.count();
    242     SkPoint* pt;
    243 
    244     if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
    245         pt = &fPts[fPts.count() - 1];
    246     } else {
    247         pt = fPts.append();
    248         *fVerbs.append() = kMove_Verb;
    249     }
    250     pt->set(x, y);
    251 
    252     fBoundsIsDirty = true;
    253 }
    254 
    255 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
    256     SkPoint pt;
    257     this->getLastPt(&pt);
    258     this->moveTo(pt.fX + x, pt.fY + y);
    259 }
    260 
    261 void SkPath::lineTo(SkScalar x, SkScalar y) {
    262     SkDEBUGCODE(this->validate();)
    263 
    264     if (fVerbs.count() == 0) {
    265         fPts.append()->set(0, 0);
    266         *fVerbs.append() = kMove_Verb;
    267     }
    268     fPts.append()->set(x, y);
    269     *fVerbs.append() = kLine_Verb;
    270 
    271     fBoundsIsDirty = true;
    272 }
    273 
    274 void SkPath::rLineTo(SkScalar x, SkScalar y) {
    275     SkPoint pt;
    276     this->getLastPt(&pt);
    277     this->lineTo(pt.fX + x, pt.fY + y);
    278 }
    279 
    280 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
    281     SkDEBUGCODE(this->validate();)
    282 
    283     if (fVerbs.count() == 0) {
    284         fPts.append()->set(0, 0);
    285         *fVerbs.append() = kMove_Verb;
    286     }
    287 
    288     SkPoint* pts = fPts.append(2);
    289     pts[0].set(x1, y1);
    290     pts[1].set(x2, y2);
    291     *fVerbs.append() = kQuad_Verb;
    292 
    293     fBoundsIsDirty = true;
    294 }
    295 
    296 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
    297     SkPoint pt;
    298     this->getLastPt(&pt);
    299     this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
    300 }
    301 
    302 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    303                      SkScalar x3, SkScalar y3) {
    304     SkDEBUGCODE(this->validate();)
    305 
    306     if (fVerbs.count() == 0) {
    307         fPts.append()->set(0, 0);
    308         *fVerbs.append() = kMove_Verb;
    309     }
    310     SkPoint* pts = fPts.append(3);
    311     pts[0].set(x1, y1);
    312     pts[1].set(x2, y2);
    313     pts[2].set(x3, y3);
    314     *fVerbs.append() = kCubic_Verb;
    315 
    316     fBoundsIsDirty = true;
    317 }
    318 
    319 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    320                       SkScalar x3, SkScalar y3) {
    321     SkPoint pt;
    322     this->getLastPt(&pt);
    323     this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
    324                   pt.fX + x3, pt.fY + y3);
    325 }
    326 
    327 void SkPath::close() {
    328     SkDEBUGCODE(this->validate();)
    329 
    330     int count = fVerbs.count();
    331     if (count > 0) {
    332         switch (fVerbs[count - 1]) {
    333             case kLine_Verb:
    334             case kQuad_Verb:
    335             case kCubic_Verb:
    336                 *fVerbs.append() = kClose_Verb;
    337                 break;
    338             default:
    339                 // don't add a close if the prev wasn't a primitive
    340                 break;
    341         }
    342     }
    343 }
    344 
    345 ///////////////////////////////////////////////////////////////////////////////
    346 
    347 void SkPath::addRect(const SkRect& rect, Direction dir) {
    348     this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
    349 }
    350 
    351 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
    352                      SkScalar bottom, Direction dir) {
    353     SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
    354 
    355     this->incReserve(5);
    356 
    357     this->moveTo(left, top);
    358     if (dir == kCCW_Direction) {
    359         this->lineTo(left, bottom);
    360         this->lineTo(right, bottom);
    361         this->lineTo(right, top);
    362     } else {
    363         this->lineTo(right, top);
    364         this->lineTo(right, bottom);
    365         this->lineTo(left, bottom);
    366     }
    367     this->close();
    368 }
    369 
    370 #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
    371 
    372 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
    373                           Direction dir) {
    374     SkAutoPathBoundsUpdate apbu(this, rect);
    375 
    376     SkScalar    w = rect.width();
    377     SkScalar    halfW = SkScalarHalf(w);
    378     SkScalar    h = rect.height();
    379     SkScalar    halfH = SkScalarHalf(h);
    380 
    381     if (halfW <= 0 || halfH <= 0) {
    382         return;
    383     }
    384 
    385     bool    skip_hori = rx >= halfW;
    386     bool    skip_vert = ry >= halfH;
    387 
    388     if (skip_hori && skip_vert) {
    389         this->addOval(rect, dir);
    390         return;
    391     }
    392     if (skip_hori) {
    393         rx = halfW;
    394     } else if (skip_vert) {
    395         ry = halfH;
    396     }
    397 
    398     SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
    399     SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
    400 
    401     this->incReserve(17);
    402     this->moveTo(rect.fRight - rx, rect.fTop);
    403     if (dir == kCCW_Direction) {
    404         if (!skip_hori) {
    405             this->lineTo(rect.fLeft + rx, rect.fTop);       // top
    406         }
    407         this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
    408                       rect.fLeft, rect.fTop + ry - sy,
    409                       rect.fLeft, rect.fTop + ry);          // top-left
    410         if (!skip_vert) {
    411             this->lineTo(rect.fLeft, rect.fBottom - ry);        // left
    412         }
    413         this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
    414                       rect.fLeft + rx - sx, rect.fBottom,
    415                       rect.fLeft + rx, rect.fBottom);       // bot-left
    416         if (!skip_hori) {
    417             this->lineTo(rect.fRight - rx, rect.fBottom);   // bottom
    418         }
    419         this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
    420                       rect.fRight, rect.fBottom - ry + sy,
    421                       rect.fRight, rect.fBottom - ry);      // bot-right
    422         if (!skip_vert) {
    423             this->lineTo(rect.fRight, rect.fTop + ry);
    424         }
    425         this->cubicTo(rect.fRight, rect.fTop + ry - sy,
    426                       rect.fRight - rx + sx, rect.fTop,
    427                       rect.fRight - rx, rect.fTop);         // top-right
    428     } else {
    429         this->cubicTo(rect.fRight - rx + sx, rect.fTop,
    430                       rect.fRight, rect.fTop + ry - sy,
    431                       rect.fRight, rect.fTop + ry);         // top-right
    432         if (!skip_vert) {
    433             this->lineTo(rect.fRight, rect.fBottom - ry);
    434         }
    435         this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
    436                       rect.fRight - rx + sx, rect.fBottom,
    437                       rect.fRight - rx, rect.fBottom);      // bot-right
    438         if (!skip_hori) {
    439             this->lineTo(rect.fLeft + rx, rect.fBottom);    // bottom
    440         }
    441         this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
    442                       rect.fLeft, rect.fBottom - ry + sy,
    443                       rect.fLeft, rect.fBottom - ry);       // bot-left
    444         if (!skip_vert) {
    445             this->lineTo(rect.fLeft, rect.fTop + ry);       // left
    446         }
    447         this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
    448                       rect.fLeft + rx - sx, rect.fTop,
    449                       rect.fLeft + rx, rect.fTop);          // top-left
    450         if (!skip_hori) {
    451             this->lineTo(rect.fRight - rx, rect.fTop);      // top
    452         }
    453     }
    454     this->close();
    455 }
    456 
    457 static void add_corner_arc(SkPath* path, const SkRect& rect,
    458                            SkScalar rx, SkScalar ry, int startAngle,
    459                            SkPath::Direction dir, bool forceMoveTo) {
    460     rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
    461     ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
    462 
    463     SkRect   r;
    464     r.set(-rx, -ry, rx, ry);
    465 
    466     switch (startAngle) {
    467         case   0:
    468             r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
    469             break;
    470         case  90:
    471             r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
    472             break;
    473         case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
    474         case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
    475         default: SkASSERT(!"unexpected startAngle in add_corner_arc");
    476     }
    477 
    478     SkScalar start = SkIntToScalar(startAngle);
    479     SkScalar sweep = SkIntToScalar(90);
    480     if (SkPath::kCCW_Direction == dir) {
    481         start += sweep;
    482         sweep = -sweep;
    483     }
    484 
    485     path->arcTo(r, start, sweep, forceMoveTo);
    486 }
    487 
    488 void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
    489                           Direction dir) {
    490     SkAutoPathBoundsUpdate apbu(this, rect);
    491 
    492     if (kCW_Direction == dir) {
    493         add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
    494         add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
    495         add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
    496         add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
    497     } else {
    498         add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
    499         add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
    500         add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
    501         add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
    502     }
    503     this->close();
    504 }
    505 
    506 void SkPath::addOval(const SkRect& oval, Direction dir) {
    507     SkAutoPathBoundsUpdate apbu(this, oval);
    508 
    509     SkScalar    cx = oval.centerX();
    510     SkScalar    cy = oval.centerY();
    511     SkScalar    rx = SkScalarHalf(oval.width());
    512     SkScalar    ry = SkScalarHalf(oval.height());
    513 #if 0   // these seem faster than using quads (1/2 the number of edges)
    514     SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
    515     SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
    516 
    517     this->incReserve(13);
    518     this->moveTo(cx + rx, cy);
    519     if (dir == kCCW_Direction) {
    520         this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
    521         this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
    522         this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
    523         this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
    524     } else {
    525         this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
    526         this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
    527         this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
    528         this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
    529     }
    530 #else
    531     SkScalar    sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
    532     SkScalar    sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
    533     SkScalar    mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
    534     SkScalar    my = SkScalarMul(ry, SK_ScalarRoot2Over2);
    535 
    536     /*
    537         To handle imprecision in computing the center and radii, we revert to
    538         the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
    539         to ensure that we don't exceed the oval's bounds *ever*, since we want
    540         to use oval for our fast-bounds, rather than have to recompute it.
    541     */
    542     const SkScalar L = oval.fLeft;      // cx - rx
    543     const SkScalar T = oval.fTop;       // cy - ry
    544     const SkScalar R = oval.fRight;     // cx + rx
    545     const SkScalar B = oval.fBottom;    // cy + ry
    546 
    547     this->incReserve(17);   // 8 quads + close
    548     this->moveTo(R, cy);
    549     if (dir == kCCW_Direction) {
    550         this->quadTo(      R, cy - sy, cx + mx, cy - my);
    551         this->quadTo(cx + sx,       T, cx     ,       T);
    552         this->quadTo(cx - sx,       T, cx - mx, cy - my);
    553         this->quadTo(      L, cy - sy,       L, cy     );
    554         this->quadTo(      L, cy + sy, cx - mx, cy + my);
    555         this->quadTo(cx - sx,       B, cx     ,       B);
    556         this->quadTo(cx + sx,       B, cx + mx, cy + my);
    557         this->quadTo(      R, cy + sy,       R, cy     );
    558     } else {
    559         this->quadTo(      R, cy + sy, cx + mx, cy + my);
    560         this->quadTo(cx + sx,       B, cx     ,       B);
    561         this->quadTo(cx - sx,       B, cx - mx, cy + my);
    562         this->quadTo(      L, cy + sy,       L, cy     );
    563         this->quadTo(      L, cy - sy, cx - mx, cy - my);
    564         this->quadTo(cx - sx,       T, cx     ,       T);
    565         this->quadTo(cx + sx,       T, cx + mx, cy - my);
    566         this->quadTo(      R, cy - sy,       R, cy     );
    567     }
    568 #endif
    569     this->close();
    570 }
    571 
    572 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
    573     if (r > 0) {
    574         SkRect  rect;
    575         rect.set(x - r, y - r, x + r, y + r);
    576         this->addOval(rect, dir);
    577     }
    578 }
    579 
    580 #include "SkGeometry.h"
    581 
    582 static int build_arc_points(const SkRect& oval, SkScalar startAngle,
    583                             SkScalar sweepAngle,
    584                             SkPoint pts[kSkBuildQuadArcStorage]) {
    585     SkVector start, stop;
    586 
    587     start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
    588     stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
    589                              &stop.fX);
    590 
    591     /*  If the sweep angle is nearly (but less than) 360, then due to precision
    592         loss in radians-conversion and/or sin/cos, we may end up with coincident
    593         vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
    594         of drawing a nearly complete circle (good).
    595              e.g. canvas.drawArc(0, 359.99, ...)
    596              -vs- canvas.drawArc(0, 359.9, ...)
    597         We try to detect this edge case, and tweak the stop vector
    598      */
    599     if (start == stop) {
    600         SkScalar sw = SkScalarAbs(sweepAngle);
    601         if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
    602             SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
    603             // make a guess at a tiny angle (in radians) to tweak by
    604             SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
    605             // not sure how much will be enough, so we use a loop
    606             do {
    607                 stopRad -= deltaRad;
    608                 stop.fY = SkScalarSinCos(stopRad, &stop.fX);
    609             } while (start == stop);
    610         }
    611     }
    612 
    613     SkMatrix    matrix;
    614 
    615     matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
    616     matrix.postTranslate(oval.centerX(), oval.centerY());
    617 
    618     return SkBuildQuadArc(start, stop,
    619           sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
    620                           &matrix, pts);
    621 }
    622 
    623 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
    624                    bool forceMoveTo) {
    625     if (oval.width() < 0 || oval.height() < 0) {
    626         return;
    627     }
    628 
    629     SkPoint pts[kSkBuildQuadArcStorage];
    630     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
    631     SkASSERT((count & 1) == 1);
    632 
    633     if (fVerbs.count() == 0) {
    634         forceMoveTo = true;
    635     }
    636     this->incReserve(count);
    637     forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
    638     for (int i = 1; i < count; i += 2) {
    639         this->quadTo(pts[i], pts[i+1]);
    640     }
    641 }
    642 
    643 void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
    644                     SkScalar sweepAngle) {
    645     if (oval.isEmpty() || 0 == sweepAngle) {
    646         return;
    647     }
    648 
    649     const SkScalar kFullCircleAngle = SkIntToScalar(360);
    650 
    651     if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
    652         this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
    653         return;
    654     }
    655 
    656     SkPoint pts[kSkBuildQuadArcStorage];
    657     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
    658 
    659     this->incReserve(count);
    660     this->moveTo(pts[0]);
    661     for (int i = 1; i < count; i += 2) {
    662         this->quadTo(pts[i], pts[i+1]);
    663     }
    664 }
    665 
    666 /*
    667     Need to handle the case when the angle is sharp, and our computed end-points
    668     for the arc go behind pt1 and/or p2...
    669 */
    670 void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    671                    SkScalar radius) {
    672     SkVector    before, after;
    673 
    674     // need to know our prev pt so we can construct tangent vectors
    675     {
    676         SkPoint start;
    677         this->getLastPt(&start);
    678         before.setNormalize(x1 - start.fX, y1 - start.fY);
    679         after.setNormalize(x2 - x1, y2 - y1);
    680     }
    681 
    682     SkScalar cosh = SkPoint::DotProduct(before, after);
    683     SkScalar sinh = SkPoint::CrossProduct(before, after);
    684 
    685     if (SkScalarNearlyZero(sinh)) {   // angle is too tight
    686         return;
    687     }
    688 
    689     SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
    690     if (dist < 0) {
    691         dist = -dist;
    692     }
    693 
    694     SkScalar xx = x1 - SkScalarMul(dist, before.fX);
    695     SkScalar yy = y1 - SkScalarMul(dist, before.fY);
    696     SkRotationDirection arcDir;
    697 
    698     // now turn before/after into normals
    699     if (sinh > 0) {
    700         before.rotateCCW();
    701         after.rotateCCW();
    702         arcDir = kCW_SkRotationDirection;
    703     } else {
    704         before.rotateCW();
    705         after.rotateCW();
    706         arcDir = kCCW_SkRotationDirection;
    707     }
    708 
    709     SkMatrix    matrix;
    710     SkPoint     pts[kSkBuildQuadArcStorage];
    711 
    712     matrix.setScale(radius, radius);
    713     matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
    714                          yy - SkScalarMul(radius, before.fY));
    715 
    716     int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
    717 
    718     this->incReserve(count);
    719     // [xx,yy] == pts[0]
    720     this->lineTo(xx, yy);
    721     for (int i = 1; i < count; i += 2) {
    722         this->quadTo(pts[i], pts[i+1]);
    723     }
    724 }
    725 
    726 ///////////////////////////////////////////////////////////////////////////////
    727 
    728 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
    729     SkMatrix matrix;
    730 
    731     matrix.setTranslate(dx, dy);
    732     this->addPath(path, matrix);
    733 }
    734 
    735 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
    736     this->incReserve(path.fPts.count());
    737 
    738     Iter    iter(path, false);
    739     SkPoint pts[4];
    740     Verb    verb;
    741 
    742     SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
    743 
    744     while ((verb = iter.next(pts)) != kDone_Verb) {
    745         switch (verb) {
    746             case kMove_Verb:
    747                 proc(matrix, &pts[0], &pts[0], 1);
    748                 this->moveTo(pts[0]);
    749                 break;
    750             case kLine_Verb:
    751                 proc(matrix, &pts[1], &pts[1], 1);
    752                 this->lineTo(pts[1]);
    753                 break;
    754             case kQuad_Verb:
    755                 proc(matrix, &pts[1], &pts[1], 2);
    756                 this->quadTo(pts[1], pts[2]);
    757                 break;
    758             case kCubic_Verb:
    759                 proc(matrix, &pts[1], &pts[1], 3);
    760                 this->cubicTo(pts[1], pts[2], pts[3]);
    761                 break;
    762             case kClose_Verb:
    763                 this->close();
    764                 break;
    765             default:
    766                 SkASSERT(!"unknown verb");
    767         }
    768     }
    769 }
    770 
    771 ///////////////////////////////////////////////////////////////////////////////
    772 
    773 static const uint8_t gPtsInVerb[] = {
    774     1,  // kMove
    775     1,  // kLine
    776     2,  // kQuad
    777     3,  // kCubic
    778     0,  // kClose
    779     0   // kDone
    780 };
    781 
    782 // ignore the initial moveto, and stop when the 1st contour ends
    783 void SkPath::pathTo(const SkPath& path) {
    784     int i, vcount = path.fVerbs.count();
    785     if (vcount == 0) {
    786         return;
    787     }
    788 
    789     this->incReserve(vcount);
    790 
    791     const uint8_t*  verbs = path.fVerbs.begin();
    792     const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
    793 
    794     SkASSERT(verbs[0] == kMove_Verb);
    795     for (i = 1; i < vcount; i++) {
    796         switch (verbs[i]) {
    797             case kLine_Verb:
    798                 this->lineTo(pts[0].fX, pts[0].fY);
    799                 break;
    800             case kQuad_Verb:
    801                 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
    802                 break;
    803             case kCubic_Verb:
    804                 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
    805                               pts[2].fX, pts[2].fY);
    806                 break;
    807             case kClose_Verb:
    808                 return;
    809         }
    810         pts += gPtsInVerb[verbs[i]];
    811     }
    812 }
    813 
    814 // ignore the last point of the 1st contour
    815 void SkPath::reversePathTo(const SkPath& path) {
    816     int i, vcount = path.fVerbs.count();
    817     if (vcount == 0) {
    818         return;
    819     }
    820 
    821     this->incReserve(vcount);
    822 
    823     const uint8_t*  verbs = path.fVerbs.begin();
    824     const SkPoint*  pts = path.fPts.begin();
    825 
    826     SkASSERT(verbs[0] == kMove_Verb);
    827     for (i = 1; i < vcount; i++) {
    828         int n = gPtsInVerb[verbs[i]];
    829         if (n == 0) {
    830             break;
    831         }
    832         pts += n;
    833     }
    834 
    835     while (--i > 0) {
    836         switch (verbs[i]) {
    837             case kLine_Verb:
    838                 this->lineTo(pts[-1].fX, pts[-1].fY);
    839                 break;
    840             case kQuad_Verb:
    841                 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
    842                 break;
    843             case kCubic_Verb:
    844                 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
    845                               pts[-3].fX, pts[-3].fY);
    846                 break;
    847             default:
    848                 SkASSERT(!"bad verb");
    849                 break;
    850         }
    851         pts -= gPtsInVerb[verbs[i]];
    852     }
    853 }
    854 
    855 ///////////////////////////////////////////////////////////////////////////////
    856 
    857 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
    858     SkMatrix    matrix;
    859 
    860     matrix.setTranslate(dx, dy);
    861     this->transform(matrix, dst);
    862 }
    863 
    864 #include "SkGeometry.h"
    865 
    866 static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
    867                               int level = 2) {
    868     if (--level >= 0) {
    869         SkPoint tmp[5];
    870 
    871         SkChopQuadAtHalf(pts, tmp);
    872         subdivide_quad_to(path, &tmp[0], level);
    873         subdivide_quad_to(path, &tmp[2], level);
    874     } else {
    875         path->quadTo(pts[1], pts[2]);
    876     }
    877 }
    878 
    879 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
    880                                int level = 2) {
    881     if (--level >= 0) {
    882         SkPoint tmp[7];
    883 
    884         SkChopCubicAtHalf(pts, tmp);
    885         subdivide_cubic_to(path, &tmp[0], level);
    886         subdivide_cubic_to(path, &tmp[3], level);
    887     } else {
    888         path->cubicTo(pts[1], pts[2], pts[3]);
    889     }
    890 }
    891 
    892 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
    893     SkDEBUGCODE(this->validate();)
    894     if (dst == NULL) {
    895         dst = (SkPath*)this;
    896     }
    897 
    898     if (matrix.getType() & SkMatrix::kPerspective_Mask) {
    899         SkPath  tmp;
    900         tmp.fFillType = fFillType;
    901 
    902         SkPath::Iter    iter(*this, false);
    903         SkPoint         pts[4];
    904         SkPath::Verb    verb;
    905 
    906         while ((verb = iter.next(pts)) != kDone_Verb) {
    907             switch (verb) {
    908                 case kMove_Verb:
    909                     tmp.moveTo(pts[0]);
    910                     break;
    911                 case kLine_Verb:
    912                     tmp.lineTo(pts[1]);
    913                     break;
    914                 case kQuad_Verb:
    915                     subdivide_quad_to(&tmp, pts);
    916                     break;
    917                 case kCubic_Verb:
    918                     subdivide_cubic_to(&tmp, pts);
    919                     break;
    920                 case kClose_Verb:
    921                     tmp.close();
    922                     break;
    923                 default:
    924                     SkASSERT(!"unknown verb");
    925                     break;
    926             }
    927         }
    928 
    929         dst->swap(tmp);
    930         matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
    931     } else {
    932         // remember that dst might == this, so be sure to check
    933         // fBoundsIsDirty before we set it
    934         if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
    935             // if we're empty, fastbounds should not be mapped
    936             matrix.mapRect(&dst->fBounds, fBounds);
    937             dst->fBoundsIsDirty = false;
    938         } else {
    939             dst->fBoundsIsDirty = true;
    940         }
    941 
    942         if (this != dst) {
    943             dst->fVerbs = fVerbs;
    944             dst->fPts.setCount(fPts.count());
    945             dst->fFillType = fFillType;
    946         }
    947         matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
    948         SkDEBUGCODE(dst->validate();)
    949     }
    950 }
    951 
    952 ///////////////////////////////////////////////////////////////////////////////
    953 ///////////////////////////////////////////////////////////////////////////////
    954 
    955 enum NeedMoveToState {
    956     kAfterClose_NeedMoveToState,
    957     kAfterCons_NeedMoveToState,
    958     kAfterPrefix_NeedMoveToState
    959 };
    960 
    961 SkPath::Iter::Iter() {
    962 #ifdef SK_DEBUG
    963     fPts = NULL;
    964     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
    965     fForceClose = fNeedMoveTo = fCloseLine = false;
    966 #endif
    967     // need to init enough to make next() harmlessly return kDone_Verb
    968     fVerbs = NULL;
    969     fVerbStop = NULL;
    970     fNeedClose = false;
    971 }
    972 
    973 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
    974     this->setPath(path, forceClose);
    975 }
    976 
    977 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
    978     fPts = path.fPts.begin();
    979     fVerbs = path.fVerbs.begin();
    980     fVerbStop = path.fVerbs.end();
    981     fForceClose = SkToU8(forceClose);
    982     fNeedClose = false;
    983     fNeedMoveTo = kAfterPrefix_NeedMoveToState;
    984 }
    985 
    986 bool SkPath::Iter::isClosedContour() const {
    987     if (fVerbs == NULL || fVerbs == fVerbStop) {
    988         return false;
    989     }
    990     if (fForceClose) {
    991         return true;
    992     }
    993 
    994     const uint8_t* verbs = fVerbs;
    995     const uint8_t* stop = fVerbStop;
    996 
    997     if (kMove_Verb == *verbs) {
    998         verbs += 1; // skip the initial moveto
    999     }
   1000 
   1001     while (verbs < stop) {
   1002         unsigned v = *verbs++;
   1003         if (kMove_Verb == v) {
   1004             break;
   1005         }
   1006         if (kClose_Verb == v) {
   1007             return true;
   1008         }
   1009     }
   1010     return false;
   1011 }
   1012 
   1013 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
   1014     if (fLastPt != fMoveTo) {
   1015         // A special case: if both points are NaN, SkPoint::operation== returns
   1016         // false, but the iterator expects that they are treated as the same.
   1017         // (consider SkPoint is a 2-dimension float point).
   1018         if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
   1019             SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
   1020             return kClose_Verb;
   1021         }
   1022 
   1023         if (pts) {
   1024             pts[0] = fLastPt;
   1025             pts[1] = fMoveTo;
   1026         }
   1027         fLastPt = fMoveTo;
   1028         fCloseLine = true;
   1029         return kLine_Verb;
   1030     }
   1031     return kClose_Verb;
   1032 }
   1033 
   1034 bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
   1035     if (fNeedMoveTo == kAfterClose_NeedMoveToState) {
   1036         if (pts) {
   1037             *pts = fMoveTo;
   1038         }
   1039         fNeedClose = fForceClose;
   1040         fNeedMoveTo = kAfterCons_NeedMoveToState;
   1041         fVerbs -= 1;
   1042         return true;
   1043     }
   1044 
   1045     if (fNeedMoveTo == kAfterCons_NeedMoveToState) {
   1046         if (pts) {
   1047             *pts = fMoveTo;
   1048         }
   1049         fNeedMoveTo = kAfterPrefix_NeedMoveToState;
   1050     } else {
   1051         SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState);
   1052         if (pts) {
   1053             *pts = fPts[-1];
   1054         }
   1055     }
   1056     return false;
   1057 }
   1058 
   1059 SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
   1060     if (fVerbs == fVerbStop) {
   1061         if (fNeedClose) {
   1062             if (kLine_Verb == this->autoClose(pts)) {
   1063                 return kLine_Verb;
   1064             }
   1065             fNeedClose = false;
   1066             return kClose_Verb;
   1067         }
   1068         return kDone_Verb;
   1069     }
   1070 
   1071     unsigned        verb = *fVerbs++;
   1072     const SkPoint*  srcPts = fPts;
   1073 
   1074     switch (verb) {
   1075         case kMove_Verb:
   1076             if (fNeedClose) {
   1077                 fVerbs -= 1;
   1078                 verb = this->autoClose(pts);
   1079                 if (verb == kClose_Verb) {
   1080                     fNeedClose = false;
   1081                 }
   1082                 return (Verb)verb;
   1083             }
   1084             if (fVerbs == fVerbStop) {    // might be a trailing moveto
   1085                 return kDone_Verb;
   1086             }
   1087             fMoveTo = *srcPts;
   1088             if (pts) {
   1089                 pts[0] = *srcPts;
   1090             }
   1091             srcPts += 1;
   1092             fNeedMoveTo = kAfterCons_NeedMoveToState;
   1093             fNeedClose = fForceClose;
   1094             break;
   1095         case kLine_Verb:
   1096             if (this->cons_moveTo(pts)) {
   1097                 return kMove_Verb;
   1098             }
   1099             if (pts) {
   1100                 pts[1] = srcPts[0];
   1101             }
   1102             fLastPt = srcPts[0];
   1103             fCloseLine = false;
   1104             srcPts += 1;
   1105             break;
   1106         case kQuad_Verb:
   1107             if (this->cons_moveTo(pts)) {
   1108                 return kMove_Verb;
   1109             }
   1110             if (pts) {
   1111                 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
   1112             }
   1113             fLastPt = srcPts[1];
   1114             srcPts += 2;
   1115             break;
   1116         case kCubic_Verb:
   1117             if (this->cons_moveTo(pts)) {
   1118                 return kMove_Verb;
   1119             }
   1120             if (pts) {
   1121                 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
   1122             }
   1123             fLastPt = srcPts[2];
   1124             srcPts += 3;
   1125             break;
   1126         case kClose_Verb:
   1127             verb = this->autoClose(pts);
   1128             if (verb == kLine_Verb) {
   1129                 fVerbs -= 1;
   1130             } else {
   1131                 fNeedClose = false;
   1132             }
   1133             fNeedMoveTo = kAfterClose_NeedMoveToState;
   1134             break;
   1135     }
   1136     fPts = srcPts;
   1137     return (Verb)verb;
   1138 }
   1139 
   1140 ///////////////////////////////////////////////////////////////////////////////
   1141 
   1142 static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist,
   1143                          int count) {
   1144     SkASSERT(dist > 0);
   1145 
   1146     count *= 2;
   1147     for (int i = 0; i < count; i++) {
   1148         if (SkScalarAbs(p[i] - q[i]) > dist) {
   1149             return true;
   1150         }
   1151     }
   1152     return false;
   1153 }
   1154 
   1155 static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist,
   1156                            int subLevel = 4) {
   1157     if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) {
   1158         SkPoint tmp[5];
   1159         SkChopQuadAtHalf(pts, tmp);
   1160 
   1161         subdivide_quad(dst, &tmp[0], dist, subLevel);
   1162         subdivide_quad(dst, &tmp[2], dist, subLevel);
   1163     } else {
   1164         dst->quadTo(pts[1], pts[2]);
   1165     }
   1166 }
   1167 
   1168 static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist,
   1169                             int subLevel = 4) {
   1170     if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) {
   1171         SkPoint tmp[7];
   1172         SkChopCubicAtHalf(pts, tmp);
   1173 
   1174         subdivide_cubic(dst, &tmp[0], dist, subLevel);
   1175         subdivide_cubic(dst, &tmp[3], dist, subLevel);
   1176     } else {
   1177         dst->cubicTo(pts[1], pts[2], pts[3]);
   1178     }
   1179 }
   1180 
   1181 void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const {
   1182     SkPath  tmpPath;
   1183     if (NULL == dst || this == dst) {
   1184         dst = &tmpPath;
   1185     }
   1186 
   1187     SkPath::Iter    iter(*this, false);
   1188     SkPoint         pts[4];
   1189 
   1190     for (;;) {
   1191         switch (iter.next(pts)) {
   1192             case SkPath::kMove_Verb:
   1193                 dst->moveTo(pts[0]);
   1194                 break;
   1195             case SkPath::kLine_Verb:
   1196                 if (!bendLines) {
   1197                     dst->lineTo(pts[1]);
   1198                     break;
   1199                 }
   1200                 // construct a quad from the line
   1201                 pts[2] = pts[1];
   1202                 pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX),
   1203                            SkScalarAve(pts[0].fY, pts[2].fY));
   1204                 // fall through to the quad case
   1205             case SkPath::kQuad_Verb:
   1206                 subdivide_quad(dst, pts, dist);
   1207                 break;
   1208             case SkPath::kCubic_Verb:
   1209                 subdivide_cubic(dst, pts, dist);
   1210                 break;
   1211             case SkPath::kClose_Verb:
   1212                 dst->close();
   1213                 break;
   1214             case SkPath::kDone_Verb:
   1215                 goto DONE;
   1216         }
   1217     }
   1218 DONE:
   1219     if (&tmpPath == dst) {   // i.e. the dst should be us
   1220         dst->swap(*(SkPath*)this);
   1221     }
   1222 }
   1223 
   1224 ///////////////////////////////////////////////////////////////////////
   1225 /*
   1226     Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
   1227 */
   1228 
   1229 void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const {
   1230     SkDEBUGCODE(this->validate();)
   1231 
   1232     buffer.write32(fPts.count());
   1233     buffer.write32(fVerbs.count());
   1234     buffer.write32(fFillType);
   1235     buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
   1236     buffer.writePad(fVerbs.begin(), fVerbs.count());
   1237 }
   1238 
   1239 void SkPath::unflatten(SkFlattenableReadBuffer& buffer) {
   1240     fPts.setCount(buffer.readS32());
   1241     fVerbs.setCount(buffer.readS32());
   1242     fFillType = buffer.readS32();
   1243     buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
   1244     buffer.read(fVerbs.begin(), fVerbs.count());
   1245 
   1246     fBoundsIsDirty = true;
   1247 
   1248     SkDEBUGCODE(this->validate();)
   1249 }
   1250 
   1251 ///////////////////////////////////////////////////////////////////////////////
   1252 ///////////////////////////////////////////////////////////////////////////////
   1253 
   1254 void SkPath::dump(bool forceClose, const char title[]) const {
   1255     Iter    iter(*this, forceClose);
   1256     SkPoint pts[4];
   1257     Verb    verb;
   1258 
   1259     SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
   1260              title ? title : "");
   1261 
   1262     while ((verb = iter.next(pts)) != kDone_Verb) {
   1263         switch (verb) {
   1264             case kMove_Verb:
   1265 #ifdef SK_CAN_USE_FLOAT
   1266                 SkDebugf("  path: moveTo [%g %g]\n",
   1267                         SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
   1268 #else
   1269                 SkDebugf("  path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
   1270 #endif
   1271                 break;
   1272             case kLine_Verb:
   1273 #ifdef SK_CAN_USE_FLOAT
   1274                 SkDebugf("  path: lineTo [%g %g]\n",
   1275                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
   1276 #else
   1277                 SkDebugf("  path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
   1278 #endif
   1279                 break;
   1280             case kQuad_Verb:
   1281 #ifdef SK_CAN_USE_FLOAT
   1282                 SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
   1283                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
   1284                         SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
   1285 #else
   1286                 SkDebugf("  path: quadTo [%x %x] [%x %x]\n",
   1287                          pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
   1288 #endif
   1289                 break;
   1290             case kCubic_Verb:
   1291 #ifdef SK_CAN_USE_FLOAT
   1292                 SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
   1293                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
   1294                         SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
   1295                         SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
   1296 #else
   1297                 SkDebugf("  path: cubeTo [%x %x] [%x %x] [%x %x]\n",
   1298                          pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
   1299                          pts[3].fX, pts[3].fY);
   1300 #endif
   1301                 break;
   1302             case kClose_Verb:
   1303                 SkDebugf("  path: close\n");
   1304                 break;
   1305             default:
   1306                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
   1307                 verb = kDone_Verb;  // stop the loop
   1308                 break;
   1309         }
   1310     }
   1311     SkDebugf("path: done %s\n", title ? title : "");
   1312 }
   1313 
   1314 void SkPath::dump() const {
   1315     this->dump(false);
   1316 }
   1317 
   1318 #ifdef SK_DEBUG
   1319 void SkPath::validate() const {
   1320     SkASSERT(this != NULL);
   1321     SkASSERT((fFillType & ~3) == 0);
   1322     fPts.validate();
   1323     fVerbs.validate();
   1324 
   1325     if (!fBoundsIsDirty) {
   1326         SkRect bounds;
   1327         compute_pt_bounds(&bounds, fPts);
   1328         if (fPts.count() <= 1) {
   1329             // if we're empty, fBounds may be empty but translated, so we can't
   1330             // necessarily compare to bounds directly
   1331             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
   1332             // be [2, 2, 2, 2]
   1333             SkASSERT(bounds.isEmpty());
   1334             SkASSERT(fBounds.isEmpty());
   1335         } else {
   1336             fBounds.contains(bounds);
   1337         }
   1338     }
   1339 }
   1340 #endif
   1341 
   1342