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