2 * Copyright 2006 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
10 #include "SkCubicClipper.h"
11 #include "SkErrorInternals.h"
12 #include "SkGeometry.h"
14 #include "SkPathPriv.h"
15 #include "SkPathRef.h"
18 ////////////////////////////////////////////////////////////////////////////
21 * Path.bounds is defined to be the bounds of all the control points.
22 * If we called bounds.join(r) we would skip r if r was empty, which breaks
23 * our promise. Hence we have a custom joiner that doesn't look at emptiness
25 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
26 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
27 dst->fTop = SkMinScalar(dst->fTop, src.fTop);
28 dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
29 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
32 static bool is_degenerate(const SkPath& path) {
33 SkPath::Iter iter(path, false);
35 return SkPath::kDone_Verb == iter.next(pts);
38 class SkAutoDisableDirectionCheck {
40 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
41 fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection.load());
44 ~SkAutoDisableDirectionCheck() {
45 fPath->fFirstDirection = fSaved;
50 SkPathPriv::FirstDirection fSaved;
52 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
54 /* This guy's constructor/destructor bracket a path editing operation. It is
55 used when we know the bounds of the amount we are going to add to the path
56 (usually a new contour, but not required).
58 It captures some state about the path up front (i.e. if it already has a
59 cached bounds), and then if it can, it updates the cache bounds explicitly,
60 avoiding the need to revisit all of the points in getBounds().
62 It also notes if the path was originally degenerate, and if so, sets
63 isConvex to true. Thus it can only be used if the contour being added is
66 class SkAutoPathBoundsUpdate {
68 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
72 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
73 SkScalar right, SkScalar bottom) {
74 fRect.set(left, top, right, bottom);
78 ~SkAutoPathBoundsUpdate() {
79 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
80 : SkPath::kUnknown_Convexity);
81 if (fEmpty || fHasValidBounds) {
82 fPath->setBounds(fRect);
93 void init(SkPath* path) {
94 // Cannot use fRect for our bounds unless we know it is sorted
97 // Mark the path's bounds as dirty if (1) they are, or (2) the path
98 // is non-finite, and therefore its bounds are not meaningful
99 fHasValidBounds = path->hasComputedBounds() && path->isFinite();
100 fEmpty = path->isEmpty();
101 if (fHasValidBounds && !fEmpty) {
102 joinNoEmptyChecks(&fRect, fPath->getBounds());
104 fDegenerate = is_degenerate(*path);
107 #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
109 ////////////////////////////////////////////////////////////////////////////
112 Stores the verbs and points as they are given to us, with exceptions:
113 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
114 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
116 The iterator does more cleanup, especially if forceClose == true
117 1. If we encounter degenerate segments, remove them
118 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
119 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
120 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
123 ////////////////////////////////////////////////////////////////////////////
125 // flag to require a moveTo if we begin with something else, like lineTo etc.
126 #define INITIAL_LASTMOVETOINDEX_VALUE ~0
129 : fPathRef(SkPathRef::CreateEmpty()) {
134 void SkPath::resetFields() {
135 //fPathRef is assumed to have been emptied by the caller.
136 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
137 fFillType = kWinding_FillType;
138 fConvexity = kUnknown_Convexity;
139 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
141 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we
142 // don't want to muck with it if it's been set to something non-nullptr.
145 SkPath::SkPath(const SkPath& that)
146 : fPathRef(SkRef(that.fPathRef.get())) {
147 this->copyFields(that);
148 SkDEBUGCODE(that.validate();)
152 SkDEBUGCODE(this->validate();)
155 SkPath& SkPath::operator=(const SkPath& that) {
156 SkDEBUGCODE(that.validate();)
159 fPathRef.reset(SkRef(that.fPathRef.get()));
160 this->copyFields(that);
162 SkDEBUGCODE(this->validate();)
166 void SkPath::copyFields(const SkPath& that) {
167 //fPathRef is assumed to have been set by the caller.
168 fLastMoveToIndex = that.fLastMoveToIndex;
169 fFillType = that.fFillType;
170 fConvexity = that.fConvexity;
171 // Simulate fFirstDirection = that.fFirstDirection;
172 fFirstDirection.store(that.fFirstDirection.load());
173 fIsVolatile = that.fIsVolatile;
176 bool operator==(const SkPath& a, const SkPath& b) {
177 // note: don't need to look at isConvex or bounds, since just comparing the
178 // raw data is sufficient.
180 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
183 void SkPath::swap(SkPath& that) {
185 fPathRef.swap(that.fPathRef);
186 SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
187 SkTSwap<uint8_t>(fFillType, that.fFillType);
188 SkTSwap<uint8_t>(fConvexity, that.fConvexity);
189 // Simulate SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection);
190 uint8_t temp = fFirstDirection;
191 fFirstDirection.store(that.fFirstDirection.load());
192 that.fFirstDirection.store(temp);
193 SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile);
197 bool SkPath::isInterpolatable(const SkPath& compare) const {
198 int count = fPathRef->countVerbs();
199 if (count != compare.fPathRef->countVerbs()) {
205 if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
209 return !fPathRef->countWeights() ||
210 !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
211 fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
214 bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
215 int verbCount = fPathRef->countVerbs();
216 if (verbCount != ending.fPathRef->countVerbs()) {
224 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef);
228 static inline bool check_edge_against_rect(const SkPoint& p0,
231 SkPathPriv::FirstDirection dir) {
232 const SkPoint* edgeBegin;
234 if (SkPathPriv::kCW_FirstDirection == dir) {
242 // check the cross product of v with the vec from edgeBegin to each rect corner
243 SkScalar yL = SkScalarMul(v.fY, rect.fLeft - edgeBegin->fX);
244 SkScalar xT = SkScalarMul(v.fX, rect.fTop - edgeBegin->fY);
245 SkScalar yR = SkScalarMul(v.fY, rect.fRight - edgeBegin->fX);
246 SkScalar xB = SkScalarMul(v.fX, rect.fBottom - edgeBegin->fY);
247 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
254 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
255 // This only handles non-degenerate convex paths currently.
256 if (kConvex_Convexity != this->getConvexity()) {
260 SkPathPriv::FirstDirection direction;
261 if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
270 SkDEBUGCODE(int moveCnt = 0;)
271 SkDEBUGCODE(int segmentCount = 0;)
272 SkDEBUGCODE(int closeCount = 0;)
274 while ((verb = iter.next(pts)) != kDone_Verb) {
278 SkASSERT(!segmentCount && !closeCount);
279 SkDEBUGCODE(++moveCnt);
280 firstPt = prevPt = pts[0];
284 SkASSERT(moveCnt && !closeCount);
285 SkDEBUGCODE(++segmentCount);
289 SkASSERT(moveCnt && !closeCount);
290 SkDEBUGCODE(++segmentCount);
294 SkASSERT(moveCnt && !closeCount);
295 SkDEBUGCODE(++segmentCount);
299 SkDEBUGCODE(++closeCount;)
302 SkDEBUGFAIL("unknown verb");
305 if (SkPath::kConic_Verb == verb) {
307 orig.set(pts, iter.conicWeight());
309 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
310 SkASSERT_RELEASE(2 == count);
312 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
315 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
319 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
323 prevPt = pts[nextPt];
327 return check_edge_against_rect(prevPt, firstPt, rect, direction);
330 uint32_t SkPath::getGenerationID() const {
331 uint32_t genID = fPathRef->genID();
332 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
333 SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt)));
334 genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt;
339 void SkPath::reset() {
340 SkDEBUGCODE(this->validate();)
342 fPathRef.reset(SkPathRef::CreateEmpty());
346 void SkPath::rewind() {
347 SkDEBUGCODE(this->validate();)
349 SkPathRef::Rewind(&fPathRef);
353 bool SkPath::isLastContourClosed() const {
354 int verbCount = fPathRef->countVerbs();
355 if (0 == verbCount) {
358 return kClose_Verb == fPathRef->atVerb(verbCount - 1);
361 bool SkPath::isLine(SkPoint line[2]) const {
362 int verbCount = fPathRef->countVerbs();
364 if (2 == verbCount) {
365 SkASSERT(kMove_Verb == fPathRef->atVerb(0));
366 if (kLine_Verb == fPathRef->atVerb(1)) {
367 SkASSERT(2 == fPathRef->countPoints());
369 const SkPoint* pts = fPathRef->points();
380 Determines if path is a rect by keeping track of changes in direction
381 and looking for a loop either clockwise or counterclockwise.
383 The direction is computed such that:
389 A rectangle cycles up/right/down/left or up/left/down/right.
392 The path is closed, and followed by a line.
393 A second move creates a new endpoint.
394 A diagonal line is parsed.
395 There's more than four changes of direction.
396 There's a discontinuity on the line (e.g., a move in the middle)
397 The line reverses direction.
398 The path contains a quadratic or cubic.
399 The path contains fewer than four points.
400 *The rectangle doesn't complete a cycle.
401 *The final point isn't equal to the first point.
403 *These last two conditions we relax if we have a 3-edge path that would
404 form a rectangle if it were closed (as we do when we fill a path)
406 It's OK if the path has:
407 Several colinear line segments composing a rectangle side.
408 Single points on the rectangle side.
410 The direction takes advantage of the corners found since opposite sides
411 must travel in opposite directions.
413 FIXME: Allow colinear quads and cubics to be treated like lines.
414 FIXME: If the API passes fill-only, return true if the filled stroke
415 is a rectangle, though the caller failed to close the path.
417 first,last,next direction state-machine:
418 0x1 is set if the segment is horizontal
419 0x2 is set if the segment is moving to the right or down
421 two directions are opposites iff (dirA ^ dirB) == 0x2
422 two directions are perpendicular iff (dirA ^ dirB) == 0x1
425 static int rect_make_dir(SkScalar dx, SkScalar dy) {
426 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
428 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
429 bool* isClosed, Direction* direction) const {
432 const SkPoint* pts = *ptsPtr;
433 const SkPoint* savePts = nullptr;
436 int firstDirection = 0;
437 int lastDirection = 0;
438 int nextDirection = 0;
439 bool closedOrMoved = false;
440 bool autoClose = false;
441 bool insertClose = false;
442 int verbCnt = fPathRef->countVerbs();
443 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
444 uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
452 SkScalar left = last.fX;
453 SkScalar top = last.fY;
454 SkScalar right = pts->fX;
455 SkScalar bottom = pts->fY;
457 if (left != right && top != bottom) {
458 return false; // diagonal
460 if (left == right && top == bottom) {
461 break; // single point on side OK
463 nextDirection = rect_make_dir(right - left, bottom - top);
465 firstDirection = nextDirection;
469 closedOrMoved = false;
473 return false; // closed followed by a line
475 if (autoClose && nextDirection == firstDirection) {
476 break; // colinear with first
478 closedOrMoved = autoClose;
479 if (lastDirection != nextDirection) {
481 return false; // too many direction changes
485 if (lastDirection == nextDirection) {
486 break; // colinear segment
488 // Possible values for corners are 2, 3, and 4.
489 // When corners == 3, nextDirection opposes firstDirection.
490 // Otherwise, nextDirection at corner 2 opposes corner 4.
491 int turn = firstDirection ^ (corners - 1);
492 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
493 if ((directionCycle ^ turn) != nextDirection) {
494 return false; // direction didn't follow cycle
501 return false; // quadratic, cubic not allowed
503 if (allowPartial && !autoClose && firstDirection) {
505 *currVerb -= 1; // try move again afterwards
506 goto addMissingClose;
509 closedOrMoved = true;
512 SkDEBUGFAIL("unexpected verb");
516 lastDirection = nextDirection;
520 // Success if 4 corners and first point equals last
521 bool result = 4 == corners && (first == last || autoClose);
523 // check if we are just an incomplete rectangle, in which case we can
524 // return true, but not claim to be closed.
527 // 4 sided but the last edge is not long enough to reach the start
529 SkScalar closeX = first.x() - last.x();
530 SkScalar closeY = first.y() - last.y();
531 if (closeX && closeY) {
532 return false; // we're diagonal, abort (can we ever reach this?)
534 int closeDirection = rect_make_dir(closeX, closeY);
535 // make sure the close-segment doesn't double-back on itself
536 if (3 == corners || (4 == corners && closeDirection == lastDirection)) {
538 autoClose = false; // we are not closed
544 if (result && isClosed) {
545 *isClosed = autoClose;
547 if (result && direction) {
548 *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
553 bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
554 SkDEBUGCODE(this->validate();)
556 const SkPoint* pts = fPathRef->points();
557 const SkPoint* first = pts;
558 if (!this->isRectContour(false, &currVerb, &pts, isClosed, direction)) {
562 int32_t num = SkToS32(pts - first);
564 rect->set(first, num);
566 // 'pts' isn't updated for open rects
567 *rect = this->getBounds();
573 bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
574 SkDEBUGCODE(this->validate();)
576 const SkPoint* pts = fPathRef->points();
577 const SkPoint* first = pts;
578 Direction testDirs[2];
579 if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0])) {
582 const SkPoint* last = pts;
585 if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) {
586 testRects[0].set(first, SkToS32(last - first));
588 pts = fPathRef->points() + fPathRef->countPoints();
590 testRects[1].set(last, SkToS32(pts - last));
591 if (testRects[0].contains(testRects[1])) {
593 rects[0] = testRects[0];
594 rects[1] = testRects[1];
597 dirs[0] = testDirs[0];
598 dirs[1] = testDirs[1];
602 if (testRects[1].contains(testRects[0])) {
604 rects[0] = testRects[1];
605 rects[1] = testRects[0];
608 dirs[0] = testDirs[1];
609 dirs[1] = testDirs[0];
617 int SkPath::countPoints() const {
618 return fPathRef->countPoints();
621 int SkPath::getPoints(SkPoint dst[], int max) const {
622 SkDEBUGCODE(this->validate();)
625 SkASSERT(!max || dst);
626 int count = SkMin32(max, fPathRef->countPoints());
627 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
628 return fPathRef->countPoints();
631 SkPoint SkPath::getPoint(int index) const {
632 if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
633 return fPathRef->atPoint(index);
635 return SkPoint::Make(0, 0);
638 int SkPath::countVerbs() const {
639 return fPathRef->countVerbs();
642 static inline void copy_verbs_reverse(uint8_t* inorderDst,
643 const uint8_t* reversedSrc,
645 for (int i = 0; i < count; ++i) {
646 inorderDst[i] = reversedSrc[~i];
650 int SkPath::getVerbs(uint8_t dst[], int max) const {
651 SkDEBUGCODE(this->validate();)
654 SkASSERT(!max || dst);
655 int count = SkMin32(max, fPathRef->countVerbs());
656 copy_verbs_reverse(dst, fPathRef->verbs(), count);
657 return fPathRef->countVerbs();
660 bool SkPath::getLastPt(SkPoint* lastPt) const {
661 SkDEBUGCODE(this->validate();)
663 int count = fPathRef->countPoints();
666 *lastPt = fPathRef->atPoint(count - 1);
676 void SkPath::setPt(int index, SkScalar x, SkScalar y) {
677 SkDEBUGCODE(this->validate();)
679 int count = fPathRef->countPoints();
680 if (count <= index) {
683 SkPathRef::Editor ed(&fPathRef);
684 ed.atPoint(index)->set(x, y);
688 void SkPath::setLastPt(SkScalar x, SkScalar y) {
689 SkDEBUGCODE(this->validate();)
691 int count = fPathRef->countPoints();
695 SkPathRef::Editor ed(&fPathRef);
696 ed.atPoint(count-1)->set(x, y);
700 void SkPath::setConvexity(Convexity c) {
701 if (fConvexity != c) {
706 //////////////////////////////////////////////////////////////////////////////
707 // Construction methods
709 #define DIRTY_AFTER_EDIT \
711 fConvexity = kUnknown_Convexity; \
712 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; \
715 void SkPath::incReserve(U16CPU inc) {
716 SkDEBUGCODE(this->validate();)
717 SkPathRef::Editor(&fPathRef, inc, inc);
718 SkDEBUGCODE(this->validate();)
721 void SkPath::moveTo(SkScalar x, SkScalar y) {
722 SkDEBUGCODE(this->validate();)
724 SkPathRef::Editor ed(&fPathRef);
726 // remember our index
727 fLastMoveToIndex = fPathRef->countPoints();
729 ed.growForVerb(kMove_Verb)->set(x, y);
734 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
736 this->getLastPt(&pt);
737 this->moveTo(pt.fX + x, pt.fY + y);
740 void SkPath::injectMoveToIfNeeded() {
741 if (fLastMoveToIndex < 0) {
743 if (fPathRef->countVerbs() == 0) {
746 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
754 void SkPath::lineTo(SkScalar x, SkScalar y) {
755 SkDEBUGCODE(this->validate();)
757 this->injectMoveToIfNeeded();
759 SkPathRef::Editor ed(&fPathRef);
760 ed.growForVerb(kLine_Verb)->set(x, y);
765 void SkPath::rLineTo(SkScalar x, SkScalar y) {
766 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
768 this->getLastPt(&pt);
769 this->lineTo(pt.fX + x, pt.fY + y);
772 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
773 SkDEBUGCODE(this->validate();)
775 this->injectMoveToIfNeeded();
777 SkPathRef::Editor ed(&fPathRef);
778 SkPoint* pts = ed.growForVerb(kQuad_Verb);
785 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
786 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
788 this->getLastPt(&pt);
789 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
792 void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
794 // check for <= 0 or NaN with this test
796 this->lineTo(x2, y2);
797 } else if (!SkScalarIsFinite(w)) {
798 this->lineTo(x1, y1);
799 this->lineTo(x2, y2);
800 } else if (SK_Scalar1 == w) {
801 this->quadTo(x1, y1, x2, y2);
803 SkDEBUGCODE(this->validate();)
805 this->injectMoveToIfNeeded();
807 SkPathRef::Editor ed(&fPathRef);
808 SkPoint* pts = ed.growForVerb(kConic_Verb, w);
816 void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
818 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
820 this->getLastPt(&pt);
821 this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
824 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
825 SkScalar x3, SkScalar y3) {
826 SkDEBUGCODE(this->validate();)
828 this->injectMoveToIfNeeded();
830 SkPathRef::Editor ed(&fPathRef);
831 SkPoint* pts = ed.growForVerb(kCubic_Verb);
839 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
840 SkScalar x3, SkScalar y3) {
841 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
843 this->getLastPt(&pt);
844 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
845 pt.fX + x3, pt.fY + y3);
848 void SkPath::close() {
849 SkDEBUGCODE(this->validate();)
851 int count = fPathRef->countVerbs();
853 switch (fPathRef->atVerb(count - 1)) {
859 SkPathRef::Editor ed(&fPathRef);
860 ed.growForVerb(kClose_Verb);
864 // don't add a close if it's the first verb or a repeat
867 SkDEBUGFAIL("unexpected verb");
872 // signal that we need a moveTo to follow us (unless we're done)
874 if (fLastMoveToIndex >= 0) {
875 fLastMoveToIndex = ~fLastMoveToIndex;
878 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
882 ///////////////////////////////////////////////////////////////////////////////
886 template <unsigned N>
887 class PointIterator {
889 PointIterator(SkPath::Direction dir, unsigned startIndex)
890 : fCurrent(startIndex % N)
891 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
893 const SkPoint& current() const {
894 SkASSERT(fCurrent < N);
895 return fPts[fCurrent];
898 const SkPoint& next() {
899 fCurrent = (fCurrent + fAdvance) % N;
900 return this->current();
911 class RectPointIterator : public PointIterator<4> {
913 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
914 : PointIterator(dir, startIndex) {
916 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
917 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
918 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
919 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
923 class OvalPointIterator : public PointIterator<4> {
925 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
926 : PointIterator(dir, startIndex) {
928 const SkScalar cx = oval.centerX();
929 const SkScalar cy = oval.centerY();
931 fPts[0] = SkPoint::Make(cx, oval.fTop);
932 fPts[1] = SkPoint::Make(oval.fRight, cy);
933 fPts[2] = SkPoint::Make(cx, oval.fBottom);
934 fPts[3] = SkPoint::Make(oval.fLeft, cy);
938 class RRectPointIterator : public PointIterator<8> {
940 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
941 : PointIterator(dir, startIndex) {
943 const SkRect& bounds = rrect.getBounds();
944 const SkScalar L = bounds.fLeft;
945 const SkScalar T = bounds.fTop;
946 const SkScalar R = bounds.fRight;
947 const SkScalar B = bounds.fBottom;
949 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
950 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
951 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
952 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
953 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
954 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
955 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
956 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
960 } // anonymous namespace
962 static void assert_known_direction(int dir) {
963 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
966 void SkPath::addRect(const SkRect& rect, Direction dir) {
967 this->addRect(rect, dir, 0);
970 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
971 SkScalar bottom, Direction dir) {
972 this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
975 void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
976 assert_known_direction(dir);
977 fFirstDirection = this->hasOnlyMoveTos() ?
978 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
979 SkAutoDisableDirectionCheck addc(this);
980 SkAutoPathBoundsUpdate apbu(this, rect);
982 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
984 const int kVerbs = 5; // moveTo + 3x lineTo + close
985 this->incReserve(kVerbs);
987 RectPointIterator iter(rect, dir, startIndex);
989 this->moveTo(iter.current());
990 this->lineTo(iter.next());
991 this->lineTo(iter.next());
992 this->lineTo(iter.next());
995 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
998 void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
999 SkDEBUGCODE(this->validate();)
1004 fLastMoveToIndex = fPathRef->countPoints();
1006 // +close makes room for the extra kClose_Verb
1007 SkPathRef::Editor ed(&fPathRef, count+close, count);
1009 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
1011 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
1012 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
1016 ed.growForVerb(kClose_Verb);
1017 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1021 SkDEBUGCODE(this->validate();)
1024 #include "SkGeometry.h"
1026 static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1028 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
1029 // Chrome uses this path to move into and out of ovals. If not
1030 // treated as a special case the moves can distort the oval's
1031 // bounding box (and break the circle special case).
1032 pt->set(oval.fRight, oval.centerY());
1034 } else if (0 == oval.width() && 0 == oval.height()) {
1035 // Chrome will sometimes create 0 radius round rects. Having degenerate
1036 // quad segments in the path prevents the path from being recognized as
1038 // TODO: optimizing the case where only one of width or height is zero
1039 // should also be considered. This case, however, doesn't seem to be
1040 // as common as the single point case.
1041 pt->set(oval.fRight, oval.fTop);
1047 // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
1049 static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
1050 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
1051 startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX);
1052 stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX);
1054 /* If the sweep angle is nearly (but less than) 360, then due to precision
1055 loss in radians-conversion and/or sin/cos, we may end up with coincident
1056 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
1057 of drawing a nearly complete circle (good).
1058 e.g. canvas.drawArc(0, 359.99, ...)
1059 -vs- canvas.drawArc(0, 359.9, ...)
1060 We try to detect this edge case, and tweak the stop vector
1062 if (*startV == *stopV) {
1063 SkScalar sw = SkScalarAbs(sweepAngle);
1064 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
1065 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
1066 // make a guess at a tiny angle (in radians) to tweak by
1067 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
1068 // not sure how much will be enough, so we use a loop
1070 stopRad -= deltaRad;
1071 stopV->fY = SkScalarSinCos(stopRad, &stopV->fX);
1072 } while (*startV == *stopV);
1075 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
1079 * If this returns 0, then the caller should just line-to the singlePt, else it should
1080 * ignore singlePt and append the specified number of conics.
1082 static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
1083 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
1084 SkPoint* singlePt) {
1087 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
1088 matrix.postTranslate(oval.centerX(), oval.centerY());
1090 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
1092 matrix.mapXY(start.x(), start.y(), singlePt);
1097 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
1100 rrect.setRectRadii(rect, (const SkVector*) radii);
1101 this->addRRect(rrect, dir);
1104 void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
1105 // legacy start indices: 6 (CW) and 7(CCW)
1106 this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1109 void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
1110 assert_known_direction(dir);
1112 if (rrect.isEmpty()) {
1116 bool isRRect = hasOnlyMoveTos();
1117 const SkRect& bounds = rrect.getBounds();
1119 if (rrect.isRect()) {
1120 // degenerate(rect) => radii points are collapsing
1121 this->addRect(bounds, dir, (startIndex + 1) / 2);
1122 } else if (rrect.isOval()) {
1123 // degenerate(oval) => line points are collapsing
1124 this->addOval(bounds, dir, startIndex / 2);
1126 fFirstDirection = this->hasOnlyMoveTos() ?
1127 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
1129 SkAutoPathBoundsUpdate apbu(this, bounds);
1130 SkAutoDisableDirectionCheck addc(this);
1132 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
1133 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
1134 const SkScalar weight = SK_ScalarRoot2Over2;
1136 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1137 const int kVerbs = startsWithConic
1138 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
1139 : 10; // moveTo + 4x lineTo + 4x conicTo + close
1140 this->incReserve(kVerbs);
1142 RRectPointIterator rrectIter(rrect, dir, startIndex);
1143 // Corner iterator indices follow the collapsed radii model,
1144 // adjusted such that the start pt is "behind" the radii start pt.
1145 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
1146 RectPointIterator rectIter(bounds, dir, rectStartIndex);
1148 this->moveTo(rrectIter.current());
1149 if (startsWithConic) {
1150 for (unsigned i = 0; i < 3; ++i) {
1151 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1152 this->lineTo(rrectIter.next());
1154 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1155 // final lineTo handled by close().
1157 for (unsigned i = 0; i < 4; ++i) {
1158 this->lineTo(rrectIter.next());
1159 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1164 SkPathRef::Editor ed(&fPathRef);
1165 ed.setIsRRect(isRRect, dir, startIndex % 8);
1167 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1170 SkDEBUGCODE(fPathRef->validate();)
1173 bool SkPath::hasOnlyMoveTos() const {
1174 int count = fPathRef->countVerbs();
1175 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
1176 for (int i = 0; i < count; ++i) {
1177 if (*verbs == kLine_Verb ||
1178 *verbs == kQuad_Verb ||
1179 *verbs == kConic_Verb ||
1180 *verbs == kCubic_Verb) {
1188 bool SkPath::isZeroLength() const {
1189 int count = fPathRef->countPoints();
1193 const SkPoint* pts = fPathRef.get()->points();
1194 const SkPoint& first = *pts;
1195 for (int index = 1; index < count; ++index) {
1196 if (first != pts[index]) {
1203 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1205 assert_known_direction(dir);
1207 if (rx < 0 || ry < 0) {
1208 SkErrorInternals::SetError( kInvalidArgument_SkError,
1209 "I got %f and %f as radii to SkPath::AddRoundRect, "
1210 "but negative radii are not allowed.",
1211 SkScalarToDouble(rx), SkScalarToDouble(ry) );
1216 rrect.setRectXY(rect, rx, ry);
1217 this->addRRect(rrect, dir);
1220 void SkPath::addOval(const SkRect& oval, Direction dir) {
1221 // legacy start index: 1
1222 this->addOval(oval, dir, 1);
1225 void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
1226 assert_known_direction(dir);
1228 /* If addOval() is called after previous moveTo(),
1229 this path is still marked as an oval. This is used to
1230 fit into WebKit's calling sequences.
1231 We can't simply check isEmpty() in this case, as additional
1232 moveTo() would mark the path non empty.
1234 bool isOval = hasOnlyMoveTos();
1236 fFirstDirection = (SkPathPriv::FirstDirection)dir;
1238 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1241 SkAutoDisableDirectionCheck addc(this);
1242 SkAutoPathBoundsUpdate apbu(this, oval);
1244 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1245 const int kVerbs = 6; // moveTo + 4x conicTo + close
1246 this->incReserve(kVerbs);
1248 OvalPointIterator ovalIter(oval, dir, startPointIndex);
1249 // The corner iterator pts are tracking "behind" the oval/radii pts.
1250 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
1251 const SkScalar weight = SK_ScalarRoot2Over2;
1253 this->moveTo(ovalIter.current());
1254 for (unsigned i = 0; i < 4; ++i) {
1255 this->conicTo(rectIter.next(), ovalIter.next(), weight);
1259 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1261 SkPathRef::Editor ed(&fPathRef);
1263 ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
1266 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
1268 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1272 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1274 if (oval.width() < 0 || oval.height() < 0) {
1278 if (fPathRef->countVerbs() == 0) {
1283 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1284 forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1288 SkVector startV, stopV;
1289 SkRotationDirection dir;
1290 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1293 SkConic conics[SkConic::kMaxConicsForArc];
1294 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1296 this->incReserve(count * 2 + 1);
1297 const SkPoint& pt = conics[0].fPts[0];
1298 forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
1299 for (int i = 0; i < count; ++i) {
1300 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1303 forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
1307 // This converts the SVG arc to conics.
1308 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1309 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1310 // See also SVG implementation notes:
1311 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1312 // Note that arcSweep bool value is flipped from the original implementation.
1313 void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1314 SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
1315 this->injectMoveToIfNeeded();
1317 this->getLastPt(&srcPts[0]);
1318 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1319 // joining the endpoints.
1320 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1325 // If the current point and target point for the arc are identical, it should be treated as a
1326 // zero length path. This ensures continuity in animations.
1327 srcPts[1].set(x, y);
1328 if (srcPts[0] == srcPts[1]) {
1332 rx = SkScalarAbs(rx);
1333 ry = SkScalarAbs(ry);
1334 SkVector midPointDistance = srcPts[0] - srcPts[1];
1335 midPointDistance *= 0.5f;
1337 SkMatrix pointTransform;
1338 pointTransform.setRotate(-angle);
1340 SkPoint transformedMidPoint;
1341 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1342 SkScalar squareRx = rx * rx;
1343 SkScalar squareRy = ry * ry;
1344 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1345 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1347 // Check if the radii are big enough to draw the arc, scale radii if not.
1348 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1349 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1350 if (radiiScale > 1) {
1351 radiiScale = SkScalarSqrt(radiiScale);
1356 pointTransform.setScale(1 / rx, 1 / ry);
1357 pointTransform.preRotate(-angle);
1360 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
1361 SkVector delta = unitPts[1] - unitPts[0];
1363 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1364 SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
1366 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1367 if (SkToBool(arcSweep) != SkToBool(arcLarge)) { // flipped from the original implementation
1368 scaleFactor = -scaleFactor;
1370 delta.scale(scaleFactor);
1371 SkPoint centerPoint = unitPts[0] + unitPts[1];
1372 centerPoint *= 0.5f;
1373 centerPoint.offset(-delta.fY, delta.fX);
1374 unitPts[0] -= centerPoint;
1375 unitPts[1] -= centerPoint;
1376 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1377 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1378 SkScalar thetaArc = theta2 - theta1;
1379 if (thetaArc < 0 && !arcSweep) { // arcSweep flipped from the original implementation
1380 thetaArc += SK_ScalarPI * 2;
1381 } else if (thetaArc > 0 && arcSweep) { // arcSweep flipped from the original implementation
1382 thetaArc -= SK_ScalarPI * 2;
1384 pointTransform.setRotate(angle);
1385 pointTransform.preScale(rx, ry);
1387 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
1388 SkScalar thetaWidth = thetaArc / segments;
1389 SkScalar t = SkScalarTan(0.5f * thetaWidth);
1390 if (!SkScalarIsFinite(t)) {
1393 SkScalar startTheta = theta1;
1394 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1395 for (int i = 0; i < segments; ++i) {
1396 SkScalar endTheta = startTheta + thetaWidth;
1397 SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
1399 unitPts[1].set(cosEndTheta, sinEndTheta);
1400 unitPts[1] += centerPoint;
1401 unitPts[0] = unitPts[1];
1402 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1404 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
1405 this->conicTo(mapped[0], mapped[1], w);
1406 startTheta = endTheta;
1410 void SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1411 SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
1412 SkPoint currentPoint;
1413 this->getLastPt(¤tPoint);
1414 this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy);
1417 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1418 if (oval.isEmpty() || 0 == sweepAngle) {
1422 const SkScalar kFullCircleAngle = SkIntToScalar(360);
1424 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1425 // We can treat the arc as an oval if it begins at one of our legal starting positions.
1426 // See SkPath::addOval() docs.
1427 SkScalar startOver90 = startAngle / 90.f;
1428 SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
1429 SkScalar error = startOver90 - startOver90I;
1430 if (SkScalarNearlyEqual(error, 0)) {
1431 // Index 1 is at startAngle == 0.
1432 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
1433 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
1434 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
1435 (unsigned) startIndex);
1439 this->arcTo(oval, startAngle, sweepAngle, true);
1443 Need to handle the case when the angle is sharp, and our computed end-points
1444 for the arc go behind pt1 and/or p2...
1446 void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1448 this->lineTo(x1, y1);
1452 SkVector before, after;
1454 // need to know our prev pt so we can construct tangent vectors
1457 this->getLastPt(&start);
1458 // Handle degenerate cases by adding a line to the first point and
1460 before.setNormalize(x1 - start.fX, y1 - start.fY);
1461 after.setNormalize(x2 - x1, y2 - y1);
1464 SkScalar cosh = SkPoint::DotProduct(before, after);
1465 SkScalar sinh = SkPoint::CrossProduct(before, after);
1467 if (SkScalarNearlyZero(sinh)) { // angle is too tight
1468 this->lineTo(x1, y1);
1472 SkScalar dist = SkScalarAbs(SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh));
1474 SkScalar xx = x1 - SkScalarMul(dist, before.fX);
1475 SkScalar yy = y1 - SkScalarMul(dist, before.fY);
1476 after.setLength(dist);
1477 this->lineTo(xx, yy);
1478 SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
1479 this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1482 ///////////////////////////////////////////////////////////////////////////////
1484 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1487 matrix.setTranslate(dx, dy);
1488 this->addPath(path, matrix, mode);
1491 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) {
1492 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
1498 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
1499 bool firstVerb = true;
1500 while ((verb = iter.next(pts)) != kDone_Verb) {
1503 proc(matrix, &pts[0], &pts[0], 1);
1504 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1505 injectMoveToIfNeeded(); // In case last contour is closed
1506 this->lineTo(pts[0]);
1508 this->moveTo(pts[0]);
1512 proc(matrix, &pts[1], &pts[1], 1);
1513 this->lineTo(pts[1]);
1516 proc(matrix, &pts[1], &pts[1], 2);
1517 this->quadTo(pts[1], pts[2]);
1520 proc(matrix, &pts[1], &pts[1], 2);
1521 this->conicTo(pts[1], pts[2], iter.conicWeight());
1524 proc(matrix, &pts[1], &pts[1], 3);
1525 this->cubicTo(pts[1], pts[2], pts[3]);
1531 SkDEBUGFAIL("unknown verb");
1537 ///////////////////////////////////////////////////////////////////////////////
1539 static int pts_in_verb(unsigned verb) {
1540 static const uint8_t gPtsInVerb[] = {
1550 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1551 return gPtsInVerb[verb];
1554 // ignore the last point of the 1st contour
1555 void SkPath::reversePathTo(const SkPath& path) {
1556 int i, vcount = path.fPathRef->countVerbs();
1557 // exit early if the path is empty, or just has a moveTo.
1562 SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
1564 const uint8_t* verbs = path.fPathRef->verbs();
1565 const SkPoint* pts = path.fPathRef->points();
1566 const SkScalar* conicWeights = path.fPathRef->conicWeights();
1568 SkASSERT(verbs[~0] == kMove_Verb);
1569 for (i = 1; i < vcount; ++i) {
1570 unsigned v = verbs[~i];
1571 int n = pts_in_verb(v);
1576 conicWeights += (SkPath::kConic_Verb == v);
1580 switch (verbs[~i]) {
1582 this->lineTo(pts[-1].fX, pts[-1].fY);
1585 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
1588 this->conicTo(pts[-1], pts[-2], *--conicWeights);
1591 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
1592 pts[-3].fX, pts[-3].fY);
1595 SkDEBUGFAIL("bad verb");
1598 pts -= pts_in_verb(verbs[~i]);
1602 void SkPath::reverseAddPath(const SkPath& src) {
1603 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
1605 const SkPoint* pts = src.fPathRef->pointsEnd();
1606 // we will iterator through src's verbs backwards
1607 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
1608 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
1609 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
1611 bool needMove = true;
1612 bool needClose = false;
1613 while (verbs < verbsEnd) {
1614 uint8_t v = *(verbs++);
1615 int n = pts_in_verb(v);
1619 this->moveTo(pts->fX, pts->fY);
1630 pts += 1; // so we see the point in "if (needMove)" above
1633 this->lineTo(pts[0]);
1636 this->quadTo(pts[1], pts[0]);
1639 this->conicTo(pts[1], pts[0], *--conicWeights);
1642 this->cubicTo(pts[2], pts[1], pts[0]);
1648 SkDEBUGFAIL("unexpected verb");
1653 ///////////////////////////////////////////////////////////////////////////////
1655 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1658 matrix.setTranslate(dx, dy);
1659 this->transform(matrix, dst);
1662 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1667 SkChopCubicAtHalf(pts, tmp);
1668 subdivide_cubic_to(path, &tmp[0], level);
1669 subdivide_cubic_to(path, &tmp[3], level);
1671 path->cubicTo(pts[1], pts[2], pts[3]);
1675 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
1676 SkDEBUGCODE(this->validate();)
1677 if (dst == nullptr) {
1678 dst = (SkPath*)this;
1681 if (matrix.hasPerspective()) {
1683 tmp.fFillType = fFillType;
1685 SkPath::Iter iter(*this, false);
1689 while ((verb = iter.next(pts, false)) != kDone_Verb) {
1698 // promote the quad to a conic
1699 tmp.conicTo(pts[1], pts[2],
1700 SkConic::TransformW(pts, SK_Scalar1, matrix));
1703 tmp.conicTo(pts[1], pts[2],
1704 SkConic::TransformW(pts, iter.conicWeight(), matrix));
1707 subdivide_cubic_to(&tmp, pts);
1713 SkDEBUGFAIL("unknown verb");
1719 SkPathRef::Editor ed(&dst->fPathRef);
1720 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
1721 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1723 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
1726 dst->fFillType = fFillType;
1727 dst->fConvexity = fConvexity;
1728 dst->fIsVolatile = fIsVolatile;
1731 if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
1732 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1735 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
1736 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
1738 dst->fFirstDirection = SkPathPriv::OppositeFirstDirection(
1739 (SkPathPriv::FirstDirection)fFirstDirection.load());
1740 } else if (det2x2 > 0) {
1741 dst->fFirstDirection = fFirstDirection.load();
1743 dst->fConvexity = kUnknown_Convexity;
1744 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1748 SkDEBUGCODE(dst->validate();)
1752 ///////////////////////////////////////////////////////////////////////////////
1753 ///////////////////////////////////////////////////////////////////////////////
1756 kEmptyContour_SegmentState, // The current contour is empty. We may be
1757 // starting processing or we may have just
1758 // closed a contour.
1759 kAfterMove_SegmentState, // We have seen a move, but nothing else.
1760 kAfterPrimitive_SegmentState // We have seen a primitive but not yet
1761 // closed the path. Also the initial state.
1764 SkPath::Iter::Iter() {
1767 fConicWeights = nullptr;
1768 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1769 fForceClose = fCloseLine = false;
1770 fSegmentState = kEmptyContour_SegmentState;
1772 // need to init enough to make next() harmlessly return kDone_Verb
1774 fVerbStop = nullptr;
1778 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1779 this->setPath(path, forceClose);
1782 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1783 fPts = path.fPathRef->points();
1784 fVerbs = path.fPathRef->verbs();
1785 fVerbStop = path.fPathRef->verbsMemBegin();
1786 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
1787 fLastPt.fX = fLastPt.fY = 0;
1788 fMoveTo.fX = fMoveTo.fY = 0;
1789 fForceClose = SkToU8(forceClose);
1791 fSegmentState = kEmptyContour_SegmentState;
1794 bool SkPath::Iter::isClosedContour() const {
1795 if (fVerbs == nullptr || fVerbs == fVerbStop) {
1802 const uint8_t* verbs = fVerbs;
1803 const uint8_t* stop = fVerbStop;
1805 if (kMove_Verb == *(verbs - 1)) {
1806 verbs -= 1; // skip the initial moveto
1809 while (verbs > stop) {
1810 // verbs points one beyond the current verb, decrement first.
1811 unsigned v = *(--verbs);
1812 if (kMove_Verb == v) {
1815 if (kClose_Verb == v) {
1822 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1824 if (fLastPt != fMoveTo) {
1825 // A special case: if both points are NaN, SkPoint::operation== returns
1826 // false, but the iterator expects that they are treated as the same.
1827 // (consider SkPoint is a 2-dimension float point).
1828 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1829 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1844 const SkPoint& SkPath::Iter::cons_moveTo() {
1845 if (fSegmentState == kAfterMove_SegmentState) {
1846 // Set the first return pt to the move pt
1847 fSegmentState = kAfterPrimitive_SegmentState;
1850 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
1851 // Set the first return pt to the last pt of the previous primitive.
1856 void SkPath::Iter::consumeDegenerateSegments(bool exact) {
1857 // We need to step over anything that will not move the current draw point
1858 // forward before the next move is seen
1859 const uint8_t* lastMoveVerb = 0;
1860 const SkPoint* lastMovePt = 0;
1861 const SkScalar* lastMoveWeight = nullptr;
1862 SkPoint lastPt = fLastPt;
1863 while (fVerbs != fVerbStop) {
1864 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
1867 // Keep a record of this most recent move
1868 lastMoveVerb = fVerbs;
1870 lastMoveWeight = fConicWeights;
1877 // A close when we are in a segment is always valid except when it
1878 // follows a move which follows a segment.
1879 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
1882 // A close at any other time must be ignored
1887 if (!IsLineDegenerate(lastPt, fPts[0], exact)) {
1889 fVerbs = lastMoveVerb;
1891 fConicWeights = lastMoveWeight;
1896 // Ignore this line and continue
1903 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) {
1905 fVerbs = lastMoveVerb;
1907 fConicWeights = lastMoveWeight;
1912 // Ignore this line and continue
1915 fConicWeights += (kConic_Verb == verb);
1919 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) {
1921 fVerbs = lastMoveVerb;
1923 fConicWeights = lastMoveWeight;
1928 // Ignore this line and continue
1934 SkDEBUGFAIL("Should never see kDone_Verb");
1939 SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
1942 if (fVerbs == fVerbStop) {
1943 // Close the curve if requested and if there is some curve to close
1944 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
1945 if (kLine_Verb == this->autoClose(ptsParam)) {
1954 // fVerbs is one beyond the current verb, decrement first
1955 unsigned verb = *(--fVerbs);
1956 const SkPoint* SK_RESTRICT srcPts = fPts;
1957 SkPoint* SK_RESTRICT pts = ptsParam;
1962 fVerbs++; // move back one verb
1963 verb = this->autoClose(pts);
1964 if (verb == kClose_Verb) {
1969 if (fVerbs == fVerbStop) { // might be a trailing moveto
1975 fSegmentState = kAfterMove_SegmentState;
1977 fNeedClose = fForceClose;
1980 pts[0] = this->cons_moveTo();
1982 fLastPt = srcPts[0];
1990 pts[0] = this->cons_moveTo();
1991 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1992 fLastPt = srcPts[1];
1996 pts[0] = this->cons_moveTo();
1997 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1998 fLastPt = srcPts[2];
2002 verb = this->autoClose(pts);
2003 if (verb == kLine_Verb) {
2004 fVerbs++; // move back one verb
2007 fSegmentState = kEmptyContour_SegmentState;
2016 ///////////////////////////////////////////////////////////////////////////////
2019 Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
2022 size_t SkPath::writeToMemory(void* storage) const {
2023 SkDEBUGCODE(this->validate();)
2025 if (nullptr == storage) {
2026 const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
2027 return SkAlign4(byteCount);
2030 SkWBuffer buffer(storage);
2032 int32_t packed = (fConvexity << kConvexity_SerializationShift) |
2033 (fFillType << kFillType_SerializationShift) |
2034 (fFirstDirection << kDirection_SerializationShift) |
2035 (fIsVolatile << kIsVolatile_SerializationShift) |
2038 buffer.write32(packed);
2039 buffer.write32(fLastMoveToIndex);
2041 fPathRef->writeToBuffer(&buffer);
2043 buffer.padToAlign4();
2044 return buffer.pos();
2047 size_t SkPath::readFromMemory(const void* storage, size_t length) {
2048 SkRBufferWithSizeCheck buffer(storage, length);
2051 if (!buffer.readS32(&packed)) {
2055 unsigned version = packed & 0xFF;
2056 if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
2060 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
2061 fFillType = (packed >> kFillType_SerializationShift) & 0x3;
2062 uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
2063 fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
2064 SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
2069 fPathRef.reset(pathRef);
2070 SkDEBUGCODE(this->validate();)
2071 buffer.skipToAlign4();
2073 // compatibility check
2074 if (version < kPathPrivFirstDirection_Version) {
2075 switch (dir) { // old values
2077 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2080 fFirstDirection = SkPathPriv::kCW_FirstDirection;
2083 fFirstDirection = SkPathPriv::kCCW_FirstDirection;
2089 fFirstDirection = dir;
2092 return buffer.pos();
2095 ///////////////////////////////////////////////////////////////////////////////
2097 #include "SkStringUtils.h"
2098 #include "SkStream.h"
2100 static void append_params(SkString* str, const char label[], const SkPoint pts[],
2101 int count, SkScalarAsStringType strType, SkScalar conicWeight = -1) {
2105 const SkScalar* values = &pts[0].fX;
2108 for (int i = 0; i < count; ++i) {
2109 SkAppendScalar(str, values[i], strType);
2110 if (i < count - 1) {
2114 if (conicWeight >= 0) {
2116 SkAppendScalar(str, conicWeight, strType);
2119 if (kHex_SkScalarAsStringType == strType) {
2120 str->append(" // ");
2121 for (int i = 0; i < count; ++i) {
2122 SkAppendScalarDec(str, values[i]);
2123 if (i < count - 1) {
2127 if (conicWeight >= 0) {
2129 SkAppendScalarDec(str, conicWeight);
2135 void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
2136 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
2137 Iter iter(*this, forceClose);
2142 SkDebugf("path: forceClose=%s\n", forceClose ? "true" : "false");
2146 while ((verb = iter.next(pts, false)) != kDone_Verb) {
2149 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
2152 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
2155 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
2158 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2161 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
2164 builder.append("path.close();\n");
2167 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
2168 verb = kDone_Verb; // stop the loop
2171 if (!wStream && builder.size()) {
2172 SkDebugf("%s", builder.c_str());
2177 wStream->writeText(builder.c_str());
2181 void SkPath::dump() const {
2182 this->dump(nullptr, false, false);
2185 void SkPath::dumpHex() const {
2186 this->dump(nullptr, false, true);
2190 void SkPath::validate() const {
2191 SkASSERT((fFillType & ~3) == 0);
2193 #ifdef SK_DEBUG_PATH
2194 if (!fBoundsIsDirty) {
2197 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2198 SkASSERT(SkToBool(fIsFinite) == isFinite);
2200 if (fPathRef->countPoints() <= 1) {
2201 // if we're empty, fBounds may be empty but translated, so we can't
2202 // necessarily compare to bounds directly
2203 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2205 SkASSERT(bounds.isEmpty());
2206 SkASSERT(fBounds.isEmpty());
2208 if (bounds.isEmpty()) {
2209 SkASSERT(fBounds.isEmpty());
2211 if (!fBounds.isEmpty()) {
2212 SkASSERT(fBounds.contains(bounds));
2217 #endif // SK_DEBUG_PATH
2221 ///////////////////////////////////////////////////////////////////////////////
2223 static int sign(SkScalar x) { return x < 0; }
2224 #define kValueNeverReturnedBySign 2
2229 kStraight_DirChange,
2230 kBackwards_DirChange,
2236 static bool almost_equal(SkScalar compA, SkScalar compB) {
2237 // The error epsilon was empirically derived; worse case round rects
2238 // with a mid point outset by 2x float epsilon in tests had an error
2240 const int epsilon = 16;
2241 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2244 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2245 int aBits = SkFloatAs2sCompliment(compA);
2246 int bBits = SkFloatAs2sCompliment(compB);
2247 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2250 static bool approximately_zero_when_compared_to(double x, double y) {
2251 return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
2255 // only valid for a single contour
2256 struct Convexicator {
2259 , fConvexity(SkPath::kConvex_Convexity)
2260 , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
2263 fExpectedDir = kInvalid_DirChange;
2269 fFirstVec.set(0, 0);
2272 fSx = fSy = kValueNeverReturnedBySign;
2275 SkPath::Convexity getConvexity() const { return fConvexity; }
2277 /** The direction returned is only valid if the path is determined convex */
2278 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2280 void addPt(const SkPoint& pt) {
2281 if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
2285 if (0 == fPtCount) {
2289 SkVector vec = pt - fCurrPt;
2290 SkScalar lengthSqd = vec.lengthSqd();
2291 if (!SkScalarIsFinite(lengthSqd)) {
2293 } else if (lengthSqd) {
2297 if (++fPtCount == 2) {
2298 fFirstVec = fLastVec = vec;
2300 SkASSERT(fPtCount > 2);
2304 int sx = sign(vec.fX);
2305 int sy = sign(vec.fY);
2311 if (fDx > 3 || fDy > 3) {
2312 fConvexity = SkPath::kConcave_Convexity;
2320 this->addVec(fFirstVec);
2324 DirChange directionChange(const SkVector& curVec) {
2325 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2327 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2328 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2329 largest = SkTMax(largest, -smallest);
2331 if (!almost_equal(largest, largest + cross)) {
2332 int sign = SkScalarSignAsInt(cross);
2334 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2339 double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
2340 double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
2341 double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
2342 double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
2343 double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
2344 if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
2345 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
2347 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2352 if (!SkScalarNearlyZero(fLastVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2353 !SkScalarNearlyZero(curVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2354 fLastVec.dot(curVec) < 0.0f) {
2355 return kBackwards_DirChange;
2358 return kStraight_DirChange;
2362 bool isFinite() const {
2366 void setCurve(bool isCurve) {
2371 void addVec(const SkVector& vec) {
2372 SkASSERT(vec.fX || vec.fY);
2373 DirChange dir = this->directionChange(vec);
2375 case kLeft_DirChange: // fall through
2376 case kRight_DirChange:
2377 if (kInvalid_DirChange == fExpectedDir) {
2379 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2380 : SkPathPriv::kCCW_FirstDirection;
2381 } else if (dir != fExpectedDir) {
2382 fConvexity = SkPath::kConcave_Convexity;
2383 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2387 case kStraight_DirChange:
2389 case kBackwards_DirChange:
2391 fConvexity = SkPath::kConcave_Convexity;
2392 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2396 case kInvalid_DirChange:
2397 SkFAIL("Use of invalid direction change flag");
2405 // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
2406 // value with the current vec is deemed to be of a significant value.
2407 SkVector fLastVec, fFirstVec;
2408 int fPtCount; // non-degenerate points
2409 DirChange fExpectedDir;
2410 SkPath::Convexity fConvexity;
2411 SkPathPriv::FirstDirection fFirstDirection;
2412 int fDx, fDy, fSx, fSy;
2417 SkPath::Convexity SkPath::internalGetConvexity() const {
2418 SkASSERT(kUnknown_Convexity == fConvexity);
2421 SkPath::Iter iter(*this, true);
2423 int contourCount = 0;
2428 return kUnknown_Convexity;
2430 while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
2433 if (++contourCount > 1) {
2434 fConvexity = kConcave_Convexity;
2435 return kConcave_Convexity;
2441 state.setCurve(false);
2448 count = 2 + (kCubic_Verb == verb);
2449 // As an additional enhancement, this could set curve true only
2450 // if the curve is nonlinear
2451 state.setCurve(true);
2454 state.setCurve(false);
2459 SkDEBUGFAIL("bad verb");
2460 fConvexity = kConcave_Convexity;
2461 return kConcave_Convexity;
2464 for (int i = 1; i <= count; i++) {
2465 state.addPt(pts[i]);
2468 if (!state.isFinite()) {
2469 return kUnknown_Convexity;
2471 if (kConcave_Convexity == state.getConvexity()) {
2472 fConvexity = kConcave_Convexity;
2473 return kConcave_Convexity;
2476 fConvexity = state.getConvexity();
2477 if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
2478 fFirstDirection = state.getFirstDirection();
2480 return static_cast<Convexity>(fConvexity);
2483 ///////////////////////////////////////////////////////////////////////////////
2487 ContourIter(const SkPathRef& pathRef);
2489 bool done() const { return fDone; }
2490 // if !done() then these may be called
2491 int count() const { return fCurrPtCount; }
2492 const SkPoint* pts() const { return fCurrPt; }
2497 const SkPoint* fCurrPt;
2498 const uint8_t* fCurrVerb;
2499 const uint8_t* fStopVerbs;
2500 const SkScalar* fCurrConicWeight;
2502 SkDEBUGCODE(int fContourCounter;)
2505 ContourIter::ContourIter(const SkPathRef& pathRef) {
2506 fStopVerbs = pathRef.verbsMemBegin();
2508 fCurrPt = pathRef.points();
2509 fCurrVerb = pathRef.verbs();
2510 fCurrConicWeight = pathRef.conicWeights();
2512 SkDEBUGCODE(fContourCounter = 0;)
2516 void ContourIter::next() {
2517 if (fCurrVerb <= fStopVerbs) {
2524 // skip pts of prev contour
2525 fCurrPt += fCurrPtCount;
2527 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
2528 int ptCount = 1; // moveTo
2529 const uint8_t* verbs = fCurrVerb;
2531 for (--verbs; verbs > fStopVerbs; --verbs) {
2532 switch (verbs[~0]) {
2533 case SkPath::kMove_Verb:
2535 case SkPath::kLine_Verb:
2538 case SkPath::kConic_Verb:
2539 fCurrConicWeight += 1;
2541 case SkPath::kQuad_Verb:
2544 case SkPath::kCubic_Verb:
2547 case SkPath::kClose_Verb:
2550 SkDEBUGFAIL("unexpected verb");
2555 fCurrPtCount = ptCount;
2557 SkDEBUGCODE(++fContourCounter;)
2560 // returns cross product of (p1 - p0) and (p2 - p0)
2561 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2562 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2563 // We may get 0 when the above subtracts underflow. We expect this to be
2564 // very rare and lazily promote to double.
2566 double p0x = SkScalarToDouble(p0.fX);
2567 double p0y = SkScalarToDouble(p0.fY);
2569 double p1x = SkScalarToDouble(p1.fX);
2570 double p1y = SkScalarToDouble(p1.fY);
2572 double p2x = SkScalarToDouble(p2.fX);
2573 double p2y = SkScalarToDouble(p2.fY);
2575 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2576 (p1y - p0y) * (p2x - p0x));
2582 // Returns the first pt with the maximum Y coordinate
2583 static int find_max_y(const SkPoint pts[], int count) {
2584 SkASSERT(count > 0);
2585 SkScalar max = pts[0].fY;
2587 for (int i = 1; i < count; ++i) {
2588 SkScalar y = pts[i].fY;
2597 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2601 if (i == index) { // we wrapped around, so abort
2604 if (pts[index] != pts[i]) { // found a different point, success!
2612 * Starting at index, and moving forward (incrementing), find the xmin and
2613 * xmax of the contiguous points that have the same Y.
2615 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2617 const SkScalar y = pts[index].fY;
2618 SkScalar min = pts[index].fX;
2620 int minIndex = index;
2621 int maxIndex = index;
2622 for (int i = index + 1; i < n; ++i) {
2623 if (pts[i].fY != y) {
2626 SkScalar x = pts[i].fX;
2630 } else if (x > max) {
2635 *maxIndexPtr = maxIndex;
2639 static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
2640 *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
2644 * We loop through all contours, and keep the computed cross-product of the
2645 * contour that contained the global y-max. If we just look at the first
2646 * contour, we may find one that is wound the opposite way (correctly) since
2647 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2648 * that is outer most (or at least has the global y-max) before we can consider
2649 * its cross product.
2651 bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
2652 if (kUnknown_FirstDirection != path.fFirstDirection.load()) {
2653 *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
2657 // don't want to pay the cost for computing this if it
2658 // is unknown, so we don't call isConvex()
2659 if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) {
2660 SkASSERT(kUnknown_FirstDirection == path.fFirstDirection);
2661 *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
2665 ContourIter iter(*path.fPathRef.get());
2667 // initialize with our logical y-min
2668 SkScalar ymax = path.getBounds().fTop;
2669 SkScalar ymaxCross = 0;
2671 for (; !iter.done(); iter.next()) {
2672 int n = iter.count();
2677 const SkPoint* pts = iter.pts();
2679 int index = find_max_y(pts, n);
2680 if (pts[index].fY < ymax) {
2684 // If there is more than 1 distinct point at the y-max, we take the
2685 // x-min and x-max of them and just subtract to compute the dir.
2686 if (pts[(index + 1) % n].fY == pts[index].fY) {
2688 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2689 if (minIndex == maxIndex) {
2692 SkASSERT(pts[minIndex].fY == pts[index].fY);
2693 SkASSERT(pts[maxIndex].fY == pts[index].fY);
2694 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2695 // we just subtract the indices, and let that auto-convert to
2696 // SkScalar, since we just want - or + to signal the direction.
2697 cross = minIndex - maxIndex;
2700 // Find a next and prev index to use for the cross-product test,
2701 // but we try to find pts that form non-zero vectors from pts[index]
2703 // Its possible that we can't find two non-degenerate vectors, so
2704 // we have to guard our search (e.g. all the pts could be in the
2707 // we pass n - 1 instead of -1 so we don't foul up % operator by
2708 // passing it a negative LH argument.
2709 int prev = find_diff_pt(pts, index, n, n - 1);
2710 if (prev == index) {
2711 // completely degenerate, skip to next contour
2714 int next = find_diff_pt(pts, index, n, 1);
2715 SkASSERT(next != index);
2716 cross = cross_prod(pts[prev], pts[index], pts[next]);
2717 // if we get a zero and the points are horizontal, then we look at the spread in
2718 // x-direction. We really should continue to walk away from the degeneracy until
2719 // there is a divergence.
2720 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2721 // construct the subtract so we get the correct Direction below
2722 cross = pts[index].fX - pts[next].fX;
2727 // record our best guess so far
2728 ymax = pts[index].fY;
2733 crossToDir(ymaxCross, dir);
2734 path.fFirstDirection = *dir;
2741 ///////////////////////////////////////////////////////////////////////////////
2743 static bool between(SkScalar a, SkScalar b, SkScalar c) {
2744 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
2745 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
2746 return (a - b) * (c - b) <= 0;
2749 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
2750 SkScalar D, SkScalar t) {
2751 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
2754 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2756 SkScalar A = c3 + 3*(c1 - c2) - c0;
2757 SkScalar B = 3*(c2 - c1 - c1 + c0);
2758 SkScalar C = 3*(c1 - c0);
2760 return eval_cubic_coeff(A, B, C, D, t);
2763 template <size_t N> static void find_minmax(const SkPoint pts[],
2764 SkScalar* minPtr, SkScalar* maxPtr) {
2766 min = max = pts[0].fX;
2767 for (size_t i = 1; i < N; ++i) {
2768 min = SkMinScalar(min, pts[i].fX);
2769 max = SkMaxScalar(max, pts[i].fX);
2775 static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
2776 if (start.fY == end.fY) {
2777 return between(start.fX, x, end.fX) && x != end.fX;
2779 return x == start.fX && y == start.fY;
2783 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2784 SkScalar y0 = pts[0].fY;
2785 SkScalar y3 = pts[3].fY;
2792 if (y < y0 || y > y3) {
2795 if (checkOnCurve(x, y, pts[0], pts[3])) {
2803 // quickreject or quickaccept
2805 find_minmax<4>(pts, &min, &max);
2813 // compute the actual x(t) value
2815 if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
2818 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
2819 if (SkScalarNearlyEqual(xt, x)) {
2820 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points
2825 return xt < x ? dir : 0;
2828 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2830 int n = SkChopCubicAtYExtrema(pts, dst);
2832 for (int i = 0; i <= n; ++i) {
2833 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
2838 static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
2840 SkASSERT(t >= 0 && t <= 1);
2841 SkScalar src2w = src[2] * w;
2842 SkScalar C = src[0];
2843 SkScalar A = src[4] - 2 * src2w + C;
2844 SkScalar B = 2 * (src2w - C);
2845 return (A * t + B) * t + C;
2849 static double conic_eval_denominator(SkScalar w, SkScalar t) {
2850 SkScalar B = 2 * (w - 1);
2853 return (A * t + B) * t + C;
2856 static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
2857 const SkPoint* pts = conic.fPts;
2858 SkScalar y0 = pts[0].fY;
2859 SkScalar y2 = pts[2].fY;
2866 if (y < y0 || y > y2) {
2869 if (checkOnCurve(x, y, pts[0], pts[2])) {
2878 SkScalar A = pts[2].fY;
2879 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
2880 SkScalar C = pts[0].fY;
2881 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
2882 B -= C; // B = b*w - w * yCept + yCept - a
2884 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
2888 // zero roots are returned only when y0 == y
2889 // Need [0] if dir == 1
2890 // and [2] if dir == -1
2891 xt = pts[1 - dir].fX;
2893 SkScalar t = roots[0];
2894 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
2896 if (SkScalarNearlyEqual(xt, x)) {
2897 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
2902 return xt < x ? dir : 0;
2905 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
2906 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
2917 static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
2918 int* onCurveCount) {
2919 SkConic conic(pts, weight);
2921 // If the data points are very large, the conic may not be monotonic but may also
2922 // fail to chop. Then, the chopper does not split the original conic in two.
2923 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
2924 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
2926 w += winding_mono_conic(chopped[1], x, y, onCurveCount);
2931 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2932 SkScalar y0 = pts[0].fY;
2933 SkScalar y2 = pts[2].fY;
2940 if (y < y0 || y > y2) {
2943 if (checkOnCurve(x, y, pts[0], pts[2])) {
2950 // bounds check on X (not required. is it faster?)
2952 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2958 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2959 2 * (pts[1].fY - pts[0].fY),
2965 // zero roots are returned only when y0 == y
2966 // Need [0] if dir == 1
2967 // and [2] if dir == -1
2968 xt = pts[1 - dir].fX;
2970 SkScalar t = roots[0];
2971 SkScalar C = pts[0].fX;
2972 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2973 SkScalar B = 2 * (pts[1].fX - C);
2974 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
2976 if (SkScalarNearlyEqual(xt, x)) {
2977 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
2982 return xt < x ? dir : 0;
2985 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2989 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
2990 n = SkChopQuadAtYExtrema(pts, dst);
2993 int w = winding_mono_quad(pts, x, y, onCurveCount);
2995 w += winding_mono_quad(&pts[2], x, y, onCurveCount);
3000 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3001 SkScalar x0 = pts[0].fX;
3002 SkScalar y0 = pts[0].fY;
3003 SkScalar x1 = pts[1].fX;
3004 SkScalar y1 = pts[1].fY;
3006 SkScalar dy = y1 - y0;
3013 if (y < y0 || y > y1) {
3016 if (checkOnCurve(x, y, pts[0], pts[1])) {
3023 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - SkScalarMul(dy, x - x0);
3026 // zero cross means the point is on the line, and since the case where
3027 // y of the query point is at the end point is handled above, we can be
3028 // sure that we're on the line (excluding the end point) here
3029 if (x != x1 || y != pts[1].fY) {
3033 } else if (SkScalarSignAsInt(cross) == dir) {
3039 static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
3040 SkTDArray<SkVector>* tangents) {
3041 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
3042 && !between(pts[2].fY, y, pts[3].fY)) {
3045 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
3046 && !between(pts[2].fX, x, pts[3].fX)) {
3050 int n = SkChopCubicAtYExtrema(pts, dst);
3051 for (int i = 0; i <= n; ++i) {
3052 SkPoint* c = &dst[i * 3];
3054 if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
3057 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
3058 if (!SkScalarNearlyEqual(x, xt)) {
3062 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
3063 tangents->push(tangent);
3067 static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
3068 SkTDArray<SkVector>* tangents) {
3069 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3072 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3076 SkScalar A = pts[2].fY;
3077 SkScalar B = pts[1].fY * w - y * w + y;
3078 SkScalar C = pts[0].fY;
3079 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3080 B -= C; // B = b*w - w * yCept + yCept - a
3082 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3083 for (int index = 0; index < n; ++index) {
3084 SkScalar t = roots[index];
3085 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
3086 if (!SkScalarNearlyEqual(x, xt)) {
3089 SkConic conic(pts, w);
3090 tangents->push(conic.evalTangentAt(t));
3094 static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
3095 SkTDArray<SkVector>* tangents) {
3096 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3099 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3103 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3104 2 * (pts[1].fY - pts[0].fY),
3107 for (int index = 0; index < n; ++index) {
3108 SkScalar t = roots[index];
3109 SkScalar C = pts[0].fX;
3110 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3111 SkScalar B = 2 * (pts[1].fX - C);
3112 SkScalar xt = (A * t + B) * t + C;
3113 if (!SkScalarNearlyEqual(x, xt)) {
3116 tangents->push(SkEvalQuadTangentAt(pts, t));
3120 static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
3121 SkTDArray<SkVector>* tangents) {
3122 SkScalar y0 = pts[0].fY;
3123 SkScalar y1 = pts[1].fY;
3124 if (!between(y0, y, y1)) {
3127 SkScalar x0 = pts[0].fX;
3128 SkScalar x1 = pts[1].fX;
3129 if (!between(x0, x, x1)) {
3132 SkScalar dx = x1 - x0;
3133 SkScalar dy = y1 - y0;
3134 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3142 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3143 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3146 bool SkPath::contains(SkScalar x, SkScalar y) const {
3147 bool isInverse = this->isInverseFillType();
3148 if (this->isEmpty()) {
3152 if (!contains_inclusive(this->getBounds(), x, y)) {
3156 SkPath::Iter iter(*this, true);
3159 int onCurveCount = 0;
3162 switch (iter.next(pts, false)) {
3163 case SkPath::kMove_Verb:
3164 case SkPath::kClose_Verb:
3166 case SkPath::kLine_Verb:
3167 w += winding_line(pts, x, y, &onCurveCount);
3169 case SkPath::kQuad_Verb:
3170 w += winding_quad(pts, x, y, &onCurveCount);
3172 case SkPath::kConic_Verb:
3173 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3175 case SkPath::kCubic_Verb:
3176 w += winding_cubic(pts, x, y, &onCurveCount);
3178 case SkPath::kDone_Verb:
3183 bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
3184 || SkPath::kInverseEvenOdd_FillType == this->getFillType();
3191 if (onCurveCount <= 1) {
3192 return SkToBool(onCurveCount) ^ isInverse;
3194 if ((onCurveCount & 1) || evenOddFill) {
3195 return SkToBool(onCurveCount & 1) ^ isInverse;
3197 // If the point touches an even number of curves, and the fill is winding, check for
3198 // coincidence. Count coincidence as places where the on curve points have identical tangents.
3199 iter.setPath(*this, true);
3201 SkTDArray<SkVector> tangents;
3204 int oldCount = tangents.count();
3205 switch (iter.next(pts, false)) {
3206 case SkPath::kMove_Verb:
3207 case SkPath::kClose_Verb:
3209 case SkPath::kLine_Verb:
3210 tangent_line(pts, x, y, &tangents);
3212 case SkPath::kQuad_Verb:
3213 tangent_quad(pts, x, y, &tangents);
3215 case SkPath::kConic_Verb:
3216 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3218 case SkPath::kCubic_Verb:
3219 tangent_cubic(pts, x, y, &tangents);
3221 case SkPath::kDone_Verb:
3225 if (tangents.count() > oldCount) {
3226 int last = tangents.count() - 1;
3227 const SkVector& tangent = tangents[last];
3228 if (SkScalarNearlyZero(tangent.lengthSqd())) {
3229 tangents.remove(last);
3231 for (int index = 0; index < last; ++index) {
3232 const SkVector& test = tangents[index];
3233 if (SkScalarNearlyZero(test.cross(tangent))
3234 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3235 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3236 tangents.remove(last);
3237 tangents.removeShuffle(index);
3244 return SkToBool(tangents.count()) ^ isInverse;
3247 int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3248 SkScalar w, SkPoint pts[], int pow2) {
3249 const SkConic conic(p0, p1, p2, w);
3250 return conic.chopIntoQuadsPOW2(pts, pow2);
3253 bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
3255 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3258 SkPath::RawIter iter(path);
3263 while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
3265 case SkPath::kMove_Verb:
3266 if (0 != rectPtCnt) {
3269 rectPts[0] = verbPts[0];
3272 case SkPath::kLine_Verb:
3273 if (5 == rectPtCnt) {
3276 rectPts[rectPtCnt] = verbPts[1];
3279 case SkPath::kClose_Verb:
3280 if (4 == rectPtCnt) {
3281 rectPts[4] = rectPts[0];
3289 if (rectPtCnt < 5) {
3292 if (rectPts[0] != rectPts[4]) {
3295 int verticalCnt = 0;
3296 int horizontalCnt = 0;
3297 // dirs are 0 - right, 1 - down, 2 - left, 3 - up.
3301 for (int i = 0; i < 4; ++i) {
3303 if (rectPts[i].fX == rectPts[i + 1].fX) {
3307 if (rectPts[1].fY > rectPts[0].fY) {
3309 tempRect.fTop = rectPts[0].fY;
3310 tempRect.fBottom = rectPts[1].fY;
3313 tempRect.fTop = rectPts[1].fY;
3314 tempRect.fBottom = rectPts[0].fY;
3316 } else if (1 == i) {
3317 if (rectPts[2].fY > rectPts[1].fY) {
3319 tempRect.fTop = rectPts[1].fY;
3320 tempRect.fBottom = rectPts[2].fY;
3323 tempRect.fTop = rectPts[2].fY;
3324 tempRect.fBottom = rectPts[1].fY;
3328 if (rectPts[i].fY == rectPts[i + 1].fY) {
3332 if (rectPts[1].fX > rectPts[0].fX) {
3334 tempRect.fLeft = rectPts[0].fX;
3335 tempRect.fRight = rectPts[1].fX;
3338 tempRect.fLeft = rectPts[1].fX;
3339 tempRect.fRight = rectPts[0].fX;
3341 } else if (1 == i) {
3342 if (rectPts[2].fX > rectPts[1].fX) {
3344 tempRect.fLeft = rectPts[1].fX;
3345 tempRect.fRight = rectPts[2].fX;
3348 tempRect.fLeft = rectPts[2].fX;
3349 tempRect.fRight = rectPts[1].fX;
3357 if (2 != horizontalCnt || 2 != verticalCnt) {
3360 // low bit indicates a vertical dir
3361 SkASSERT((firstDir ^ secondDir) & 0b1);
3362 if (((firstDir + 1) & 0b11) == secondDir) {
3363 *direction = SkPath::kCW_Direction;
3366 SkASSERT(((secondDir + 1) & 0b11) == firstDir);
3367 *direction = SkPath::kCCW_Direction;