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