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