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