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