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