1 :
2 : /*
3 : * Copyright 2006 The Android Open Source Project
4 : *
5 : * Use of this source code is governed by a BSD-style license that can be
6 : * found in the LICENSE file.
7 : */
8 :
9 :
10 : #include "SkPath.h"
11 : #include "SkReader32.h"
12 : #include "SkWriter32.h"
13 : #include "SkMath.h"
14 :
15 : ////////////////////////////////////////////////////////////////////////////
16 :
17 : /**
18 : * Path.bounds is defined to be the bounds of all the control points.
19 : * If we called bounds.join(r) we would skip r if r was empty, which breaks
20 : * our promise. Hence we have a custom joiner that doesn't look at emptiness
21 : */
22 0 : static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
23 0 : dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
24 0 : dst->fTop = SkMinScalar(dst->fTop, src.fTop);
25 0 : dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
26 0 : dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
27 0 : }
28 :
29 : /* This guy's constructor/destructor bracket a path editing operation. It is
30 : used when we know the bounds of the amount we are going to add to the path
31 : (usually a new contour, but not required).
32 :
33 : It captures some state about the path up front (i.e. if it already has a
34 : cached bounds), and the if it can, it updates the cache bounds explicitly,
35 : avoiding the need to revisit all of the points in getBounds().
36 :
37 : It also notes if the path was originally empty, and if so, sets isConvex
38 : to true. Thus it can only be used if the contour being added is convex.
39 : */
40 : class SkAutoPathBoundsUpdate {
41 : public:
42 0 : SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
43 0 : this->init(path);
44 0 : }
45 :
46 0 : SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
47 : SkScalar right, SkScalar bottom) {
48 0 : fRect.set(left, top, right, bottom);
49 0 : this->init(path);
50 0 : }
51 :
52 0 : ~SkAutoPathBoundsUpdate() {
53 0 : fPath->setIsConvex(fEmpty);
54 0 : if (fEmpty) {
55 0 : fPath->fBounds = fRect;
56 0 : fPath->fBoundsIsDirty = false;
57 0 : } else if (!fDirty) {
58 0 : joinNoEmptyChecks(&fPath->fBounds, fRect);
59 0 : fPath->fBoundsIsDirty = false;
60 : }
61 0 : }
62 :
63 : private:
64 : SkPath* fPath;
65 : SkRect fRect;
66 : bool fDirty;
67 : bool fEmpty;
68 :
69 : // returns true if we should proceed
70 0 : void init(SkPath* path) {
71 0 : fPath = path;
72 0 : fDirty = SkToBool(path->fBoundsIsDirty);
73 0 : fEmpty = path->isEmpty();
74 : // Cannot use fRect for our bounds unless we know it is sorted
75 0 : fRect.sort();
76 0 : }
77 : };
78 :
79 0 : static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
80 0 : if (pts.count() <= 1) { // we ignore just 1 point (moveto)
81 0 : bounds->set(0, 0, 0, 0);
82 : } else {
83 0 : bounds->set(pts.begin(), pts.count());
84 : // SkDebugf("------- compute bounds %p %d", &pts, pts.count());
85 : }
86 0 : }
87 :
88 : ////////////////////////////////////////////////////////////////////////////
89 :
90 : /*
91 : Stores the verbs and points as they are given to us, with exceptions:
92 : - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
93 : - we insert a Move(0,0) if Line | Quad | Cubic is our first command
94 :
95 : The iterator does more cleanup, especially if forceClose == true
96 : 1. If we encounter degenerate segments, remove them
97 : 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
98 : 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
99 : 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
100 : */
101 :
102 : ////////////////////////////////////////////////////////////////////////////
103 :
104 0 : SkPath::SkPath()
105 : : fFillType(kWinding_FillType)
106 0 : , fBoundsIsDirty(true) {
107 0 : fConvexity = kUnknown_Convexity;
108 0 : fSegmentMask = 0;
109 : #ifdef SK_BUILD_FOR_ANDROID
110 : fGenerationID = 0;
111 : #endif
112 0 : }
113 :
114 0 : SkPath::SkPath(const SkPath& src) {
115 0 : SkDEBUGCODE(src.validate();)
116 0 : *this = src;
117 : #ifdef SK_BUILD_FOR_ANDROID
118 : // the assignment operator above increments the ID so correct for that here
119 : fGenerationID--;
120 : #endif
121 0 : }
122 :
123 0 : SkPath::~SkPath() {
124 0 : SkDEBUGCODE(this->validate();)
125 0 : }
126 :
127 0 : SkPath& SkPath::operator=(const SkPath& src) {
128 0 : SkDEBUGCODE(src.validate();)
129 :
130 0 : if (this != &src) {
131 0 : fBounds = src.fBounds;
132 0 : fPts = src.fPts;
133 0 : fVerbs = src.fVerbs;
134 0 : fFillType = src.fFillType;
135 0 : fBoundsIsDirty = src.fBoundsIsDirty;
136 0 : fConvexity = src.fConvexity;
137 0 : fSegmentMask = src.fSegmentMask;
138 : GEN_ID_INC;
139 : }
140 0 : SkDEBUGCODE(this->validate();)
141 0 : return *this;
142 : }
143 :
144 0 : bool operator==(const SkPath& a, const SkPath& b) {
145 : // note: don't need to look at isConvex or bounds, since just comparing the
146 : // raw data is sufficient.
147 :
148 : // We explicitly check fSegmentMask as a quick-reject. We could skip it,
149 : // since it is only a cache of info in the fVerbs, but its a fast way to
150 : // notice a difference
151 :
152 : return &a == &b ||
153 : (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
154 0 : a.fVerbs == b.fVerbs && a.fPts == b.fPts);
155 : }
156 :
157 0 : void SkPath::swap(SkPath& other) {
158 0 : SkASSERT(&other != NULL);
159 :
160 0 : if (this != &other) {
161 0 : SkTSwap<SkRect>(fBounds, other.fBounds);
162 0 : fPts.swap(other.fPts);
163 0 : fVerbs.swap(other.fVerbs);
164 0 : SkTSwap<uint8_t>(fFillType, other.fFillType);
165 0 : SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
166 0 : SkTSwap<uint8_t>(fConvexity, other.fConvexity);
167 0 : SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
168 : GEN_ID_INC;
169 : }
170 0 : }
171 :
172 : #ifdef SK_BUILD_FOR_ANDROID
173 : uint32_t SkPath::getGenerationID() const {
174 : return fGenerationID;
175 : }
176 : #endif
177 :
178 0 : void SkPath::reset() {
179 0 : SkDEBUGCODE(this->validate();)
180 :
181 0 : fPts.reset();
182 0 : fVerbs.reset();
183 : GEN_ID_INC;
184 0 : fBoundsIsDirty = true;
185 0 : fConvexity = kUnknown_Convexity;
186 0 : fSegmentMask = 0;
187 0 : }
188 :
189 0 : void SkPath::rewind() {
190 0 : SkDEBUGCODE(this->validate();)
191 :
192 0 : fPts.rewind();
193 0 : fVerbs.rewind();
194 : GEN_ID_INC;
195 0 : fConvexity = kUnknown_Convexity;
196 0 : fBoundsIsDirty = true;
197 0 : fSegmentMask = 0;
198 0 : }
199 :
200 0 : bool SkPath::isEmpty() const {
201 0 : SkDEBUGCODE(this->validate();)
202 : #if SK_OLD_EMPTY_PATH_BEHAVIOR
203 : int count = fVerbs.count();
204 : return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
205 : #else
206 0 : return 0 == fVerbs.count();
207 : #endif
208 : }
209 :
210 : /*
211 : Determines if path is a rect by keeping track of changes in direction
212 : and looking for a loop either clockwise or counterclockwise.
213 :
214 : The direction is computed such that:
215 : 0: vertical up
216 : 1: horizontal right
217 : 2: vertical down
218 : 3: horizontal left
219 :
220 : A rectangle cycles up/right/down/left or up/left/down/right.
221 :
222 : The test fails if:
223 : The path is closed, and followed by a line.
224 : A second move creates a new endpoint.
225 : A diagonal line is parsed.
226 : There's more than four changes of direction.
227 : There's a discontinuity on the line (e.g., a move in the middle)
228 : The line reverses direction.
229 : The rectangle doesn't complete a cycle.
230 : The path contains a quadratic or cubic.
231 : The path contains fewer than four points.
232 : The final point isn't equal to the first point.
233 :
234 : It's OK if the path has:
235 : Several colinear line segments composing a rectangle side.
236 : Single points on the rectangle side.
237 :
238 : The direction takes advantage of the corners found since opposite sides
239 : must travel in opposite directions.
240 :
241 : FIXME: Allow colinear quads and cubics to be treated like lines.
242 : FIXME: If the API passes fill-only, return true if the filled stroke
243 : is a rectangle, though the caller failed to close the path.
244 : */
245 0 : bool SkPath::isRect(SkRect* rect) const {
246 0 : SkDEBUGCODE(this->validate();)
247 :
248 0 : int corners = 0;
249 : SkPoint first, last;
250 0 : first.set(0, 0);
251 0 : last.set(0, 0);
252 0 : int firstDirection = 0;
253 0 : int lastDirection = 0;
254 0 : int nextDirection = 0;
255 0 : bool closedOrMoved = false;
256 0 : bool autoClose = false;
257 0 : const uint8_t* verbs = fVerbs.begin();
258 0 : const uint8_t* verbStop = fVerbs.end();
259 0 : const SkPoint* pts = fPts.begin();
260 0 : while (verbs != verbStop) {
261 0 : switch (*verbs++) {
262 : case kClose_Verb:
263 0 : pts = fPts.begin();
264 0 : autoClose = true;
265 : case kLine_Verb: {
266 0 : SkScalar left = last.fX;
267 0 : SkScalar top = last.fY;
268 0 : SkScalar right = pts->fX;
269 0 : SkScalar bottom = pts->fY;
270 0 : ++pts;
271 0 : if (left != right && top != bottom) {
272 0 : return false; // diagonal
273 : }
274 0 : if (left == right && top == bottom) {
275 0 : break; // single point on side OK
276 : }
277 : nextDirection = (left != right) << 0 |
278 0 : (left < right || top < bottom) << 1;
279 0 : if (0 == corners) {
280 0 : firstDirection = nextDirection;
281 0 : first = last;
282 0 : last = pts[-1];
283 0 : corners = 1;
284 0 : closedOrMoved = false;
285 0 : break;
286 : }
287 0 : if (closedOrMoved) {
288 0 : return false; // closed followed by a line
289 : }
290 0 : closedOrMoved = autoClose;
291 0 : if (lastDirection != nextDirection) {
292 0 : if (++corners > 4) {
293 0 : return false; // too many direction changes
294 : }
295 : }
296 0 : last = pts[-1];
297 0 : if (lastDirection == nextDirection) {
298 0 : break; // colinear segment
299 : }
300 : // Possible values for corners are 2, 3, and 4.
301 : // When corners == 3, nextDirection opposes firstDirection.
302 : // Otherwise, nextDirection at corner 2 opposes corner 4.
303 0 : int turn = firstDirection ^ (corners - 1);
304 0 : int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
305 0 : if ((directionCycle ^ turn) != nextDirection) {
306 0 : return false; // direction didn't follow cycle
307 : }
308 0 : break;
309 : }
310 : case kQuad_Verb:
311 : case kCubic_Verb:
312 0 : return false; // quadratic, cubic not allowed
313 : case kMove_Verb:
314 0 : last = *pts++;
315 0 : closedOrMoved = true;
316 0 : break;
317 : }
318 0 : lastDirection = nextDirection;
319 : }
320 : // Success if 4 corners and first point equals last
321 0 : bool result = 4 == corners && first == last;
322 0 : if (result && rect) {
323 0 : *rect = getBounds();
324 : }
325 0 : return result;
326 : }
327 :
328 0 : int SkPath::getPoints(SkPoint copy[], int max) const {
329 0 : SkDEBUGCODE(this->validate();)
330 :
331 0 : SkASSERT(max >= 0);
332 0 : int count = fPts.count();
333 0 : if (copy && max > 0 && count > 0) {
334 0 : memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
335 : }
336 0 : return count;
337 : }
338 :
339 0 : SkPoint SkPath::getPoint(int index) const {
340 0 : if ((unsigned)index < (unsigned)fPts.count()) {
341 0 : return fPts[index];
342 : }
343 0 : return SkPoint::Make(0, 0);
344 : }
345 :
346 0 : bool SkPath::getLastPt(SkPoint* lastPt) const {
347 0 : SkDEBUGCODE(this->validate();)
348 :
349 0 : int count = fPts.count();
350 0 : if (count > 0) {
351 0 : if (lastPt) {
352 0 : *lastPt = fPts[count - 1];
353 : }
354 0 : return true;
355 : }
356 0 : if (lastPt) {
357 0 : lastPt->set(0, 0);
358 : }
359 0 : return false;
360 : }
361 :
362 0 : void SkPath::setLastPt(SkScalar x, SkScalar y) {
363 0 : SkDEBUGCODE(this->validate();)
364 :
365 0 : int count = fPts.count();
366 0 : if (count == 0) {
367 0 : this->moveTo(x, y);
368 : } else {
369 0 : fPts[count - 1].set(x, y);
370 : GEN_ID_INC;
371 : }
372 0 : }
373 :
374 0 : void SkPath::computeBounds() const {
375 0 : SkDEBUGCODE(this->validate();)
376 0 : SkASSERT(fBoundsIsDirty);
377 :
378 0 : fBoundsIsDirty = false;
379 0 : compute_pt_bounds(&fBounds, fPts);
380 0 : }
381 :
382 0 : void SkPath::setConvexity(Convexity c) {
383 0 : if (fConvexity != c) {
384 0 : fConvexity = c;
385 : GEN_ID_INC;
386 : }
387 0 : }
388 :
389 : //////////////////////////////////////////////////////////////////////////////
390 : // Construction methods
391 :
392 : #define DIRTY_AFTER_EDIT \
393 : do { \
394 : fBoundsIsDirty = true; \
395 : fConvexity = kUnknown_Convexity; \
396 : } while (0)
397 :
398 0 : void SkPath::incReserve(U16CPU inc) {
399 0 : SkDEBUGCODE(this->validate();)
400 :
401 0 : fVerbs.setReserve(fVerbs.count() + inc);
402 0 : fPts.setReserve(fPts.count() + inc);
403 :
404 0 : SkDEBUGCODE(this->validate();)
405 0 : }
406 :
407 0 : void SkPath::moveTo(SkScalar x, SkScalar y) {
408 0 : SkDEBUGCODE(this->validate();)
409 :
410 0 : int vc = fVerbs.count();
411 : SkPoint* pt;
412 :
413 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
414 : if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
415 : pt = &fPts[fPts.count() - 1];
416 : } else {
417 : pt = fPts.append();
418 : *fVerbs.append() = kMove_Verb;
419 : }
420 : #else
421 0 : pt = fPts.append();
422 0 : *fVerbs.append() = kMove_Verb;
423 : #endif
424 0 : pt->set(x, y);
425 :
426 : GEN_ID_INC;
427 0 : DIRTY_AFTER_EDIT;
428 0 : }
429 :
430 0 : void SkPath::rMoveTo(SkScalar x, SkScalar y) {
431 : SkPoint pt;
432 0 : this->getLastPt(&pt);
433 0 : this->moveTo(pt.fX + x, pt.fY + y);
434 0 : }
435 :
436 0 : void SkPath::lineTo(SkScalar x, SkScalar y) {
437 0 : SkDEBUGCODE(this->validate();)
438 :
439 0 : if (fVerbs.count() == 0) {
440 0 : fPts.append()->set(0, 0);
441 0 : *fVerbs.append() = kMove_Verb;
442 : }
443 0 : fPts.append()->set(x, y);
444 0 : *fVerbs.append() = kLine_Verb;
445 0 : fSegmentMask |= kLine_SegmentMask;
446 :
447 : GEN_ID_INC;
448 0 : DIRTY_AFTER_EDIT;
449 0 : }
450 :
451 0 : void SkPath::rLineTo(SkScalar x, SkScalar y) {
452 : SkPoint pt;
453 0 : this->getLastPt(&pt);
454 0 : this->lineTo(pt.fX + x, pt.fY + y);
455 0 : }
456 :
457 0 : void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
458 0 : SkDEBUGCODE(this->validate();)
459 :
460 0 : if (fVerbs.count() == 0) {
461 0 : fPts.append()->set(0, 0);
462 0 : *fVerbs.append() = kMove_Verb;
463 : }
464 :
465 0 : SkPoint* pts = fPts.append(2);
466 0 : pts[0].set(x1, y1);
467 0 : pts[1].set(x2, y2);
468 0 : *fVerbs.append() = kQuad_Verb;
469 0 : fSegmentMask |= kQuad_SegmentMask;
470 :
471 : GEN_ID_INC;
472 0 : DIRTY_AFTER_EDIT;
473 0 : }
474 :
475 0 : void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
476 : SkPoint pt;
477 0 : this->getLastPt(&pt);
478 0 : this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
479 0 : }
480 :
481 0 : void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
482 : SkScalar x3, SkScalar y3) {
483 0 : SkDEBUGCODE(this->validate();)
484 :
485 0 : if (fVerbs.count() == 0) {
486 0 : fPts.append()->set(0, 0);
487 0 : *fVerbs.append() = kMove_Verb;
488 : }
489 0 : SkPoint* pts = fPts.append(3);
490 0 : pts[0].set(x1, y1);
491 0 : pts[1].set(x2, y2);
492 0 : pts[2].set(x3, y3);
493 0 : *fVerbs.append() = kCubic_Verb;
494 0 : fSegmentMask |= kCubic_SegmentMask;
495 :
496 : GEN_ID_INC;
497 0 : DIRTY_AFTER_EDIT;
498 0 : }
499 :
500 0 : void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
501 : SkScalar x3, SkScalar y3) {
502 : SkPoint pt;
503 0 : this->getLastPt(&pt);
504 : this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
505 0 : pt.fX + x3, pt.fY + y3);
506 0 : }
507 :
508 0 : void SkPath::close() {
509 0 : SkDEBUGCODE(this->validate();)
510 :
511 0 : int count = fVerbs.count();
512 0 : if (count > 0) {
513 0 : switch (fVerbs[count - 1]) {
514 : case kLine_Verb:
515 : case kQuad_Verb:
516 : case kCubic_Verb:
517 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
518 : case kMove_Verb:
519 : #endif
520 0 : *fVerbs.append() = kClose_Verb;
521 : GEN_ID_INC;
522 0 : break;
523 : default:
524 : // don't add a close if it's the first verb or a repeat
525 0 : break;
526 : }
527 : }
528 0 : }
529 :
530 : ///////////////////////////////////////////////////////////////////////////////
531 :
532 0 : void SkPath::addRect(const SkRect& rect, Direction dir) {
533 0 : this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
534 0 : }
535 :
536 0 : void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
537 : SkScalar bottom, Direction dir) {
538 0 : SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
539 :
540 0 : this->incReserve(5);
541 :
542 0 : this->moveTo(left, top);
543 0 : if (dir == kCCW_Direction) {
544 0 : this->lineTo(left, bottom);
545 0 : this->lineTo(right, bottom);
546 0 : this->lineTo(right, top);
547 : } else {
548 0 : this->lineTo(right, top);
549 0 : this->lineTo(right, bottom);
550 0 : this->lineTo(left, bottom);
551 : }
552 0 : this->close();
553 0 : }
554 :
555 : #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
556 :
557 0 : void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
558 : Direction dir) {
559 0 : SkScalar w = rect.width();
560 0 : SkScalar halfW = SkScalarHalf(w);
561 0 : SkScalar h = rect.height();
562 0 : SkScalar halfH = SkScalarHalf(h);
563 :
564 0 : if (halfW <= 0 || halfH <= 0) {
565 0 : return;
566 : }
567 :
568 0 : bool skip_hori = rx >= halfW;
569 0 : bool skip_vert = ry >= halfH;
570 :
571 0 : if (skip_hori && skip_vert) {
572 0 : this->addOval(rect, dir);
573 0 : return;
574 : }
575 :
576 0 : SkAutoPathBoundsUpdate apbu(this, rect);
577 :
578 0 : if (skip_hori) {
579 0 : rx = halfW;
580 0 : } else if (skip_vert) {
581 0 : ry = halfH;
582 : }
583 :
584 0 : SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
585 0 : SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
586 :
587 0 : this->incReserve(17);
588 0 : this->moveTo(rect.fRight - rx, rect.fTop);
589 0 : if (dir == kCCW_Direction) {
590 0 : if (!skip_hori) {
591 0 : this->lineTo(rect.fLeft + rx, rect.fTop); // top
592 : }
593 : this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
594 : rect.fLeft, rect.fTop + ry - sy,
595 0 : rect.fLeft, rect.fTop + ry); // top-left
596 0 : if (!skip_vert) {
597 0 : this->lineTo(rect.fLeft, rect.fBottom - ry); // left
598 : }
599 : this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
600 : rect.fLeft + rx - sx, rect.fBottom,
601 0 : rect.fLeft + rx, rect.fBottom); // bot-left
602 0 : if (!skip_hori) {
603 0 : this->lineTo(rect.fRight - rx, rect.fBottom); // bottom
604 : }
605 : this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
606 : rect.fRight, rect.fBottom - ry + sy,
607 0 : rect.fRight, rect.fBottom - ry); // bot-right
608 0 : if (!skip_vert) {
609 0 : this->lineTo(rect.fRight, rect.fTop + ry);
610 : }
611 : this->cubicTo(rect.fRight, rect.fTop + ry - sy,
612 : rect.fRight - rx + sx, rect.fTop,
613 0 : rect.fRight - rx, rect.fTop); // top-right
614 : } else {
615 : this->cubicTo(rect.fRight - rx + sx, rect.fTop,
616 : rect.fRight, rect.fTop + ry - sy,
617 0 : rect.fRight, rect.fTop + ry); // top-right
618 0 : if (!skip_vert) {
619 0 : this->lineTo(rect.fRight, rect.fBottom - ry);
620 : }
621 : this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
622 : rect.fRight - rx + sx, rect.fBottom,
623 0 : rect.fRight - rx, rect.fBottom); // bot-right
624 0 : if (!skip_hori) {
625 0 : this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom
626 : }
627 : this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
628 : rect.fLeft, rect.fBottom - ry + sy,
629 0 : rect.fLeft, rect.fBottom - ry); // bot-left
630 0 : if (!skip_vert) {
631 0 : this->lineTo(rect.fLeft, rect.fTop + ry); // left
632 : }
633 : this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
634 : rect.fLeft + rx - sx, rect.fTop,
635 0 : rect.fLeft + rx, rect.fTop); // top-left
636 0 : if (!skip_hori) {
637 0 : this->lineTo(rect.fRight - rx, rect.fTop); // top
638 : }
639 : }
640 0 : this->close();
641 : }
642 :
643 0 : static void add_corner_arc(SkPath* path, const SkRect& rect,
644 : SkScalar rx, SkScalar ry, int startAngle,
645 : SkPath::Direction dir, bool forceMoveTo) {
646 0 : rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
647 0 : ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
648 :
649 : SkRect r;
650 0 : r.set(-rx, -ry, rx, ry);
651 :
652 0 : switch (startAngle) {
653 : case 0:
654 0 : r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
655 0 : break;
656 : case 90:
657 0 : r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);
658 0 : break;
659 0 : case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;
660 0 : case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
661 0 : default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
662 : }
663 :
664 0 : SkScalar start = SkIntToScalar(startAngle);
665 0 : SkScalar sweep = SkIntToScalar(90);
666 0 : if (SkPath::kCCW_Direction == dir) {
667 0 : start += sweep;
668 0 : sweep = -sweep;
669 : }
670 :
671 0 : path->arcTo(r, start, sweep, forceMoveTo);
672 0 : }
673 :
674 0 : void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
675 : Direction dir) {
676 : // abort before we invoke SkAutoPathBoundsUpdate()
677 0 : if (rect.isEmpty()) {
678 0 : return;
679 : }
680 :
681 0 : SkAutoPathBoundsUpdate apbu(this, rect);
682 :
683 0 : if (kCW_Direction == dir) {
684 0 : add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
685 0 : add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
686 0 : add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
687 0 : add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
688 : } else {
689 0 : add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
690 0 : add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
691 0 : add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
692 0 : add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
693 : }
694 0 : this->close();
695 : }
696 :
697 0 : void SkPath::addOval(const SkRect& oval, Direction dir) {
698 0 : SkAutoPathBoundsUpdate apbu(this, oval);
699 :
700 0 : SkScalar cx = oval.centerX();
701 0 : SkScalar cy = oval.centerY();
702 0 : SkScalar rx = SkScalarHalf(oval.width());
703 0 : SkScalar ry = SkScalarHalf(oval.height());
704 : #if 0 // these seem faster than using quads (1/2 the number of edges)
705 : SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
706 : SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
707 :
708 : this->incReserve(13);
709 : this->moveTo(cx + rx, cy);
710 : if (dir == kCCW_Direction) {
711 : this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
712 : this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
713 : this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
714 : this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
715 : } else {
716 : this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
717 : this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
718 : this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
719 : this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
720 : }
721 : #else
722 0 : SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
723 0 : SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
724 0 : SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
725 0 : SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2);
726 :
727 : /*
728 : To handle imprecision in computing the center and radii, we revert to
729 : the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
730 : to ensure that we don't exceed the oval's bounds *ever*, since we want
731 : to use oval for our fast-bounds, rather than have to recompute it.
732 : */
733 0 : const SkScalar L = oval.fLeft; // cx - rx
734 0 : const SkScalar T = oval.fTop; // cy - ry
735 0 : const SkScalar R = oval.fRight; // cx + rx
736 0 : const SkScalar B = oval.fBottom; // cy + ry
737 :
738 0 : this->incReserve(17); // 8 quads + close
739 0 : this->moveTo(R, cy);
740 0 : if (dir == kCCW_Direction) {
741 0 : this->quadTo( R, cy - sy, cx + mx, cy - my);
742 0 : this->quadTo(cx + sx, T, cx , T);
743 0 : this->quadTo(cx - sx, T, cx - mx, cy - my);
744 0 : this->quadTo( L, cy - sy, L, cy );
745 0 : this->quadTo( L, cy + sy, cx - mx, cy + my);
746 0 : this->quadTo(cx - sx, B, cx , B);
747 0 : this->quadTo(cx + sx, B, cx + mx, cy + my);
748 0 : this->quadTo( R, cy + sy, R, cy );
749 : } else {
750 0 : this->quadTo( R, cy + sy, cx + mx, cy + my);
751 0 : this->quadTo(cx + sx, B, cx , B);
752 0 : this->quadTo(cx - sx, B, cx - mx, cy + my);
753 0 : this->quadTo( L, cy + sy, L, cy );
754 0 : this->quadTo( L, cy - sy, cx - mx, cy - my);
755 0 : this->quadTo(cx - sx, T, cx , T);
756 0 : this->quadTo(cx + sx, T, cx + mx, cy - my);
757 0 : this->quadTo( R, cy - sy, R, cy );
758 : }
759 : #endif
760 0 : this->close();
761 0 : }
762 :
763 0 : void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
764 0 : if (r > 0) {
765 : SkRect rect;
766 0 : rect.set(x - r, y - r, x + r, y + r);
767 0 : this->addOval(rect, dir);
768 : }
769 0 : }
770 :
771 : #include "SkGeometry.h"
772 :
773 0 : static int build_arc_points(const SkRect& oval, SkScalar startAngle,
774 : SkScalar sweepAngle,
775 : SkPoint pts[kSkBuildQuadArcStorage]) {
776 : SkVector start, stop;
777 :
778 0 : start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
779 : stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
780 0 : &stop.fX);
781 :
782 : /* If the sweep angle is nearly (but less than) 360, then due to precision
783 : loss in radians-conversion and/or sin/cos, we may end up with coincident
784 : vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
785 : of drawing a nearly complete circle (good).
786 : e.g. canvas.drawArc(0, 359.99, ...)
787 : -vs- canvas.drawArc(0, 359.9, ...)
788 : We try to detect this edge case, and tweak the stop vector
789 : */
790 0 : if (start == stop) {
791 0 : SkScalar sw = SkScalarAbs(sweepAngle);
792 0 : if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
793 0 : SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
794 : // make a guess at a tiny angle (in radians) to tweak by
795 0 : SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
796 : // not sure how much will be enough, so we use a loop
797 0 : do {
798 0 : stopRad -= deltaRad;
799 0 : stop.fY = SkScalarSinCos(stopRad, &stop.fX);
800 : } while (start == stop);
801 : }
802 : }
803 :
804 : SkMatrix matrix;
805 :
806 0 : matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
807 0 : matrix.postTranslate(oval.centerX(), oval.centerY());
808 :
809 : return SkBuildQuadArc(start, stop,
810 : sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
811 0 : &matrix, pts);
812 : }
813 :
814 0 : void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
815 : bool forceMoveTo) {
816 0 : if (oval.width() < 0 || oval.height() < 0) {
817 0 : return;
818 : }
819 :
820 : SkPoint pts[kSkBuildQuadArcStorage];
821 0 : int count = build_arc_points(oval, startAngle, sweepAngle, pts);
822 0 : SkASSERT((count & 1) == 1);
823 :
824 0 : if (fVerbs.count() == 0) {
825 0 : forceMoveTo = true;
826 : }
827 0 : this->incReserve(count);
828 0 : forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
829 0 : for (int i = 1; i < count; i += 2) {
830 0 : this->quadTo(pts[i], pts[i+1]);
831 : }
832 : }
833 :
834 0 : void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
835 : SkScalar sweepAngle) {
836 0 : if (oval.isEmpty() || 0 == sweepAngle) {
837 0 : return;
838 : }
839 :
840 0 : const SkScalar kFullCircleAngle = SkIntToScalar(360);
841 :
842 0 : if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
843 0 : this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
844 0 : return;
845 : }
846 :
847 : SkPoint pts[kSkBuildQuadArcStorage];
848 0 : int count = build_arc_points(oval, startAngle, sweepAngle, pts);
849 :
850 0 : this->incReserve(count);
851 0 : this->moveTo(pts[0]);
852 0 : for (int i = 1; i < count; i += 2) {
853 0 : this->quadTo(pts[i], pts[i+1]);
854 : }
855 : }
856 :
857 : /*
858 : Need to handle the case when the angle is sharp, and our computed end-points
859 : for the arc go behind pt1 and/or p2...
860 : */
861 0 : void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
862 : SkScalar radius) {
863 : SkVector before, after;
864 :
865 : // need to know our prev pt so we can construct tangent vectors
866 : {
867 : SkPoint start;
868 0 : this->getLastPt(&start);
869 : // Handle degenerate cases by adding a line to the first point and
870 : // bailing out.
871 0 : if ((x1 == start.fX && y1 == start.fY) ||
872 : (x1 == x2 && y1 == y2) ||
873 : radius == 0) {
874 0 : this->lineTo(x1, y1);
875 0 : return;
876 : }
877 0 : before.setNormalize(x1 - start.fX, y1 - start.fY);
878 0 : after.setNormalize(x2 - x1, y2 - y1);
879 : }
880 :
881 0 : SkScalar cosh = SkPoint::DotProduct(before, after);
882 0 : SkScalar sinh = SkPoint::CrossProduct(before, after);
883 :
884 0 : if (SkScalarNearlyZero(sinh)) { // angle is too tight
885 0 : this->lineTo(x1, y1);
886 0 : return;
887 : }
888 :
889 0 : SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
890 0 : if (dist < 0) {
891 0 : dist = -dist;
892 : }
893 :
894 0 : SkScalar xx = x1 - SkScalarMul(dist, before.fX);
895 0 : SkScalar yy = y1 - SkScalarMul(dist, before.fY);
896 : SkRotationDirection arcDir;
897 :
898 : // now turn before/after into normals
899 0 : if (sinh > 0) {
900 0 : before.rotateCCW();
901 0 : after.rotateCCW();
902 0 : arcDir = kCW_SkRotationDirection;
903 : } else {
904 0 : before.rotateCW();
905 0 : after.rotateCW();
906 0 : arcDir = kCCW_SkRotationDirection;
907 : }
908 :
909 : SkMatrix matrix;
910 : SkPoint pts[kSkBuildQuadArcStorage];
911 :
912 0 : matrix.setScale(radius, radius);
913 : matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
914 0 : yy - SkScalarMul(radius, before.fY));
915 :
916 0 : int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
917 :
918 0 : this->incReserve(count);
919 : // [xx,yy] == pts[0]
920 0 : this->lineTo(xx, yy);
921 0 : for (int i = 1; i < count; i += 2) {
922 0 : this->quadTo(pts[i], pts[i+1]);
923 : }
924 : }
925 :
926 : ///////////////////////////////////////////////////////////////////////////////
927 :
928 0 : void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
929 : SkMatrix matrix;
930 :
931 0 : matrix.setTranslate(dx, dy);
932 0 : this->addPath(path, matrix);
933 0 : }
934 :
935 0 : void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
936 0 : this->incReserve(path.fPts.count());
937 :
938 0 : RawIter iter(path);
939 : SkPoint pts[4];
940 : Verb verb;
941 :
942 0 : SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
943 :
944 0 : while ((verb = iter.next(pts)) != kDone_Verb) {
945 0 : switch (verb) {
946 : case kMove_Verb:
947 0 : proc(matrix, &pts[0], &pts[0], 1);
948 0 : this->moveTo(pts[0]);
949 0 : break;
950 : case kLine_Verb:
951 0 : proc(matrix, &pts[1], &pts[1], 1);
952 0 : this->lineTo(pts[1]);
953 0 : break;
954 : case kQuad_Verb:
955 0 : proc(matrix, &pts[1], &pts[1], 2);
956 0 : this->quadTo(pts[1], pts[2]);
957 0 : break;
958 : case kCubic_Verb:
959 0 : proc(matrix, &pts[1], &pts[1], 3);
960 0 : this->cubicTo(pts[1], pts[2], pts[3]);
961 0 : break;
962 : case kClose_Verb:
963 0 : this->close();
964 0 : break;
965 : default:
966 0 : SkDEBUGFAIL("unknown verb");
967 : }
968 : }
969 0 : }
970 :
971 : ///////////////////////////////////////////////////////////////////////////////
972 :
973 : static const uint8_t gPtsInVerb[] = {
974 : 1, // kMove
975 : 1, // kLine
976 : 2, // kQuad
977 : 3, // kCubic
978 : 0, // kClose
979 : 0 // kDone
980 : };
981 :
982 : // ignore the initial moveto, and stop when the 1st contour ends
983 0 : void SkPath::pathTo(const SkPath& path) {
984 0 : int i, vcount = path.fVerbs.count();
985 0 : if (vcount == 0) {
986 0 : return;
987 : }
988 :
989 0 : this->incReserve(vcount);
990 :
991 0 : const uint8_t* verbs = path.fVerbs.begin();
992 0 : const SkPoint* pts = path.fPts.begin() + 1; // 1 for the initial moveTo
993 :
994 0 : SkASSERT(verbs[0] == kMove_Verb);
995 0 : for (i = 1; i < vcount; i++) {
996 0 : switch (verbs[i]) {
997 : case kLine_Verb:
998 0 : this->lineTo(pts[0].fX, pts[0].fY);
999 0 : break;
1000 : case kQuad_Verb:
1001 0 : this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
1002 0 : break;
1003 : case kCubic_Verb:
1004 0 : this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
1005 0 : pts[2].fX, pts[2].fY);
1006 0 : break;
1007 : case kClose_Verb:
1008 0 : return;
1009 : }
1010 0 : pts += gPtsInVerb[verbs[i]];
1011 : }
1012 : }
1013 :
1014 : // ignore the last point of the 1st contour
1015 0 : void SkPath::reversePathTo(const SkPath& path) {
1016 0 : int i, vcount = path.fVerbs.count();
1017 0 : if (vcount == 0) {
1018 0 : return;
1019 : }
1020 :
1021 0 : this->incReserve(vcount);
1022 :
1023 0 : const uint8_t* verbs = path.fVerbs.begin();
1024 0 : const SkPoint* pts = path.fPts.begin();
1025 :
1026 0 : SkASSERT(verbs[0] == kMove_Verb);
1027 0 : for (i = 1; i < vcount; i++) {
1028 0 : int n = gPtsInVerb[verbs[i]];
1029 0 : if (n == 0) {
1030 0 : break;
1031 : }
1032 0 : pts += n;
1033 : }
1034 :
1035 0 : while (--i > 0) {
1036 0 : switch (verbs[i]) {
1037 : case kLine_Verb:
1038 0 : this->lineTo(pts[-1].fX, pts[-1].fY);
1039 0 : break;
1040 : case kQuad_Verb:
1041 0 : this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
1042 0 : break;
1043 : case kCubic_Verb:
1044 0 : this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
1045 0 : pts[-3].fX, pts[-3].fY);
1046 0 : break;
1047 : default:
1048 0 : SkDEBUGFAIL("bad verb");
1049 0 : break;
1050 : }
1051 0 : pts -= gPtsInVerb[verbs[i]];
1052 : }
1053 : }
1054 :
1055 : ///////////////////////////////////////////////////////////////////////////////
1056 :
1057 0 : void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1058 : SkMatrix matrix;
1059 :
1060 0 : matrix.setTranslate(dx, dy);
1061 0 : this->transform(matrix, dst);
1062 0 : }
1063 :
1064 : #include "SkGeometry.h"
1065 :
1066 0 : static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
1067 : int level = 2) {
1068 0 : if (--level >= 0) {
1069 : SkPoint tmp[5];
1070 :
1071 0 : SkChopQuadAtHalf(pts, tmp);
1072 0 : subdivide_quad_to(path, &tmp[0], level);
1073 0 : subdivide_quad_to(path, &tmp[2], level);
1074 : } else {
1075 0 : path->quadTo(pts[1], pts[2]);
1076 : }
1077 0 : }
1078 :
1079 0 : static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1080 : int level = 2) {
1081 0 : if (--level >= 0) {
1082 : SkPoint tmp[7];
1083 :
1084 0 : SkChopCubicAtHalf(pts, tmp);
1085 0 : subdivide_cubic_to(path, &tmp[0], level);
1086 0 : subdivide_cubic_to(path, &tmp[3], level);
1087 : } else {
1088 0 : path->cubicTo(pts[1], pts[2], pts[3]);
1089 : }
1090 0 : }
1091 :
1092 0 : void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
1093 0 : SkDEBUGCODE(this->validate();)
1094 0 : if (dst == NULL) {
1095 0 : dst = (SkPath*)this;
1096 : }
1097 :
1098 0 : if (matrix.hasPerspective()) {
1099 0 : SkPath tmp;
1100 0 : tmp.fFillType = fFillType;
1101 :
1102 0 : SkPath::Iter iter(*this, false);
1103 : SkPoint pts[4];
1104 : SkPath::Verb verb;
1105 :
1106 0 : while ((verb = iter.next(pts)) != kDone_Verb) {
1107 0 : switch (verb) {
1108 : case kMove_Verb:
1109 0 : tmp.moveTo(pts[0]);
1110 0 : break;
1111 : case kLine_Verb:
1112 0 : tmp.lineTo(pts[1]);
1113 0 : break;
1114 : case kQuad_Verb:
1115 0 : subdivide_quad_to(&tmp, pts);
1116 0 : break;
1117 : case kCubic_Verb:
1118 0 : subdivide_cubic_to(&tmp, pts);
1119 0 : break;
1120 : case kClose_Verb:
1121 0 : tmp.close();
1122 0 : break;
1123 : default:
1124 0 : SkDEBUGFAIL("unknown verb");
1125 0 : break;
1126 : }
1127 : }
1128 :
1129 0 : dst->swap(tmp);
1130 0 : matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
1131 : } else {
1132 : // remember that dst might == this, so be sure to check
1133 : // fBoundsIsDirty before we set it
1134 0 : if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
1135 : // if we're empty, fastbounds should not be mapped
1136 0 : matrix.mapRect(&dst->fBounds, fBounds);
1137 0 : dst->fBoundsIsDirty = false;
1138 : } else {
1139 : GEN_ID_PTR_INC(dst);
1140 0 : dst->fBoundsIsDirty = true;
1141 : }
1142 :
1143 0 : if (this != dst) {
1144 0 : dst->fVerbs = fVerbs;
1145 0 : dst->fPts.setCount(fPts.count());
1146 0 : dst->fFillType = fFillType;
1147 0 : dst->fSegmentMask = fSegmentMask;
1148 0 : dst->fConvexity = fConvexity;
1149 : }
1150 0 : matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
1151 0 : SkDEBUGCODE(dst->validate();)
1152 : }
1153 0 : }
1154 :
1155 : ///////////////////////////////////////////////////////////////////////////////
1156 : ///////////////////////////////////////////////////////////////////////////////
1157 :
1158 : enum SegmentState {
1159 : kAfterClose_SegmentState, // We will need a move next, but we have a
1160 : // previous close pt to use for the new move.
1161 : kAfterMove_SegmentState, // We have seen a move, but nothing else.
1162 : kAfterPrimitive_SegmentState // We have seen a primitive but not yet
1163 : // closed the path. Also the initial state.
1164 : };
1165 :
1166 0 : SkPath::Iter::Iter() {
1167 : #ifdef SK_DEBUG
1168 0 : fPts = NULL;
1169 0 : fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1170 0 : fForceClose = fCloseLine = false;
1171 0 : fSegmentState = kAfterPrimitive_SegmentState;
1172 : #endif
1173 : // need to init enough to make next() harmlessly return kDone_Verb
1174 0 : fVerbs = NULL;
1175 0 : fVerbStop = NULL;
1176 0 : fNeedClose = false;
1177 0 : }
1178 :
1179 0 : SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1180 0 : this->setPath(path, forceClose);
1181 0 : }
1182 :
1183 0 : void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1184 0 : fPts = path.fPts.begin();
1185 0 : fVerbs = path.fVerbs.begin();
1186 0 : fVerbStop = path.fVerbs.end();
1187 0 : fLastPt.fX = fLastPt.fY = 0;
1188 0 : fMoveTo.fX = fMoveTo.fY = 0;
1189 0 : fForceClose = SkToU8(forceClose);
1190 0 : fNeedClose = false;
1191 0 : fSegmentState = kAfterClose_SegmentState;
1192 0 : }
1193 :
1194 0 : bool SkPath::Iter::isClosedContour() const {
1195 0 : if (fVerbs == NULL || fVerbs == fVerbStop) {
1196 0 : return false;
1197 : }
1198 0 : if (fForceClose) {
1199 0 : return true;
1200 : }
1201 :
1202 0 : const uint8_t* verbs = fVerbs;
1203 0 : const uint8_t* stop = fVerbStop;
1204 :
1205 0 : if (kMove_Verb == *verbs) {
1206 0 : verbs += 1; // skip the initial moveto
1207 : }
1208 :
1209 0 : while (verbs < stop) {
1210 0 : unsigned v = *verbs++;
1211 0 : if (kMove_Verb == v) {
1212 0 : break;
1213 : }
1214 0 : if (kClose_Verb == v) {
1215 0 : return true;
1216 : }
1217 : }
1218 0 : return false;
1219 : }
1220 :
1221 0 : SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1222 0 : if (fLastPt != fMoveTo) {
1223 : // A special case: if both points are NaN, SkPoint::operation== returns
1224 : // false, but the iterator expects that they are treated as the same.
1225 : // (consider SkPoint is a 2-dimension float point).
1226 0 : if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1227 0 : SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1228 0 : return kClose_Verb;
1229 : }
1230 :
1231 0 : if (pts) {
1232 0 : pts[0] = fLastPt;
1233 0 : pts[1] = fMoveTo;
1234 : }
1235 0 : fLastPt = fMoveTo;
1236 0 : fCloseLine = true;
1237 0 : return kLine_Verb;
1238 : } else {
1239 0 : pts[0] = fMoveTo;
1240 0 : return kClose_Verb;
1241 : }
1242 : }
1243 :
1244 0 : bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
1245 0 : if (fSegmentState == kAfterClose_SegmentState) {
1246 : // We have closed a curve and have a primitive, so we need a move.
1247 : // Set the first return pt to the most recent move pt
1248 0 : if (pts) {
1249 0 : *pts = fMoveTo;
1250 : }
1251 0 : fNeedClose = fForceClose;
1252 0 : fSegmentState = kAfterMove_SegmentState;
1253 0 : fVerbs -= 1; // Step back to see the primitive again
1254 0 : return true;
1255 : }
1256 :
1257 0 : if (fSegmentState == kAfterMove_SegmentState) {
1258 : // Set the first return pt to the move pt
1259 0 : if (pts) {
1260 0 : *pts = fMoveTo;
1261 : }
1262 0 : fSegmentState = kAfterPrimitive_SegmentState;
1263 : } else {
1264 0 : SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
1265 : // Set the first return pt to the last pt of the previous primitive.
1266 0 : if (pts) {
1267 0 : *pts = fPts[-1];
1268 : }
1269 : }
1270 0 : return false;
1271 : }
1272 :
1273 0 : void SkPath::Iter::consumeDegenerateSegments() {
1274 : // We need to step over anything that will not move the current draw point
1275 : // forward before the next move is seen
1276 0 : const uint8_t* lastMoveVerb = 0;
1277 0 : const SkPoint* lastMovePt = 0;
1278 0 : SkPoint lastPt = fLastPt;
1279 0 : while (fVerbs != fVerbStop) {
1280 0 : unsigned verb = *fVerbs;
1281 0 : switch (verb) {
1282 : case kMove_Verb:
1283 : // Keep a record of this most recent move
1284 0 : lastMoveVerb = fVerbs;
1285 0 : lastMovePt = fPts;
1286 0 : lastPt = fPts[0];
1287 0 : fVerbs++;
1288 0 : fPts++;
1289 0 : break;
1290 :
1291 : case kClose_Verb:
1292 : // A close when we are in a segment is always valid
1293 0 : if (fSegmentState == kAfterPrimitive_SegmentState) {
1294 0 : return;
1295 : }
1296 : // A close at any other time must be ignored
1297 0 : fVerbs++;
1298 0 : break;
1299 :
1300 : case kLine_Verb:
1301 0 : if (!IsLineDegenerate(lastPt, fPts[0])) {
1302 0 : if (lastMoveVerb) {
1303 0 : fVerbs = lastMoveVerb;
1304 0 : fPts = lastMovePt;
1305 0 : return;
1306 : }
1307 0 : return;
1308 : }
1309 : // Ignore this line and continue
1310 0 : fVerbs++;
1311 0 : fPts++;
1312 0 : break;
1313 :
1314 : case kQuad_Verb:
1315 0 : if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
1316 0 : if (lastMoveVerb) {
1317 0 : fVerbs = lastMoveVerb;
1318 0 : fPts = lastMovePt;
1319 0 : return;
1320 : }
1321 0 : return;
1322 : }
1323 : // Ignore this line and continue
1324 0 : fVerbs++;
1325 0 : fPts += 2;
1326 0 : break;
1327 :
1328 : case kCubic_Verb:
1329 0 : if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2])) {
1330 0 : if (lastMoveVerb) {
1331 0 : fVerbs = lastMoveVerb;
1332 0 : fPts = lastMovePt;
1333 0 : return;
1334 : }
1335 0 : return;
1336 : }
1337 : // Ignore this line and continue
1338 0 : fVerbs++;
1339 0 : fPts += 3;
1340 0 : break;
1341 :
1342 : default:
1343 0 : SkDEBUGFAIL("Should never see kDone_Verb");
1344 : }
1345 : }
1346 : }
1347 :
1348 0 : SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
1349 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
1350 0 : this->consumeDegenerateSegments();
1351 : #endif
1352 :
1353 0 : if (fVerbs == fVerbStop) {
1354 : // Close the curve if requested and if there is some curve to close
1355 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
1356 : if (fNeedClose) {
1357 : #else
1358 0 : if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
1359 : #endif
1360 0 : if (kLine_Verb == this->autoClose(pts)) {
1361 0 : return kLine_Verb;
1362 : }
1363 0 : fNeedClose = false;
1364 0 : return kClose_Verb;
1365 : }
1366 0 : return kDone_Verb;
1367 : }
1368 :
1369 0 : unsigned verb = *fVerbs++;
1370 0 : const SkPoint* srcPts = fPts;
1371 :
1372 0 : switch (verb) {
1373 : case kMove_Verb:
1374 0 : if (fNeedClose) {
1375 0 : fVerbs -= 1;
1376 0 : verb = this->autoClose(pts);
1377 0 : if (verb == kClose_Verb) {
1378 0 : fNeedClose = false;
1379 : }
1380 0 : return (Verb)verb;
1381 : }
1382 0 : if (fVerbs == fVerbStop) { // might be a trailing moveto
1383 0 : return kDone_Verb;
1384 : }
1385 0 : fMoveTo = *srcPts;
1386 0 : if (pts) {
1387 0 : pts[0] = *srcPts;
1388 : }
1389 0 : srcPts += 1;
1390 0 : fSegmentState = kAfterMove_SegmentState;
1391 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
1392 0 : fLastPt = fMoveTo;
1393 : #endif
1394 0 : fNeedClose = fForceClose;
1395 0 : break;
1396 : case kLine_Verb:
1397 0 : if (this->cons_moveTo(pts)) {
1398 0 : return kMove_Verb;
1399 : }
1400 0 : if (pts) {
1401 0 : pts[1] = srcPts[0];
1402 : }
1403 0 : fLastPt = srcPts[0];
1404 0 : fCloseLine = false;
1405 0 : srcPts += 1;
1406 0 : break;
1407 : case kQuad_Verb:
1408 0 : if (this->cons_moveTo(pts)) {
1409 0 : return kMove_Verb;
1410 : }
1411 0 : if (pts) {
1412 0 : memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1413 : }
1414 0 : fLastPt = srcPts[1];
1415 0 : srcPts += 2;
1416 0 : break;
1417 : case kCubic_Verb:
1418 0 : if (this->cons_moveTo(pts)) {
1419 0 : return kMove_Verb;
1420 : }
1421 0 : if (pts) {
1422 0 : memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1423 : }
1424 0 : fLastPt = srcPts[2];
1425 0 : srcPts += 3;
1426 0 : break;
1427 : case kClose_Verb:
1428 0 : verb = this->autoClose(pts);
1429 0 : if (verb == kLine_Verb) {
1430 0 : fVerbs -= 1;
1431 : } else {
1432 0 : fNeedClose = false;
1433 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
1434 0 : fSegmentState = kAfterClose_SegmentState;
1435 : #endif
1436 : }
1437 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
1438 : fSegmentState = kAfterClose_SegmentState;
1439 : #else
1440 0 : fLastPt = fMoveTo;
1441 : #endif
1442 0 : break;
1443 : }
1444 0 : fPts = srcPts;
1445 0 : return (Verb)verb;
1446 : }
1447 :
1448 : ///////////////////////////////////////////////////////////////////////////////
1449 :
1450 0 : SkPath::RawIter::RawIter() {
1451 : #ifdef SK_DEBUG
1452 0 : fPts = NULL;
1453 0 : fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1454 : #endif
1455 : // need to init enough to make next() harmlessly return kDone_Verb
1456 0 : fVerbs = NULL;
1457 0 : fVerbStop = NULL;
1458 0 : }
1459 :
1460 0 : SkPath::RawIter::RawIter(const SkPath& path) {
1461 0 : this->setPath(path);
1462 0 : }
1463 :
1464 0 : void SkPath::RawIter::setPath(const SkPath& path) {
1465 0 : fPts = path.fPts.begin();
1466 0 : fVerbs = path.fVerbs.begin();
1467 0 : fVerbStop = path.fVerbs.end();
1468 0 : fMoveTo.fX = fMoveTo.fY = 0;
1469 0 : fLastPt.fX = fLastPt.fY = 0;
1470 0 : }
1471 :
1472 0 : SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
1473 0 : if (fVerbs == fVerbStop) {
1474 0 : return kDone_Verb;
1475 : }
1476 :
1477 0 : unsigned verb = *fVerbs++;
1478 0 : const SkPoint* srcPts = fPts;
1479 :
1480 0 : switch (verb) {
1481 : case kMove_Verb:
1482 0 : if (pts) {
1483 0 : pts[0] = *srcPts;
1484 : }
1485 0 : fMoveTo = srcPts[0];
1486 0 : fLastPt = fMoveTo;
1487 0 : srcPts += 1;
1488 0 : break;
1489 : case kLine_Verb:
1490 0 : if (pts) {
1491 0 : pts[0] = fLastPt;
1492 0 : pts[1] = srcPts[0];
1493 : }
1494 0 : fLastPt = srcPts[0];
1495 0 : srcPts += 1;
1496 0 : break;
1497 : case kQuad_Verb:
1498 0 : if (pts) {
1499 0 : pts[0] = fLastPt;
1500 0 : memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1501 : }
1502 0 : fLastPt = srcPts[1];
1503 0 : srcPts += 2;
1504 0 : break;
1505 : case kCubic_Verb:
1506 0 : if (pts) {
1507 0 : pts[0] = fLastPt;
1508 0 : memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1509 : }
1510 0 : fLastPt = srcPts[2];
1511 0 : srcPts += 3;
1512 0 : break;
1513 : case kClose_Verb:
1514 0 : fLastPt = fMoveTo;
1515 0 : if (pts) {
1516 0 : pts[0] = fMoveTo;
1517 : }
1518 0 : break;
1519 : }
1520 0 : fPts = srcPts;
1521 0 : return (Verb)verb;
1522 : }
1523 :
1524 : ///////////////////////////////////////////////////////////////////////////////
1525 :
1526 : /*
1527 : Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
1528 : */
1529 :
1530 0 : void SkPath::flatten(SkWriter32& buffer) const {
1531 0 : SkDEBUGCODE(this->validate();)
1532 :
1533 0 : buffer.write32(fPts.count());
1534 0 : buffer.write32(fVerbs.count());
1535 0 : buffer.write32((fFillType << 8) | fSegmentMask);
1536 0 : buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
1537 0 : buffer.writePad(fVerbs.begin(), fVerbs.count());
1538 0 : }
1539 :
1540 0 : void SkPath::unflatten(SkReader32& buffer) {
1541 0 : fPts.setCount(buffer.readS32());
1542 0 : fVerbs.setCount(buffer.readS32());
1543 0 : uint32_t packed = buffer.readS32();
1544 0 : fFillType = packed >> 8;
1545 0 : fSegmentMask = packed & 0xFF;
1546 0 : buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
1547 0 : buffer.read(fVerbs.begin(), fVerbs.count());
1548 :
1549 : GEN_ID_INC;
1550 0 : DIRTY_AFTER_EDIT;
1551 :
1552 0 : SkDEBUGCODE(this->validate();)
1553 0 : }
1554 :
1555 : ///////////////////////////////////////////////////////////////////////////////
1556 :
1557 0 : void SkPath::dump(bool forceClose, const char title[]) const {
1558 0 : Iter iter(*this, forceClose);
1559 : SkPoint pts[4];
1560 : Verb verb;
1561 :
1562 : SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
1563 0 : title ? title : "");
1564 :
1565 0 : while ((verb = iter.next(pts)) != kDone_Verb) {
1566 0 : switch (verb) {
1567 : case kMove_Verb:
1568 : #ifdef SK_CAN_USE_FLOAT
1569 : SkDebugf(" path: moveTo [%g %g]\n",
1570 0 : SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
1571 : #else
1572 : SkDebugf(" path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
1573 : #endif
1574 0 : break;
1575 : case kLine_Verb:
1576 : #ifdef SK_CAN_USE_FLOAT
1577 : SkDebugf(" path: lineTo [%g %g]\n",
1578 0 : SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
1579 : #else
1580 : SkDebugf(" path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
1581 : #endif
1582 0 : break;
1583 : case kQuad_Verb:
1584 : #ifdef SK_CAN_USE_FLOAT
1585 : SkDebugf(" path: quadTo [%g %g] [%g %g]\n",
1586 : SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
1587 0 : SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
1588 : #else
1589 : SkDebugf(" path: quadTo [%x %x] [%x %x]\n",
1590 : pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
1591 : #endif
1592 0 : break;
1593 : case kCubic_Verb:
1594 : #ifdef SK_CAN_USE_FLOAT
1595 : SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n",
1596 : SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
1597 : SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
1598 0 : SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
1599 : #else
1600 : SkDebugf(" path: cubeTo [%x %x] [%x %x] [%x %x]\n",
1601 : pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
1602 : pts[3].fX, pts[3].fY);
1603 : #endif
1604 0 : break;
1605 : case kClose_Verb:
1606 0 : SkDebugf(" path: close\n");
1607 0 : break;
1608 : default:
1609 0 : SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
1610 0 : verb = kDone_Verb; // stop the loop
1611 0 : break;
1612 : }
1613 : }
1614 0 : SkDebugf("path: done %s\n", title ? title : "");
1615 0 : }
1616 :
1617 0 : void SkPath::dump() const {
1618 0 : this->dump(false);
1619 0 : }
1620 :
1621 : #ifdef SK_DEBUG
1622 0 : void SkPath::validate() const {
1623 0 : SkASSERT(this != NULL);
1624 0 : SkASSERT((fFillType & ~3) == 0);
1625 0 : fPts.validate();
1626 0 : fVerbs.validate();
1627 :
1628 0 : if (!fBoundsIsDirty) {
1629 : SkRect bounds;
1630 0 : compute_pt_bounds(&bounds, fPts);
1631 0 : if (fPts.count() <= 1) {
1632 : // if we're empty, fBounds may be empty but translated, so we can't
1633 : // necessarily compare to bounds directly
1634 : // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
1635 : // be [2, 2, 2, 2]
1636 0 : SkASSERT(bounds.isEmpty());
1637 0 : SkASSERT(fBounds.isEmpty());
1638 : } else {
1639 0 : if (bounds.isEmpty()) {
1640 0 : SkASSERT(fBounds.isEmpty());
1641 : } else {
1642 0 : if (!fBounds.isEmpty()) {
1643 0 : SkASSERT(fBounds.contains(bounds));
1644 : }
1645 : }
1646 : }
1647 : }
1648 :
1649 0 : uint32_t mask = 0;
1650 0 : for (int i = 0; i < fVerbs.count(); i++) {
1651 0 : switch (fVerbs[i]) {
1652 : case kLine_Verb:
1653 0 : mask |= kLine_SegmentMask;
1654 0 : break;
1655 : case kQuad_Verb:
1656 0 : mask |= kQuad_SegmentMask;
1657 0 : break;
1658 : case kCubic_Verb:
1659 0 : mask |= kCubic_SegmentMask;
1660 : }
1661 : }
1662 0 : SkASSERT(mask == fSegmentMask);
1663 0 : }
1664 : #endif
1665 :
1666 : ///////////////////////////////////////////////////////////////////////////////
1667 :
1668 0 : static int sign(SkScalar x) { return x < 0; }
1669 : #define kValueNeverReturnedBySign 2
1670 :
1671 0 : static int CrossProductSign(const SkVector& a, const SkVector& b) {
1672 0 : return SkScalarSignAsInt(SkPoint::CrossProduct(a, b));
1673 : }
1674 :
1675 : // only valid for a single contour
1676 : struct Convexicator {
1677 0 : Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
1678 0 : fSign = 0;
1679 : // warnings
1680 0 : fCurrPt.set(0, 0);
1681 0 : fVec0.set(0, 0);
1682 0 : fVec1.set(0, 0);
1683 0 : fFirstVec.set(0, 0);
1684 :
1685 0 : fDx = fDy = 0;
1686 0 : fSx = fSy = kValueNeverReturnedBySign;
1687 0 : }
1688 :
1689 0 : SkPath::Convexity getConvexity() const { return fConvexity; }
1690 :
1691 0 : void addPt(const SkPoint& pt) {
1692 0 : if (SkPath::kConcave_Convexity == fConvexity) {
1693 0 : return;
1694 : }
1695 :
1696 0 : if (0 == fPtCount) {
1697 0 : fCurrPt = pt;
1698 0 : ++fPtCount;
1699 : } else {
1700 0 : SkVector vec = pt - fCurrPt;
1701 0 : if (vec.fX || vec.fY) {
1702 0 : fCurrPt = pt;
1703 0 : if (++fPtCount == 2) {
1704 0 : fFirstVec = fVec1 = vec;
1705 : } else {
1706 0 : SkASSERT(fPtCount > 2);
1707 0 : this->addVec(vec);
1708 : }
1709 :
1710 0 : int sx = sign(vec.fX);
1711 0 : int sy = sign(vec.fY);
1712 0 : fDx += (sx != fSx);
1713 0 : fDy += (sy != fSy);
1714 0 : fSx = sx;
1715 0 : fSy = sy;
1716 :
1717 0 : if (fDx > 3 || fDy > 3) {
1718 0 : fConvexity = SkPath::kConcave_Convexity;
1719 : }
1720 : }
1721 : }
1722 : }
1723 :
1724 0 : void close() {
1725 0 : if (fPtCount > 2) {
1726 0 : this->addVec(fFirstVec);
1727 : }
1728 0 : }
1729 :
1730 : private:
1731 0 : void addVec(const SkVector& vec) {
1732 0 : SkASSERT(vec.fX || vec.fY);
1733 0 : fVec0 = fVec1;
1734 0 : fVec1 = vec;
1735 0 : int sign = CrossProductSign(fVec0, fVec1);
1736 0 : if (0 == fSign) {
1737 0 : fSign = sign;
1738 0 : } else if (sign) {
1739 0 : if (fSign != sign) {
1740 0 : fConvexity = SkPath::kConcave_Convexity;
1741 : }
1742 : }
1743 0 : }
1744 :
1745 : SkPoint fCurrPt;
1746 : SkVector fVec0, fVec1, fFirstVec;
1747 : int fPtCount; // non-degenerate points
1748 : int fSign;
1749 : SkPath::Convexity fConvexity;
1750 : int fDx, fDy, fSx, fSy;
1751 : };
1752 :
1753 0 : SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
1754 : SkPoint pts[4];
1755 : SkPath::Verb verb;
1756 0 : SkPath::Iter iter(path, true);
1757 :
1758 0 : int contourCount = 0;
1759 : int count;
1760 0 : Convexicator state;
1761 :
1762 0 : while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1763 0 : switch (verb) {
1764 : case kMove_Verb:
1765 0 : if (++contourCount > 1) {
1766 0 : return kConcave_Convexity;
1767 : }
1768 0 : pts[1] = pts[0];
1769 0 : count = 1;
1770 0 : break;
1771 0 : case kLine_Verb: count = 1; break;
1772 0 : case kQuad_Verb: count = 2; break;
1773 0 : case kCubic_Verb: count = 3; break;
1774 : case kClose_Verb:
1775 0 : state.close();
1776 0 : count = 0;
1777 0 : break;
1778 : default:
1779 0 : SkDEBUGFAIL("bad verb");
1780 0 : return kConcave_Convexity;
1781 : }
1782 :
1783 0 : for (int i = 1; i <= count; i++) {
1784 0 : state.addPt(pts[i]);
1785 : }
1786 : // early exit
1787 0 : if (kConcave_Convexity == state.getConvexity()) {
1788 0 : return kConcave_Convexity;
1789 : }
1790 : }
1791 0 : return state.getConvexity();
1792 : }
|