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