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