1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkPath.h"
9
10 #include "SkBuffer.h"
11 #include "SkCubicClipper.h"
12 #include "SkData.h"
13 #include "SkGeometry.h"
14 #include "SkMacros.h"
15 #include "SkMath.h"
16 #include "SkMatrixPriv.h"
17 #include "SkPathPriv.h"
18 #include "SkPathRef.h"
19 #include "SkPointPriv.h"
20 #include "SkRRect.h"
21 #include "SkSafeMath.h"
22 #include "SkTLazy.h"
23 #include "SkTo.h"
24
25 #include <cmath>
26 #include <utility>
27
28 struct SkPath_Storage_Equivalent {
29 void* fPtr;
30 int32_t fIndex;
31 uint32_t fFlags;
32 };
33
34 static_assert(sizeof(SkPath) == sizeof(SkPath_Storage_Equivalent),
35 "Please keep an eye on SkPath packing.");
36
poly_eval(float A,float B,float C,float t)37 static float poly_eval(float A, float B, float C, float t) {
38 return (A * t + B) * t + C;
39 }
40
poly_eval(float A,float B,float C,float D,float t)41 static float poly_eval(float A, float B, float C, float D, float t) {
42 return ((A * t + B) * t + C) * t + D;
43 }
44
45 ////////////////////////////////////////////////////////////////////////////
46
47 /**
48 * Path.bounds is defined to be the bounds of all the control points.
49 * If we called bounds.join(r) we would skip r if r was empty, which breaks
50 * our promise. Hence we have a custom joiner that doesn't look at emptiness
51 */
joinNoEmptyChecks(SkRect * dst,const SkRect & src)52 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
53 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
54 dst->fTop = SkMinScalar(dst->fTop, src.fTop);
55 dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
56 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
57 }
58
is_degenerate(const SkPath & path)59 static bool is_degenerate(const SkPath& path) {
60 SkPath::Iter iter(path, false);
61 SkPoint pts[4];
62 return SkPath::kDone_Verb == iter.next(pts);
63 }
64
65 class SkAutoDisableDirectionCheck {
66 public:
SkAutoDisableDirectionCheck(SkPath * path)67 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
68 fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->getFirstDirection());
69 }
70
~SkAutoDisableDirectionCheck()71 ~SkAutoDisableDirectionCheck() {
72 fPath->setFirstDirection(fSaved);
73 }
74
75 private:
76 SkPath* fPath;
77 SkPathPriv::FirstDirection fSaved;
78 };
79 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
80
81 /* This guy's constructor/destructor bracket a path editing operation. It is
82 used when we know the bounds of the amount we are going to add to the path
83 (usually a new contour, but not required).
84
85 It captures some state about the path up front (i.e. if it already has a
86 cached bounds), and then if it can, it updates the cache bounds explicitly,
87 avoiding the need to revisit all of the points in getBounds().
88
89 It also notes if the path was originally degenerate, and if so, sets
90 isConvex to true. Thus it can only be used if the contour being added is
91 convex.
92 */
93 class SkAutoPathBoundsUpdate {
94 public:
SkAutoPathBoundsUpdate(SkPath * path,const SkRect & r)95 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
96 this->init(path);
97 }
98
SkAutoPathBoundsUpdate(SkPath * path,SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)99 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
100 SkScalar right, SkScalar bottom) {
101 fRect.set(left, top, right, bottom);
102 this->init(path);
103 }
104
~SkAutoPathBoundsUpdate()105 ~SkAutoPathBoundsUpdate() {
106 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
107 : SkPath::kUnknown_Convexity);
108 if ((fEmpty || fHasValidBounds) && fRect.isFinite()) {
109 fPath->setBounds(fRect);
110 }
111 }
112
113 private:
114 SkPath* fPath;
115 SkRect fRect;
116 bool fHasValidBounds;
117 bool fDegenerate;
118 bool fEmpty;
119
init(SkPath * path)120 void init(SkPath* path) {
121 // Cannot use fRect for our bounds unless we know it is sorted
122 fRect.sort();
123 fPath = path;
124 // Mark the path's bounds as dirty if (1) they are, or (2) the path
125 // is non-finite, and therefore its bounds are not meaningful
126 fHasValidBounds = path->hasComputedBounds() && path->isFinite();
127 fEmpty = path->isEmpty();
128 if (fHasValidBounds && !fEmpty) {
129 joinNoEmptyChecks(&fRect, fPath->getBounds());
130 }
131 fDegenerate = is_degenerate(*path);
132 }
133 };
134 #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
135
136 ////////////////////////////////////////////////////////////////////////////
137
138 /*
139 Stores the verbs and points as they are given to us, with exceptions:
140 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
141 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
142
143 The iterator does more cleanup, especially if forceClose == true
144 1. If we encounter degenerate segments, remove them
145 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
146 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
147 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
148 */
149
150 ////////////////////////////////////////////////////////////////////////////
151
152 // flag to require a moveTo if we begin with something else, like lineTo etc.
153 #define INITIAL_LASTMOVETOINDEX_VALUE ~0
154
SkPath()155 SkPath::SkPath()
156 : fPathRef(SkPathRef::CreateEmpty()) {
157 this->resetFields();
158 fIsVolatile = false;
159 fIsBadForDAA = false;
160 }
161
resetFields()162 void SkPath::resetFields() {
163 //fPathRef is assumed to have been emptied by the caller.
164 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
165 fFillType = kWinding_FillType;
166 this->setConvexity(kUnknown_Convexity);
167 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
168
169 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we
170 // don't want to muck with it if it's been set to something non-nullptr.
171 }
172
SkPath(const SkPath & that)173 SkPath::SkPath(const SkPath& that)
174 : fPathRef(SkRef(that.fPathRef.get())) {
175 this->copyFields(that);
176 SkDEBUGCODE(that.validate();)
177 }
178
~SkPath()179 SkPath::~SkPath() {
180 SkDEBUGCODE(this->validate();)
181 }
182
operator =(const SkPath & that)183 SkPath& SkPath::operator=(const SkPath& that) {
184 SkDEBUGCODE(that.validate();)
185
186 if (this != &that) {
187 fPathRef.reset(SkRef(that.fPathRef.get()));
188 this->copyFields(that);
189 }
190 SkDEBUGCODE(this->validate();)
191 return *this;
192 }
193
copyFields(const SkPath & that)194 void SkPath::copyFields(const SkPath& that) {
195 //fPathRef is assumed to have been set by the caller.
196 fLastMoveToIndex = that.fLastMoveToIndex;
197 fFillType = that.fFillType;
198 fIsVolatile = that.fIsVolatile;
199 fIsBadForDAA = that.fIsBadForDAA;
200
201 // Non-atomic assignment of atomic values.
202 this->setConvexity(that.getConvexityOrUnknown());
203 this->setFirstDirection(that.getFirstDirection());
204 }
205
operator ==(const SkPath & a,const SkPath & b)206 bool operator==(const SkPath& a, const SkPath& b) {
207 // note: don't need to look at isConvex or bounds, since just comparing the
208 // raw data is sufficient.
209 return &a == &b ||
210 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
211 }
212
swap(SkPath & that)213 void SkPath::swap(SkPath& that) {
214 if (this != &that) {
215 fPathRef.swap(that.fPathRef);
216 std::swap(fLastMoveToIndex, that.fLastMoveToIndex);
217
218 const auto ft = fFillType;
219 fFillType = that.fFillType;
220 that.fFillType = ft;
221
222 const auto iv = fIsVolatile;
223 fIsVolatile = that.fIsVolatile;
224 that.fIsVolatile = iv;
225
226 // Non-atomic swaps of atomic values.
227 Convexity c = this->getConvexityOrUnknown();
228 this->setConvexity(that.getConvexityOrUnknown());
229 that.setConvexity(c);
230
231 uint8_t fd = this->getFirstDirection();
232 this->setFirstDirection(that.getFirstDirection());
233 that.setFirstDirection(fd);
234 }
235 }
236
isInterpolatable(const SkPath & compare) const237 bool SkPath::isInterpolatable(const SkPath& compare) const {
238 int count = fPathRef->countVerbs();
239 if (count != compare.fPathRef->countVerbs()) {
240 return false;
241 }
242 if (!count) {
243 return true;
244 }
245 if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
246 count)) {
247 return false;
248 }
249 return !fPathRef->countWeights() ||
250 !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
251 fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
252 }
253
interpolate(const SkPath & ending,SkScalar weight,SkPath * out) const254 bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
255 int pointCount = fPathRef->countPoints();
256 if (pointCount != ending.fPathRef->countPoints()) {
257 return false;
258 }
259 if (!pointCount) {
260 return true;
261 }
262 out->reset();
263 out->addPath(*this);
264 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get());
265 return true;
266 }
267
check_edge_against_rect(const SkPoint & p0,const SkPoint & p1,const SkRect & rect,SkPathPriv::FirstDirection dir)268 static inline bool check_edge_against_rect(const SkPoint& p0,
269 const SkPoint& p1,
270 const SkRect& rect,
271 SkPathPriv::FirstDirection dir) {
272 const SkPoint* edgeBegin;
273 SkVector v;
274 if (SkPathPriv::kCW_FirstDirection == dir) {
275 v = p1 - p0;
276 edgeBegin = &p0;
277 } else {
278 v = p0 - p1;
279 edgeBegin = &p1;
280 }
281 if (v.fX || v.fY) {
282 // check the cross product of v with the vec from edgeBegin to each rect corner
283 SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
284 SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
285 SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
286 SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
287 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
288 return false;
289 }
290 }
291 return true;
292 }
293
conservativelyContainsRect(const SkRect & rect) const294 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
295 // This only handles non-degenerate convex paths currently.
296 if (kConvex_Convexity != this->getConvexity()) {
297 return false;
298 }
299
300 SkPathPriv::FirstDirection direction;
301 if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
302 return false;
303 }
304
305 SkPoint firstPt;
306 SkPoint prevPt;
307 SkPath::Iter iter(*this, true);
308 SkPath::Verb verb;
309 SkPoint pts[4];
310 int segmentCount = 0;
311 SkDEBUGCODE(int moveCnt = 0;)
312 SkDEBUGCODE(int closeCount = 0;)
313
314 while ((verb = iter.next(pts, true, true)) != kDone_Verb) {
315 int nextPt = -1;
316 switch (verb) {
317 case kMove_Verb:
318 SkASSERT(!segmentCount && !closeCount);
319 SkDEBUGCODE(++moveCnt);
320 firstPt = prevPt = pts[0];
321 break;
322 case kLine_Verb:
323 nextPt = 1;
324 SkASSERT(moveCnt && !closeCount);
325 ++segmentCount;
326 break;
327 case kQuad_Verb:
328 case kConic_Verb:
329 SkASSERT(moveCnt && !closeCount);
330 ++segmentCount;
331 nextPt = 2;
332 break;
333 case kCubic_Verb:
334 SkASSERT(moveCnt && !closeCount);
335 ++segmentCount;
336 nextPt = 3;
337 break;
338 case kClose_Verb:
339 SkDEBUGCODE(++closeCount;)
340 break;
341 default:
342 SkDEBUGFAIL("unknown verb");
343 }
344 if (-1 != nextPt) {
345 if (SkPath::kConic_Verb == verb) {
346 SkConic orig;
347 orig.set(pts, iter.conicWeight());
348 SkPoint quadPts[5];
349 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
350 SkASSERT_RELEASE(2 == count);
351
352 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
353 return false;
354 }
355 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
356 return false;
357 }
358 } else {
359 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
360 return false;
361 }
362 }
363 prevPt = pts[nextPt];
364 }
365 }
366
367 if (segmentCount) {
368 return check_edge_against_rect(prevPt, firstPt, rect, direction);
369 }
370 return false;
371 }
372
getGenerationID() const373 uint32_t SkPath::getGenerationID() const {
374 uint32_t genID = fPathRef->genID();
375 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
376 SkASSERT((unsigned)fFillType < (1 << (32 - SkPathPriv::kPathRefGenIDBitCnt)));
377 genID |= static_cast<uint32_t>(fFillType) << SkPathPriv::kPathRefGenIDBitCnt;
378 #endif
379 return genID;
380 }
381
reset()382 SkPath& SkPath::reset() {
383 SkDEBUGCODE(this->validate();)
384
385 fPathRef.reset(SkPathRef::CreateEmpty());
386 this->resetFields();
387 return *this;
388 }
389
rewind()390 SkPath& SkPath::rewind() {
391 SkDEBUGCODE(this->validate();)
392
393 SkPathRef::Rewind(&fPathRef);
394 this->resetFields();
395 return *this;
396 }
397
isLastContourClosed() const398 bool SkPath::isLastContourClosed() const {
399 int verbCount = fPathRef->countVerbs();
400 if (0 == verbCount) {
401 return false;
402 }
403 return kClose_Verb == fPathRef->atVerb(verbCount - 1);
404 }
405
isLine(SkPoint line[2]) const406 bool SkPath::isLine(SkPoint line[2]) const {
407 int verbCount = fPathRef->countVerbs();
408
409 if (2 == verbCount) {
410 SkASSERT(kMove_Verb == fPathRef->atVerb(0));
411 if (kLine_Verb == fPathRef->atVerb(1)) {
412 SkASSERT(2 == fPathRef->countPoints());
413 if (line) {
414 const SkPoint* pts = fPathRef->points();
415 line[0] = pts[0];
416 line[1] = pts[1];
417 }
418 return true;
419 }
420 }
421 return false;
422 }
423
424 /*
425 Determines if path is a rect by keeping track of changes in direction
426 and looking for a loop either clockwise or counterclockwise.
427
428 The direction is computed such that:
429 0: vertical up
430 1: horizontal left
431 2: vertical down
432 3: horizontal right
433
434 A rectangle cycles up/right/down/left or up/left/down/right.
435
436 The test fails if:
437 The path is closed, and followed by a line.
438 A second move creates a new endpoint.
439 A diagonal line is parsed.
440 There's more than four changes of direction.
441 There's a discontinuity on the line (e.g., a move in the middle)
442 The line reverses direction.
443 The path contains a quadratic or cubic.
444 The path contains fewer than four points.
445 *The rectangle doesn't complete a cycle.
446 *The final point isn't equal to the first point.
447
448 *These last two conditions we relax if we have a 3-edge path that would
449 form a rectangle if it were closed (as we do when we fill a path)
450
451 It's OK if the path has:
452 Several colinear line segments composing a rectangle side.
453 Single points on the rectangle side.
454
455 The direction takes advantage of the corners found since opposite sides
456 must travel in opposite directions.
457
458 FIXME: Allow colinear quads and cubics to be treated like lines.
459 FIXME: If the API passes fill-only, return true if the filled stroke
460 is a rectangle, though the caller failed to close the path.
461
462 directions values:
463 0x1 is set if the segment is horizontal
464 0x2 is set if the segment is moving to the right or down
465 thus:
466 two directions are opposites iff (dirA ^ dirB) == 0x2
467 two directions are perpendicular iff (dirA ^ dirB) == 0x1
468
469 */
rect_make_dir(SkScalar dx,SkScalar dy)470 static int rect_make_dir(SkScalar dx, SkScalar dy) {
471 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
472 }
473
isRectContour(bool allowPartial,int * currVerb,const SkPoint ** ptsPtr,bool * isClosed,Direction * direction,SkRect * rect) const474 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
475 bool* isClosed, Direction* direction, SkRect* rect) const {
476 int corners = 0;
477 SkPoint closeXY; // used to determine if final line falls on a diagonal
478 SkPoint lineStart; // used to construct line from previous point
479 const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves)
480 const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed)
481 SkPoint firstCorner;
482 SkPoint thirdCorner;
483 const SkPoint* pts = *ptsPtr;
484 const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects
485 lineStart.set(0, 0);
486 signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized
487 bool closedOrMoved = false;
488 bool autoClose = false;
489 bool insertClose = false;
490 int verbCnt = fPathRef->countVerbs();
491 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
492 uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
493 switch (verb) {
494 case kClose_Verb:
495 savePts = pts;
496 autoClose = true;
497 insertClose = false;
498 case kLine_Verb: {
499 if (kClose_Verb != verb) {
500 lastPt = pts;
501 }
502 SkPoint lineEnd = kClose_Verb == verb ? *firstPt : *pts++;
503 SkVector lineDelta = lineEnd - lineStart;
504 if (lineDelta.fX && lineDelta.fY) {
505 return false; // diagonal
506 }
507 if (!lineDelta.isFinite()) {
508 return false; // path contains infinity or NaN
509 }
510 if (lineStart == lineEnd) {
511 break; // single point on side OK
512 }
513 int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3
514 if (0 == corners) {
515 directions[0] = nextDirection;
516 corners = 1;
517 closedOrMoved = false;
518 lineStart = lineEnd;
519 break;
520 }
521 if (closedOrMoved) {
522 return false; // closed followed by a line
523 }
524 if (autoClose && nextDirection == directions[0]) {
525 break; // colinear with first
526 }
527 closedOrMoved = autoClose;
528 if (directions[corners - 1] == nextDirection) {
529 if (3 == corners && kLine_Verb == verb) {
530 thirdCorner = lineEnd;
531 }
532 lineStart = lineEnd;
533 break; // colinear segment
534 }
535 directions[corners++] = nextDirection;
536 // opposite lines must point in opposite directions; xoring them should equal 2
537 switch (corners) {
538 case 2:
539 firstCorner = lineStart;
540 break;
541 case 3:
542 if ((directions[0] ^ directions[2]) != 2) {
543 return false;
544 }
545 thirdCorner = lineEnd;
546 break;
547 case 4:
548 if ((directions[1] ^ directions[3]) != 2) {
549 return false;
550 }
551 break;
552 default:
553 return false; // too many direction changes
554 }
555 lineStart = lineEnd;
556 break;
557 }
558 case kQuad_Verb:
559 case kConic_Verb:
560 case kCubic_Verb:
561 return false; // quadratic, cubic not allowed
562 case kMove_Verb:
563 if (allowPartial && !autoClose && directions[0] >= 0) {
564 insertClose = true;
565 *currVerb -= 1; // try move again afterwards
566 goto addMissingClose;
567 }
568 if (!corners) {
569 firstPt = pts;
570 } else {
571 closeXY = *firstPt - *lastPt;
572 if (closeXY.fX && closeXY.fY) {
573 return false; // we're diagonal, abort
574 }
575 }
576 lineStart = *pts++;
577 closedOrMoved = true;
578 break;
579 default:
580 SkDEBUGFAIL("unexpected verb");
581 break;
582 }
583 *currVerb += 1;
584 addMissingClose:
585 ;
586 }
587 // Success if 4 corners and first point equals last
588 if (corners < 3 || corners > 4) {
589 return false;
590 }
591 if (savePts) {
592 *ptsPtr = savePts;
593 }
594 // check if close generates diagonal
595 closeXY = *firstPt - *lastPt;
596 if (closeXY.fX && closeXY.fY) {
597 return false;
598 }
599 if (rect) {
600 rect->set(firstCorner, thirdCorner);
601 }
602 if (isClosed) {
603 *isClosed = autoClose;
604 }
605 if (direction) {
606 *direction = directions[0] == ((directions[1] + 1) & 3) ? kCW_Direction : kCCW_Direction;
607 }
608 return true;
609 }
610
isRect(SkRect * rect,bool * isClosed,Direction * direction) const611 bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
612 SkDEBUGCODE(this->validate();)
613 int currVerb = 0;
614 const SkPoint* pts = fPathRef->points();
615 return this->isRectContour(false, &currVerb, &pts, isClosed, direction, rect);
616 }
617
isNestedFillRects(SkRect rects[2],Direction dirs[2]) const618 bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
619 SkDEBUGCODE(this->validate();)
620 int currVerb = 0;
621 const SkPoint* pts = fPathRef->points();
622 Direction testDirs[2];
623 SkRect testRects[2];
624 if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) {
625 return false;
626 }
627 if (isRectContour(false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) {
628 if (testRects[0].contains(testRects[1])) {
629 if (rects) {
630 rects[0] = testRects[0];
631 rects[1] = testRects[1];
632 }
633 if (dirs) {
634 dirs[0] = testDirs[0];
635 dirs[1] = testDirs[1];
636 }
637 return true;
638 }
639 if (testRects[1].contains(testRects[0])) {
640 if (rects) {
641 rects[0] = testRects[1];
642 rects[1] = testRects[0];
643 }
644 if (dirs) {
645 dirs[0] = testDirs[1];
646 dirs[1] = testDirs[0];
647 }
648 return true;
649 }
650 }
651 return false;
652 }
653
isOval(SkRect * bounds) const654 bool SkPath::isOval(SkRect* bounds) const {
655 return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr);
656 }
657
isRRect(SkRRect * rrect) const658 bool SkPath::isRRect(SkRRect* rrect) const {
659 return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr);
660 }
661
countPoints() const662 int SkPath::countPoints() const {
663 return fPathRef->countPoints();
664 }
665
getPoints(SkPoint dst[],int max) const666 int SkPath::getPoints(SkPoint dst[], int max) const {
667 SkDEBUGCODE(this->validate();)
668
669 SkASSERT(max >= 0);
670 SkASSERT(!max || dst);
671 int count = SkMin32(max, fPathRef->countPoints());
672 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
673 return fPathRef->countPoints();
674 }
675
getPoint(int index) const676 SkPoint SkPath::getPoint(int index) const {
677 if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
678 return fPathRef->atPoint(index);
679 }
680 return SkPoint::Make(0, 0);
681 }
682
countVerbs() const683 int SkPath::countVerbs() const {
684 return fPathRef->countVerbs();
685 }
686
copy_verbs_reverse(uint8_t * inorderDst,const uint8_t * reversedSrc,int count)687 static inline void copy_verbs_reverse(uint8_t* inorderDst,
688 const uint8_t* reversedSrc,
689 int count) {
690 for (int i = 0; i < count; ++i) {
691 inorderDst[i] = reversedSrc[~i];
692 }
693 }
694
getVerbs(uint8_t dst[],int max) const695 int SkPath::getVerbs(uint8_t dst[], int max) const {
696 SkDEBUGCODE(this->validate();)
697
698 SkASSERT(max >= 0);
699 SkASSERT(!max || dst);
700 int count = SkMin32(max, fPathRef->countVerbs());
701 copy_verbs_reverse(dst, fPathRef->verbs(), count);
702 return fPathRef->countVerbs();
703 }
704
getLastPt(SkPoint * lastPt) const705 bool SkPath::getLastPt(SkPoint* lastPt) const {
706 SkDEBUGCODE(this->validate();)
707
708 int count = fPathRef->countPoints();
709 if (count > 0) {
710 if (lastPt) {
711 *lastPt = fPathRef->atPoint(count - 1);
712 }
713 return true;
714 }
715 if (lastPt) {
716 lastPt->set(0, 0);
717 }
718 return false;
719 }
720
setPt(int index,SkScalar x,SkScalar y)721 void SkPath::setPt(int index, SkScalar x, SkScalar y) {
722 SkDEBUGCODE(this->validate();)
723
724 int count = fPathRef->countPoints();
725 if (count <= index) {
726 return;
727 } else {
728 SkPathRef::Editor ed(&fPathRef);
729 ed.atPoint(index)->set(x, y);
730 }
731 }
732
setLastPt(SkScalar x,SkScalar y)733 void SkPath::setLastPt(SkScalar x, SkScalar y) {
734 SkDEBUGCODE(this->validate();)
735
736 int count = fPathRef->countPoints();
737 if (count == 0) {
738 this->moveTo(x, y);
739 } else {
740 SkPathRef::Editor ed(&fPathRef);
741 ed.atPoint(count-1)->set(x, y);
742 }
743 }
744
745 // This is the public-facing non-const setConvexity().
setConvexity(Convexity c)746 void SkPath::setConvexity(Convexity c) {
747 fConvexity.store(c, std::memory_order_relaxed);
748 }
749
750 // Const hooks for working with fConvexity and fFirstDirection from const methods.
setConvexity(Convexity c) const751 void SkPath::setConvexity(Convexity c) const {
752 fConvexity.store(c, std::memory_order_relaxed);
753 }
setFirstDirection(uint8_t d) const754 void SkPath::setFirstDirection(uint8_t d) const {
755 fFirstDirection.store(d, std::memory_order_relaxed);
756 }
getFirstDirection() const757 uint8_t SkPath::getFirstDirection() const {
758 return fFirstDirection.load(std::memory_order_relaxed);
759 }
760
761 //////////////////////////////////////////////////////////////////////////////
762 // Construction methods
763
764 #define DIRTY_AFTER_EDIT \
765 do { \
766 this->setConvexity(kUnknown_Convexity); \
767 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection); \
768 } while (0)
769
incReserve(int inc)770 void SkPath::incReserve(int inc) {
771 SkDEBUGCODE(this->validate();)
772 if (inc > 0) {
773 SkPathRef::Editor(&fPathRef, inc, inc);
774 }
775 SkDEBUGCODE(this->validate();)
776 }
777
moveTo(SkScalar x,SkScalar y)778 SkPath& SkPath::moveTo(SkScalar x, SkScalar y) {
779 SkDEBUGCODE(this->validate();)
780
781 SkPathRef::Editor ed(&fPathRef);
782
783 // remember our index
784 fLastMoveToIndex = fPathRef->countPoints();
785
786 ed.growForVerb(kMove_Verb)->set(x, y);
787
788 DIRTY_AFTER_EDIT;
789 return *this;
790 }
791
rMoveTo(SkScalar x,SkScalar y)792 SkPath& SkPath::rMoveTo(SkScalar x, SkScalar y) {
793 SkPoint pt;
794 this->getLastPt(&pt);
795 return this->moveTo(pt.fX + x, pt.fY + y);
796 }
797
injectMoveToIfNeeded()798 void SkPath::injectMoveToIfNeeded() {
799 if (fLastMoveToIndex < 0) {
800 SkScalar x, y;
801 if (fPathRef->countVerbs() == 0) {
802 x = y = 0;
803 } else {
804 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
805 x = pt.fX;
806 y = pt.fY;
807 }
808 this->moveTo(x, y);
809 }
810 }
811
lineTo(SkScalar x,SkScalar y)812 SkPath& SkPath::lineTo(SkScalar x, SkScalar y) {
813 SkDEBUGCODE(this->validate();)
814
815 this->injectMoveToIfNeeded();
816
817 SkPathRef::Editor ed(&fPathRef);
818 ed.growForVerb(kLine_Verb)->set(x, y);
819
820 DIRTY_AFTER_EDIT;
821 return *this;
822 }
823
rLineTo(SkScalar x,SkScalar y)824 SkPath& SkPath::rLineTo(SkScalar x, SkScalar y) {
825 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
826 SkPoint pt;
827 this->getLastPt(&pt);
828 return this->lineTo(pt.fX + x, pt.fY + y);
829 }
830
quadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)831 SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
832 SkDEBUGCODE(this->validate();)
833
834 this->injectMoveToIfNeeded();
835
836 SkPathRef::Editor ed(&fPathRef);
837 SkPoint* pts = ed.growForVerb(kQuad_Verb);
838 pts[0].set(x1, y1);
839 pts[1].set(x2, y2);
840
841 DIRTY_AFTER_EDIT;
842 return *this;
843 }
844
rQuadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)845 SkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
846 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
847 SkPoint pt;
848 this->getLastPt(&pt);
849 return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
850 }
851
conicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar w)852 SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
853 SkScalar w) {
854 // check for <= 0 or NaN with this test
855 if (!(w > 0)) {
856 this->lineTo(x2, y2);
857 } else if (!SkScalarIsFinite(w)) {
858 this->lineTo(x1, y1);
859 this->lineTo(x2, y2);
860 } else if (SK_Scalar1 == w) {
861 this->quadTo(x1, y1, x2, y2);
862 } else {
863 SkDEBUGCODE(this->validate();)
864
865 this->injectMoveToIfNeeded();
866
867 SkPathRef::Editor ed(&fPathRef);
868 SkPoint* pts = ed.growForVerb(kConic_Verb, w);
869 pts[0].set(x1, y1);
870 pts[1].set(x2, y2);
871
872 DIRTY_AFTER_EDIT;
873 }
874 return *this;
875 }
876
rConicTo(SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2,SkScalar w)877 SkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
878 SkScalar w) {
879 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
880 SkPoint pt;
881 this->getLastPt(&pt);
882 return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
883 }
884
cubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)885 SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
886 SkScalar x3, SkScalar y3) {
887 SkDEBUGCODE(this->validate();)
888
889 this->injectMoveToIfNeeded();
890
891 SkPathRef::Editor ed(&fPathRef);
892 SkPoint* pts = ed.growForVerb(kCubic_Verb);
893 pts[0].set(x1, y1);
894 pts[1].set(x2, y2);
895 pts[2].set(x3, y3);
896
897 DIRTY_AFTER_EDIT;
898 return *this;
899 }
900
rCubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)901 SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
902 SkScalar x3, SkScalar y3) {
903 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
904 SkPoint pt;
905 this->getLastPt(&pt);
906 return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
907 pt.fX + x3, pt.fY + y3);
908 }
909
close()910 SkPath& SkPath::close() {
911 SkDEBUGCODE(this->validate();)
912
913 int count = fPathRef->countVerbs();
914 if (count > 0) {
915 switch (fPathRef->atVerb(count - 1)) {
916 case kLine_Verb:
917 case kQuad_Verb:
918 case kConic_Verb:
919 case kCubic_Verb:
920 case kMove_Verb: {
921 SkPathRef::Editor ed(&fPathRef);
922 ed.growForVerb(kClose_Verb);
923 break;
924 }
925 case kClose_Verb:
926 // don't add a close if it's the first verb or a repeat
927 break;
928 default:
929 SkDEBUGFAIL("unexpected verb");
930 break;
931 }
932 }
933
934 // signal that we need a moveTo to follow us (unless we're done)
935 #if 0
936 if (fLastMoveToIndex >= 0) {
937 fLastMoveToIndex = ~fLastMoveToIndex;
938 }
939 #else
940 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
941 #endif
942 return *this;
943 }
944
945 ///////////////////////////////////////////////////////////////////////////////
946
947 namespace {
948
949 template <unsigned N>
950 class PointIterator {
951 public:
PointIterator(SkPath::Direction dir,unsigned startIndex)952 PointIterator(SkPath::Direction dir, unsigned startIndex)
953 : fCurrent(startIndex % N)
954 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
955
current() const956 const SkPoint& current() const {
957 SkASSERT(fCurrent < N);
958 return fPts[fCurrent];
959 }
960
next()961 const SkPoint& next() {
962 fCurrent = (fCurrent + fAdvance) % N;
963 return this->current();
964 }
965
966 protected:
967 SkPoint fPts[N];
968
969 private:
970 unsigned fCurrent;
971 unsigned fAdvance;
972 };
973
974 class RectPointIterator : public PointIterator<4> {
975 public:
RectPointIterator(const SkRect & rect,SkPath::Direction dir,unsigned startIndex)976 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
977 : PointIterator(dir, startIndex) {
978
979 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
980 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
981 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
982 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
983 }
984 };
985
986 class OvalPointIterator : public PointIterator<4> {
987 public:
OvalPointIterator(const SkRect & oval,SkPath::Direction dir,unsigned startIndex)988 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
989 : PointIterator(dir, startIndex) {
990
991 const SkScalar cx = oval.centerX();
992 const SkScalar cy = oval.centerY();
993
994 fPts[0] = SkPoint::Make(cx, oval.fTop);
995 fPts[1] = SkPoint::Make(oval.fRight, cy);
996 fPts[2] = SkPoint::Make(cx, oval.fBottom);
997 fPts[3] = SkPoint::Make(oval.fLeft, cy);
998 }
999 };
1000
1001 class RRectPointIterator : public PointIterator<8> {
1002 public:
RRectPointIterator(const SkRRect & rrect,SkPath::Direction dir,unsigned startIndex)1003 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
1004 : PointIterator(dir, startIndex) {
1005
1006 const SkRect& bounds = rrect.getBounds();
1007 const SkScalar L = bounds.fLeft;
1008 const SkScalar T = bounds.fTop;
1009 const SkScalar R = bounds.fRight;
1010 const SkScalar B = bounds.fBottom;
1011
1012 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
1013 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
1014 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
1015 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
1016 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
1017 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
1018 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
1019 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
1020 }
1021 };
1022
1023 } // anonymous namespace
1024
assert_known_direction(int dir)1025 static void assert_known_direction(int dir) {
1026 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
1027 }
1028
addRect(const SkRect & rect,Direction dir)1029 SkPath& SkPath::addRect(const SkRect& rect, Direction dir) {
1030 return this->addRect(rect, dir, 0);
1031 }
1032
addRect(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom,Direction dir)1033 SkPath& SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
1034 SkScalar bottom, Direction dir) {
1035 return this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
1036 }
1037
addRect(const SkRect & rect,Direction dir,unsigned startIndex)1038 SkPath& SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
1039 assert_known_direction(dir);
1040 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathPriv::FirstDirection)dir
1041 : SkPathPriv::kUnknown_FirstDirection);
1042 SkAutoDisableDirectionCheck addc(this);
1043 SkAutoPathBoundsUpdate apbu(this, rect);
1044
1045 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1046
1047 const int kVerbs = 5; // moveTo + 3x lineTo + close
1048 this->incReserve(kVerbs);
1049
1050 RectPointIterator iter(rect, dir, startIndex);
1051
1052 this->moveTo(iter.current());
1053 this->lineTo(iter.next());
1054 this->lineTo(iter.next());
1055 this->lineTo(iter.next());
1056 this->close();
1057
1058 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1059 return *this;
1060 }
1061
addPoly(const SkPoint pts[],int count,bool close)1062 SkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) {
1063 SkDEBUGCODE(this->validate();)
1064 if (count <= 0) {
1065 return *this;
1066 }
1067
1068 fLastMoveToIndex = fPathRef->countPoints();
1069
1070 // +close makes room for the extra kClose_Verb
1071 SkPathRef::Editor ed(&fPathRef, count+close, count);
1072
1073 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
1074 if (count > 1) {
1075 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
1076 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
1077 }
1078
1079 if (close) {
1080 ed.growForVerb(kClose_Verb);
1081 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1082 }
1083
1084 DIRTY_AFTER_EDIT;
1085 SkDEBUGCODE(this->validate();)
1086 return *this;
1087 }
1088
1089 #include "SkGeometry.h"
1090
arc_is_lone_point(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,SkPoint * pt)1091 static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1092 SkPoint* pt) {
1093 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
1094 // Chrome uses this path to move into and out of ovals. If not
1095 // treated as a special case the moves can distort the oval's
1096 // bounding box (and break the circle special case).
1097 pt->set(oval.fRight, oval.centerY());
1098 return true;
1099 } else if (0 == oval.width() && 0 == oval.height()) {
1100 // Chrome will sometimes create 0 radius round rects. Having degenerate
1101 // quad segments in the path prevents the path from being recognized as
1102 // a rect.
1103 // TODO: optimizing the case where only one of width or height is zero
1104 // should also be considered. This case, however, doesn't seem to be
1105 // as common as the single point case.
1106 pt->set(oval.fRight, oval.fTop);
1107 return true;
1108 }
1109 return false;
1110 }
1111
1112 // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
1113 //
angles_to_unit_vectors(SkScalar startAngle,SkScalar sweepAngle,SkVector * startV,SkVector * stopV,SkRotationDirection * dir)1114 static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
1115 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
1116 startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX);
1117 stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX);
1118
1119 /* If the sweep angle is nearly (but less than) 360, then due to precision
1120 loss in radians-conversion and/or sin/cos, we may end up with coincident
1121 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
1122 of drawing a nearly complete circle (good).
1123 e.g. canvas.drawArc(0, 359.99, ...)
1124 -vs- canvas.drawArc(0, 359.9, ...)
1125 We try to detect this edge case, and tweak the stop vector
1126 */
1127 if (*startV == *stopV) {
1128 SkScalar sw = SkScalarAbs(sweepAngle);
1129 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
1130 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
1131 // make a guess at a tiny angle (in radians) to tweak by
1132 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
1133 // not sure how much will be enough, so we use a loop
1134 do {
1135 stopRad -= deltaRad;
1136 stopV->fY = SkScalarSinCos(stopRad, &stopV->fX);
1137 } while (*startV == *stopV);
1138 }
1139 }
1140 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
1141 }
1142
1143 /**
1144 * If this returns 0, then the caller should just line-to the singlePt, else it should
1145 * ignore singlePt and append the specified number of conics.
1146 */
build_arc_conics(const SkRect & oval,const SkVector & start,const SkVector & stop,SkRotationDirection dir,SkConic conics[SkConic::kMaxConicsForArc],SkPoint * singlePt)1147 static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
1148 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
1149 SkPoint* singlePt) {
1150 SkMatrix matrix;
1151
1152 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
1153 matrix.postTranslate(oval.centerX(), oval.centerY());
1154
1155 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
1156 if (0 == count) {
1157 matrix.mapXY(stop.x(), stop.y(), singlePt);
1158 }
1159 return count;
1160 }
1161
addRoundRect(const SkRect & rect,const SkScalar radii[],Direction dir)1162 SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
1163 Direction dir) {
1164 SkRRect rrect;
1165 rrect.setRectRadii(rect, (const SkVector*) radii);
1166 return this->addRRect(rrect, dir);
1167 }
1168
addRRect(const SkRRect & rrect,Direction dir)1169 SkPath& SkPath::addRRect(const SkRRect& rrect, Direction dir) {
1170 // legacy start indices: 6 (CW) and 7(CCW)
1171 return this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1172 }
1173
addRRect(const SkRRect & rrect,Direction dir,unsigned startIndex)1174 SkPath& SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
1175 assert_known_direction(dir);
1176
1177 bool isRRect = hasOnlyMoveTos();
1178 const SkRect& bounds = rrect.getBounds();
1179
1180 if (rrect.isRect() || rrect.isEmpty()) {
1181 // degenerate(rect) => radii points are collapsing
1182 this->addRect(bounds, dir, (startIndex + 1) / 2);
1183 } else if (rrect.isOval()) {
1184 // degenerate(oval) => line points are collapsing
1185 this->addOval(bounds, dir, startIndex / 2);
1186 } else {
1187 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathPriv::FirstDirection)dir
1188 : SkPathPriv::kUnknown_FirstDirection);
1189
1190 SkAutoPathBoundsUpdate apbu(this, bounds);
1191 SkAutoDisableDirectionCheck addc(this);
1192
1193 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
1194 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
1195 const SkScalar weight = SK_ScalarRoot2Over2;
1196
1197 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1198 const int kVerbs = startsWithConic
1199 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
1200 : 10; // moveTo + 4x lineTo + 4x conicTo + close
1201 this->incReserve(kVerbs);
1202
1203 RRectPointIterator rrectIter(rrect, dir, startIndex);
1204 // Corner iterator indices follow the collapsed radii model,
1205 // adjusted such that the start pt is "behind" the radii start pt.
1206 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
1207 RectPointIterator rectIter(bounds, dir, rectStartIndex);
1208
1209 this->moveTo(rrectIter.current());
1210 if (startsWithConic) {
1211 for (unsigned i = 0; i < 3; ++i) {
1212 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1213 this->lineTo(rrectIter.next());
1214 }
1215 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1216 // final lineTo handled by close().
1217 } else {
1218 for (unsigned i = 0; i < 4; ++i) {
1219 this->lineTo(rrectIter.next());
1220 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1221 }
1222 }
1223 this->close();
1224
1225 SkPathRef::Editor ed(&fPathRef);
1226 ed.setIsRRect(isRRect, dir, startIndex % 8);
1227
1228 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1229 }
1230
1231 SkDEBUGCODE(fPathRef->validate();)
1232 return *this;
1233 }
1234
hasOnlyMoveTos() const1235 bool SkPath::hasOnlyMoveTos() const {
1236 int count = fPathRef->countVerbs();
1237 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
1238 for (int i = 0; i < count; ++i) {
1239 if (*verbs == kLine_Verb ||
1240 *verbs == kQuad_Verb ||
1241 *verbs == kConic_Verb ||
1242 *verbs == kCubic_Verb) {
1243 return false;
1244 }
1245 ++verbs;
1246 }
1247 return true;
1248 }
1249
isZeroLengthSincePoint(int startPtIndex) const1250 bool SkPath::isZeroLengthSincePoint(int startPtIndex) const {
1251 int count = fPathRef->countPoints() - startPtIndex;
1252 if (count < 2) {
1253 return true;
1254 }
1255 const SkPoint* pts = fPathRef.get()->points() + startPtIndex;
1256 const SkPoint& first = *pts;
1257 for (int index = 1; index < count; ++index) {
1258 if (first != pts[index]) {
1259 return false;
1260 }
1261 }
1262 return true;
1263 }
1264
addRoundRect(const SkRect & rect,SkScalar rx,SkScalar ry,Direction dir)1265 SkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1266 Direction dir) {
1267 assert_known_direction(dir);
1268
1269 if (rx < 0 || ry < 0) {
1270 return *this;
1271 }
1272
1273 SkRRect rrect;
1274 rrect.setRectXY(rect, rx, ry);
1275 return this->addRRect(rrect, dir);
1276 }
1277
addOval(const SkRect & oval,Direction dir)1278 SkPath& SkPath::addOval(const SkRect& oval, Direction dir) {
1279 // legacy start index: 1
1280 return this->addOval(oval, dir, 1);
1281 }
1282
addOval(const SkRect & oval,Direction dir,unsigned startPointIndex)1283 SkPath& SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
1284 assert_known_direction(dir);
1285
1286 /* If addOval() is called after previous moveTo(),
1287 this path is still marked as an oval. This is used to
1288 fit into WebKit's calling sequences.
1289 We can't simply check isEmpty() in this case, as additional
1290 moveTo() would mark the path non empty.
1291 */
1292 bool isOval = hasOnlyMoveTos();
1293 if (isOval) {
1294 this->setFirstDirection((SkPathPriv::FirstDirection)dir);
1295 } else {
1296 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1297 }
1298
1299 SkAutoDisableDirectionCheck addc(this);
1300 SkAutoPathBoundsUpdate apbu(this, oval);
1301
1302 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1303 const int kVerbs = 6; // moveTo + 4x conicTo + close
1304 this->incReserve(kVerbs);
1305
1306 OvalPointIterator ovalIter(oval, dir, startPointIndex);
1307 // The corner iterator pts are tracking "behind" the oval/radii pts.
1308 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
1309 const SkScalar weight = SK_ScalarRoot2Over2;
1310
1311 this->moveTo(ovalIter.current());
1312 for (unsigned i = 0; i < 4; ++i) {
1313 this->conicTo(rectIter.next(), ovalIter.next(), weight);
1314 }
1315 this->close();
1316
1317 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1318
1319 SkPathRef::Editor ed(&fPathRef);
1320
1321 ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
1322 return *this;
1323 }
1324
addCircle(SkScalar x,SkScalar y,SkScalar r,Direction dir)1325 SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
1326 if (r > 0) {
1327 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1328 }
1329 return *this;
1330 }
1331
arcTo(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)1332 SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1333 bool forceMoveTo) {
1334 if (oval.width() < 0 || oval.height() < 0) {
1335 return *this;
1336 }
1337
1338 if (fPathRef->countVerbs() == 0) {
1339 forceMoveTo = true;
1340 }
1341
1342 SkPoint lonePt;
1343 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1344 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1345 }
1346
1347 SkVector startV, stopV;
1348 SkRotationDirection dir;
1349 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1350
1351 SkPoint singlePt;
1352
1353 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
1354 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
1355 // arcs from the same oval.
1356 auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
1357 SkPoint lastPt;
1358 if (forceMoveTo) {
1359 this->moveTo(pt);
1360 } else if (!this->getLastPt(&lastPt) ||
1361 !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
1362 !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
1363 this->lineTo(pt);
1364 }
1365 };
1366
1367 // At this point, we know that the arc is not a lone point, but startV == stopV
1368 // indicates that the sweepAngle is too small such that angles_to_unit_vectors
1369 // cannot handle it.
1370 if (startV == stopV) {
1371 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
1372 SkScalar radiusX = oval.width() / 2;
1373 SkScalar radiusY = oval.height() / 2;
1374 // We cannot use SkScalarSinCos function in the next line because
1375 // SkScalarSinCos has a threshold *SkScalarNearlyZero*. When sin(startAngle)
1376 // is 0 and sweepAngle is very small and radius is huge, the expected
1377 // behavior here is to draw a line. But calling SkScalarSinCos will
1378 // make sin(endAngle) to be 0 which will then draw a dot.
1379 singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
1380 oval.centerY() + radiusY * sk_float_sin(endAngle));
1381 addPt(singlePt);
1382 return *this;
1383 }
1384
1385 SkConic conics[SkConic::kMaxConicsForArc];
1386 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1387 if (count) {
1388 this->incReserve(count * 2 + 1);
1389 const SkPoint& pt = conics[0].fPts[0];
1390 addPt(pt);
1391 for (int i = 0; i < count; ++i) {
1392 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1393 }
1394 } else {
1395 addPt(singlePt);
1396 }
1397 return *this;
1398 }
1399
1400 // This converts the SVG arc to conics.
1401 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1402 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1403 // See also SVG implementation notes:
1404 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1405 // Note that arcSweep bool value is flipped from the original implementation.
arcTo(SkScalar rx,SkScalar ry,SkScalar angle,SkPath::ArcSize arcLarge,SkPath::Direction arcSweep,SkScalar x,SkScalar y)1406 SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1407 SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
1408 this->injectMoveToIfNeeded();
1409 SkPoint srcPts[2];
1410 this->getLastPt(&srcPts[0]);
1411 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1412 // joining the endpoints.
1413 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1414 if (!rx || !ry) {
1415 return this->lineTo(x, y);
1416 }
1417 // If the current point and target point for the arc are identical, it should be treated as a
1418 // zero length path. This ensures continuity in animations.
1419 srcPts[1].set(x, y);
1420 if (srcPts[0] == srcPts[1]) {
1421 return this->lineTo(x, y);
1422 }
1423 rx = SkScalarAbs(rx);
1424 ry = SkScalarAbs(ry);
1425 SkVector midPointDistance = srcPts[0] - srcPts[1];
1426 midPointDistance *= 0.5f;
1427
1428 SkMatrix pointTransform;
1429 pointTransform.setRotate(-angle);
1430
1431 SkPoint transformedMidPoint;
1432 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1433 SkScalar squareRx = rx * rx;
1434 SkScalar squareRy = ry * ry;
1435 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1436 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1437
1438 // Check if the radii are big enough to draw the arc, scale radii if not.
1439 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1440 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1441 if (radiiScale > 1) {
1442 radiiScale = SkScalarSqrt(radiiScale);
1443 rx *= radiiScale;
1444 ry *= radiiScale;
1445 }
1446
1447 pointTransform.setScale(1 / rx, 1 / ry);
1448 pointTransform.preRotate(-angle);
1449
1450 SkPoint unitPts[2];
1451 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
1452 SkVector delta = unitPts[1] - unitPts[0];
1453
1454 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1455 SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
1456
1457 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1458 if (SkToBool(arcSweep) != SkToBool(arcLarge)) { // flipped from the original implementation
1459 scaleFactor = -scaleFactor;
1460 }
1461 delta.scale(scaleFactor);
1462 SkPoint centerPoint = unitPts[0] + unitPts[1];
1463 centerPoint *= 0.5f;
1464 centerPoint.offset(-delta.fY, delta.fX);
1465 unitPts[0] -= centerPoint;
1466 unitPts[1] -= centerPoint;
1467 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1468 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1469 SkScalar thetaArc = theta2 - theta1;
1470 if (thetaArc < 0 && !arcSweep) { // arcSweep flipped from the original implementation
1471 thetaArc += SK_ScalarPI * 2;
1472 } else if (thetaArc > 0 && arcSweep) { // arcSweep flipped from the original implementation
1473 thetaArc -= SK_ScalarPI * 2;
1474 }
1475 pointTransform.setRotate(angle);
1476 pointTransform.preScale(rx, ry);
1477
1478 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
1479 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
1480 SkScalar thetaWidth = thetaArc / segments;
1481 SkScalar t = SkScalarTan(0.5f * thetaWidth);
1482 if (!SkScalarIsFinite(t)) {
1483 return *this;
1484 }
1485 SkScalar startTheta = theta1;
1486 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1487 auto scalar_is_integer = [](SkScalar scalar) -> bool {
1488 return scalar == SkScalarFloorToScalar(scalar);
1489 };
1490 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
1491 scalar_is_integer(rx) && scalar_is_integer(ry) &&
1492 scalar_is_integer(x) && scalar_is_integer(y);
1493
1494 for (int i = 0; i < segments; ++i) {
1495 SkScalar endTheta = startTheta + thetaWidth;
1496 SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
1497
1498 unitPts[1].set(cosEndTheta, sinEndTheta);
1499 unitPts[1] += centerPoint;
1500 unitPts[0] = unitPts[1];
1501 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1502 SkPoint mapped[2];
1503 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
1504 /*
1505 Computing the arc width introduces rounding errors that cause arcs to start
1506 outside their marks. A round rect may lose convexity as a result. If the input
1507 values are on integers, place the conic on integers as well.
1508 */
1509 if (expectIntegers) {
1510 SkScalar* mappedScalars = &mapped[0].fX;
1511 for (unsigned index = 0; index < sizeof(mapped) / sizeof(SkScalar); ++index) {
1512 mappedScalars[index] = SkScalarRoundToScalar(mappedScalars[index]);
1513 }
1514 }
1515 this->conicTo(mapped[0], mapped[1], w);
1516 startTheta = endTheta;
1517 }
1518 return *this;
1519 }
1520
rArcTo(SkScalar rx,SkScalar ry,SkScalar xAxisRotate,SkPath::ArcSize largeArc,SkPath::Direction sweep,SkScalar dx,SkScalar dy)1521 SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1522 SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
1523 SkPoint currentPoint;
1524 this->getLastPt(¤tPoint);
1525 return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep,
1526 currentPoint.fX + dx, currentPoint.fY + dy);
1527 }
1528
addArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)1529 SkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1530 if (oval.isEmpty() || 0 == sweepAngle) {
1531 return *this;
1532 }
1533
1534 const SkScalar kFullCircleAngle = SkIntToScalar(360);
1535
1536 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1537 // We can treat the arc as an oval if it begins at one of our legal starting positions.
1538 // See SkPath::addOval() docs.
1539 SkScalar startOver90 = startAngle / 90.f;
1540 SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
1541 SkScalar error = startOver90 - startOver90I;
1542 if (SkScalarNearlyEqual(error, 0)) {
1543 // Index 1 is at startAngle == 0.
1544 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
1545 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
1546 return this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
1547 (unsigned) startIndex);
1548 }
1549 }
1550 return this->arcTo(oval, startAngle, sweepAngle, true);
1551 }
1552
1553 /*
1554 Need to handle the case when the angle is sharp, and our computed end-points
1555 for the arc go behind pt1 and/or p2...
1556 */
arcTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)1557 SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1558 if (radius == 0) {
1559 return this->lineTo(x1, y1);
1560 }
1561
1562 SkVector before, after;
1563
1564 // need to know our prev pt so we can construct tangent vectors
1565 {
1566 SkPoint start;
1567 this->getLastPt(&start);
1568 // Handle degenerate cases by adding a line to the first point and
1569 // bailing out.
1570 before.setNormalize(x1 - start.fX, y1 - start.fY);
1571 after.setNormalize(x2 - x1, y2 - y1);
1572 }
1573
1574 SkScalar cosh = SkPoint::DotProduct(before, after);
1575 SkScalar sinh = SkPoint::CrossProduct(before, after);
1576
1577 if (SkScalarNearlyZero(sinh)) { // angle is too tight
1578 return this->lineTo(x1, y1);
1579 }
1580
1581 SkScalar dist = SkScalarAbs(radius * (1 - cosh) / sinh);
1582
1583 SkScalar xx = x1 - dist * before.fX;
1584 SkScalar yy = y1 - dist * before.fY;
1585 after.setLength(dist);
1586 this->lineTo(xx, yy);
1587 SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
1588 return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1589 }
1590
1591 ///////////////////////////////////////////////////////////////////////////////
1592
addPath(const SkPath & path,SkScalar dx,SkScalar dy,AddPathMode mode)1593 SkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1594 SkMatrix matrix;
1595
1596 matrix.setTranslate(dx, dy);
1597 return this->addPath(path, matrix, mode);
1598 }
1599
addPath(const SkPath & srcPath,const SkMatrix & matrix,AddPathMode mode)1600 SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) {
1601 // Detect if we're trying to add ourself
1602 const SkPath* src = &srcPath;
1603 SkTLazy<SkPath> tmp;
1604 if (this == src) {
1605 src = tmp.set(srcPath);
1606 }
1607
1608 SkPathRef::Editor(&fPathRef, src->countVerbs(), src->countPoints());
1609
1610 RawIter iter(*src);
1611 SkPoint pts[4];
1612 Verb verb;
1613
1614 SkMatrixPriv::MapPtsProc proc = SkMatrixPriv::GetMapPtsProc(matrix);
1615 bool firstVerb = true;
1616 while ((verb = iter.next(pts)) != kDone_Verb) {
1617 switch (verb) {
1618 case kMove_Verb:
1619 proc(matrix, &pts[0], &pts[0], 1);
1620 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1621 injectMoveToIfNeeded(); // In case last contour is closed
1622 SkPoint lastPt;
1623 // don't add lineTo if it is degenerate
1624 if (fLastMoveToIndex < 0 || !this->getLastPt(&lastPt) || lastPt != pts[0]) {
1625 this->lineTo(pts[0]);
1626 }
1627 } else {
1628 this->moveTo(pts[0]);
1629 }
1630 break;
1631 case kLine_Verb:
1632 proc(matrix, &pts[1], &pts[1], 1);
1633 this->lineTo(pts[1]);
1634 break;
1635 case kQuad_Verb:
1636 proc(matrix, &pts[1], &pts[1], 2);
1637 this->quadTo(pts[1], pts[2]);
1638 break;
1639 case kConic_Verb:
1640 proc(matrix, &pts[1], &pts[1], 2);
1641 this->conicTo(pts[1], pts[2], iter.conicWeight());
1642 break;
1643 case kCubic_Verb:
1644 proc(matrix, &pts[1], &pts[1], 3);
1645 this->cubicTo(pts[1], pts[2], pts[3]);
1646 break;
1647 case kClose_Verb:
1648 this->close();
1649 break;
1650 default:
1651 SkDEBUGFAIL("unknown verb");
1652 }
1653 firstVerb = false;
1654 }
1655 return *this;
1656 }
1657
1658 ///////////////////////////////////////////////////////////////////////////////
1659
pts_in_verb(unsigned verb)1660 static int pts_in_verb(unsigned verb) {
1661 static const uint8_t gPtsInVerb[] = {
1662 1, // kMove
1663 1, // kLine
1664 2, // kQuad
1665 2, // kConic
1666 3, // kCubic
1667 0, // kClose
1668 0 // kDone
1669 };
1670
1671 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1672 return gPtsInVerb[verb];
1673 }
1674
1675 // ignore the last point of the 1st contour
reversePathTo(const SkPath & path)1676 SkPath& SkPath::reversePathTo(const SkPath& path) {
1677 const uint8_t* verbs = path.fPathRef->verbsMemBegin(); // points at the last verb
1678 if (!verbs) { // empty path returns nullptr
1679 return *this;
1680 }
1681 const uint8_t* verbsEnd = path.fPathRef->verbs() - 1; // points just past the first verb
1682 SkASSERT(verbsEnd[0] == kMove_Verb);
1683 const SkPoint* pts = path.fPathRef->pointsEnd() - 1;
1684 const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
1685
1686 while (verbs < verbsEnd) {
1687 uint8_t v = *verbs++;
1688 pts -= pts_in_verb(v);
1689 switch (v) {
1690 case kMove_Verb:
1691 // if the path has multiple contours, stop after reversing the last
1692 return *this;
1693 case kLine_Verb:
1694 this->lineTo(pts[0]);
1695 break;
1696 case kQuad_Verb:
1697 this->quadTo(pts[1], pts[0]);
1698 break;
1699 case kConic_Verb:
1700 this->conicTo(pts[1], pts[0], *--conicWeights);
1701 break;
1702 case kCubic_Verb:
1703 this->cubicTo(pts[2], pts[1], pts[0]);
1704 break;
1705 case kClose_Verb:
1706 SkASSERT(verbs - path.fPathRef->verbsMemBegin() == 1);
1707 break;
1708 default:
1709 SkDEBUGFAIL("bad verb");
1710 break;
1711 }
1712 }
1713 return *this;
1714 }
1715
reverseAddPath(const SkPath & srcPath)1716 SkPath& SkPath::reverseAddPath(const SkPath& srcPath) {
1717 // Detect if we're trying to add ourself
1718 const SkPath* src = &srcPath;
1719 SkTLazy<SkPath> tmp;
1720 if (this == src) {
1721 src = tmp.set(srcPath);
1722 }
1723
1724 SkPathRef::Editor ed(&fPathRef, src->countVerbs(), src->countPoints());
1725
1726 const SkPoint* pts = src->fPathRef->pointsEnd();
1727 // we will iterate through src's verbs backwards
1728 const uint8_t* verbs = src->fPathRef->verbsMemBegin(); // points at the last verb
1729 const uint8_t* verbsEnd = src->fPathRef->verbs(); // points just past the first verb
1730 const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd();
1731
1732 bool needMove = true;
1733 bool needClose = false;
1734 while (verbs < verbsEnd) {
1735 uint8_t v = *(verbs++);
1736 int n = pts_in_verb(v);
1737
1738 if (needMove) {
1739 --pts;
1740 this->moveTo(pts->fX, pts->fY);
1741 needMove = false;
1742 }
1743 pts -= n;
1744 switch (v) {
1745 case kMove_Verb:
1746 if (needClose) {
1747 this->close();
1748 needClose = false;
1749 }
1750 needMove = true;
1751 pts += 1; // so we see the point in "if (needMove)" above
1752 break;
1753 case kLine_Verb:
1754 this->lineTo(pts[0]);
1755 break;
1756 case kQuad_Verb:
1757 this->quadTo(pts[1], pts[0]);
1758 break;
1759 case kConic_Verb:
1760 this->conicTo(pts[1], pts[0], *--conicWeights);
1761 break;
1762 case kCubic_Verb:
1763 this->cubicTo(pts[2], pts[1], pts[0]);
1764 break;
1765 case kClose_Verb:
1766 needClose = true;
1767 break;
1768 default:
1769 SkDEBUGFAIL("unexpected verb");
1770 }
1771 }
1772 return *this;
1773 }
1774
1775 ///////////////////////////////////////////////////////////////////////////////
1776
offset(SkScalar dx,SkScalar dy,SkPath * dst) const1777 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1778 SkMatrix matrix;
1779
1780 matrix.setTranslate(dx, dy);
1781 this->transform(matrix, dst);
1782 }
1783
subdivide_cubic_to(SkPath * path,const SkPoint pts[4],int level=2)1784 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1785 int level = 2) {
1786 if (--level >= 0) {
1787 SkPoint tmp[7];
1788
1789 SkChopCubicAtHalf(pts, tmp);
1790 subdivide_cubic_to(path, &tmp[0], level);
1791 subdivide_cubic_to(path, &tmp[3], level);
1792 } else {
1793 path->cubicTo(pts[1], pts[2], pts[3]);
1794 }
1795 }
1796
transform(const SkMatrix & matrix,SkPath * dst) const1797 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
1798 if (matrix.isIdentity()) {
1799 if (dst != nullptr && dst != this) {
1800 *dst = *this;
1801 }
1802 return;
1803 }
1804
1805 SkDEBUGCODE(this->validate();)
1806 if (dst == nullptr) {
1807 dst = (SkPath*)this;
1808 }
1809
1810 if (matrix.hasPerspective()) {
1811 SkPath tmp;
1812 tmp.fFillType = fFillType;
1813
1814 SkPath::Iter iter(*this, false);
1815 SkPoint pts[4];
1816 SkPath::Verb verb;
1817
1818 while ((verb = iter.next(pts, false)) != kDone_Verb) {
1819 switch (verb) {
1820 case kMove_Verb:
1821 tmp.moveTo(pts[0]);
1822 break;
1823 case kLine_Verb:
1824 tmp.lineTo(pts[1]);
1825 break;
1826 case kQuad_Verb:
1827 // promote the quad to a conic
1828 tmp.conicTo(pts[1], pts[2],
1829 SkConic::TransformW(pts, SK_Scalar1, matrix));
1830 break;
1831 case kConic_Verb:
1832 tmp.conicTo(pts[1], pts[2],
1833 SkConic::TransformW(pts, iter.conicWeight(), matrix));
1834 break;
1835 case kCubic_Verb:
1836 subdivide_cubic_to(&tmp, pts);
1837 break;
1838 case kClose_Verb:
1839 tmp.close();
1840 break;
1841 default:
1842 SkDEBUGFAIL("unknown verb");
1843 break;
1844 }
1845 }
1846
1847 dst->swap(tmp);
1848 SkPathRef::Editor ed(&dst->fPathRef);
1849 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
1850 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1851 } else {
1852 Convexity convexity = this->getConvexityOrUnknown();
1853
1854 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
1855
1856 if (this != dst) {
1857 dst->fLastMoveToIndex = fLastMoveToIndex;
1858 dst->fFillType = fFillType;
1859 dst->fIsVolatile = fIsVolatile;
1860 }
1861
1862 // Due to finite/fragile float numerics, we can't assume that a convex path remains
1863 // convex after a transformation, so mark it as unknown here.
1864 // However, some transformations are thought to be safe:
1865 // axis-aligned values under scale/translate.
1866 //
1867 // See skbug.com/8606
1868 // If we can land a robust convex scan-converter, we may be able to relax/remove this
1869 // check, and keep convex paths marked as such after a general transform...
1870 //
1871 if (matrix.isScaleTranslate() && SkPathPriv::IsAxisAligned(*this)) {
1872 dst->setConvexity(convexity);
1873 } else {
1874 dst->setConvexity(kUnknown_Convexity);
1875 }
1876
1877 if (this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
1878 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1879 } else {
1880 SkScalar det2x2 =
1881 matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
1882 matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY);
1883 if (det2x2 < 0) {
1884 dst->setFirstDirection(
1885 SkPathPriv::OppositeFirstDirection(
1886 (SkPathPriv::FirstDirection)this->getFirstDirection()));
1887 } else if (det2x2 > 0) {
1888 dst->setFirstDirection(this->getFirstDirection());
1889 } else {
1890 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1891 }
1892 }
1893
1894 SkDEBUGCODE(dst->validate();)
1895 }
1896 }
1897
1898 ///////////////////////////////////////////////////////////////////////////////
1899 ///////////////////////////////////////////////////////////////////////////////
1900
Iter()1901 SkPath::Iter::Iter() {
1902 #ifdef SK_DEBUG
1903 fPts = nullptr;
1904 fConicWeights = nullptr;
1905 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1906 fForceClose = fCloseLine = false;
1907 fSegmentState = kEmptyContour_SegmentState;
1908 #endif
1909 // need to init enough to make next() harmlessly return kDone_Verb
1910 fVerbs = nullptr;
1911 fVerbStop = nullptr;
1912 fNeedClose = false;
1913 }
1914
Iter(const SkPath & path,bool forceClose)1915 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1916 this->setPath(path, forceClose);
1917 }
1918
setPath(const SkPath & path,bool forceClose)1919 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1920 fPts = path.fPathRef->points();
1921 fVerbs = path.fPathRef->verbs();
1922 fVerbStop = path.fPathRef->verbsMemBegin();
1923 fConicWeights = path.fPathRef->conicWeights();
1924 if (fConicWeights) {
1925 fConicWeights -= 1; // begin one behind
1926 }
1927 fLastPt.fX = fLastPt.fY = 0;
1928 fMoveTo.fX = fMoveTo.fY = 0;
1929 fForceClose = SkToU8(forceClose);
1930 fNeedClose = false;
1931 fSegmentState = kEmptyContour_SegmentState;
1932 }
1933
isClosedContour() const1934 bool SkPath::Iter::isClosedContour() const {
1935 if (fVerbs == nullptr || fVerbs == fVerbStop) {
1936 return false;
1937 }
1938 if (fForceClose) {
1939 return true;
1940 }
1941
1942 const uint8_t* verbs = fVerbs;
1943 const uint8_t* stop = fVerbStop;
1944
1945 if (kMove_Verb == *(verbs - 1)) {
1946 verbs -= 1; // skip the initial moveto
1947 }
1948
1949 while (verbs > stop) {
1950 // verbs points one beyond the current verb, decrement first.
1951 unsigned v = *(--verbs);
1952 if (kMove_Verb == v) {
1953 break;
1954 }
1955 if (kClose_Verb == v) {
1956 return true;
1957 }
1958 }
1959 return false;
1960 }
1961
autoClose(SkPoint pts[2])1962 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1963 SkASSERT(pts);
1964 if (fLastPt != fMoveTo) {
1965 // A special case: if both points are NaN, SkPoint::operation== returns
1966 // false, but the iterator expects that they are treated as the same.
1967 // (consider SkPoint is a 2-dimension float point).
1968 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1969 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1970 return kClose_Verb;
1971 }
1972
1973 pts[0] = fLastPt;
1974 pts[1] = fMoveTo;
1975 fLastPt = fMoveTo;
1976 fCloseLine = true;
1977 return kLine_Verb;
1978 } else {
1979 pts[0] = fMoveTo;
1980 return kClose_Verb;
1981 }
1982 }
1983
cons_moveTo()1984 const SkPoint& SkPath::Iter::cons_moveTo() {
1985 if (fSegmentState == kAfterMove_SegmentState) {
1986 // Set the first return pt to the move pt
1987 fSegmentState = kAfterPrimitive_SegmentState;
1988 return fMoveTo;
1989 }
1990
1991 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
1992 // Set the first return pt to the last pt of the previous primitive.
1993 return fPts[-1];
1994 }
1995
consumeDegenerateSegments(bool exact)1996 void SkPath::Iter::consumeDegenerateSegments(bool exact) {
1997 // We need to step over anything that will not move the current draw point
1998 // forward before the next move is seen
1999 const uint8_t* lastMoveVerb = nullptr;
2000 const SkPoint* lastMovePt = nullptr;
2001 const SkScalar* lastMoveWeight = nullptr;
2002 SkPoint lastPt = fLastPt;
2003 while (fVerbs != fVerbStop) {
2004 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
2005 switch (verb) {
2006 case kMove_Verb:
2007 // Keep a record of this most recent move
2008 lastMoveVerb = fVerbs;
2009 lastMovePt = fPts;
2010 lastMoveWeight = fConicWeights;
2011 lastPt = fPts[0];
2012 fVerbs--;
2013 fPts++;
2014 break;
2015
2016 case kClose_Verb:
2017 // A close when we are in a segment is always valid except when it
2018 // follows a move which follows a segment.
2019 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
2020 return;
2021 }
2022 // A close at any other time must be ignored
2023 fVerbs--;
2024 break;
2025
2026 case kLine_Verb:
2027 if (!IsLineDegenerate(lastPt, fPts[0], exact)) {
2028 if (lastMoveVerb) {
2029 fVerbs = lastMoveVerb;
2030 fPts = lastMovePt;
2031 fConicWeights = lastMoveWeight;
2032 return;
2033 }
2034 return;
2035 }
2036 // Ignore this line and continue
2037 fVerbs--;
2038 fPts++;
2039 break;
2040
2041 case kConic_Verb:
2042 case kQuad_Verb:
2043 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) {
2044 if (lastMoveVerb) {
2045 fVerbs = lastMoveVerb;
2046 fPts = lastMovePt;
2047 fConicWeights = lastMoveWeight;
2048 return;
2049 }
2050 return;
2051 }
2052 // Ignore this line and continue
2053 fVerbs--;
2054 fPts += 2;
2055 fConicWeights += (kConic_Verb == verb);
2056 break;
2057
2058 case kCubic_Verb:
2059 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) {
2060 if (lastMoveVerb) {
2061 fVerbs = lastMoveVerb;
2062 fPts = lastMovePt;
2063 fConicWeights = lastMoveWeight;
2064 return;
2065 }
2066 return;
2067 }
2068 // Ignore this line and continue
2069 fVerbs--;
2070 fPts += 3;
2071 break;
2072
2073 default:
2074 SkDEBUGFAIL("Should never see kDone_Verb");
2075 }
2076 }
2077 }
2078
doNext(SkPoint ptsParam[4])2079 SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
2080 SkASSERT(ptsParam);
2081
2082 if (fVerbs == fVerbStop) {
2083 // Close the curve if requested and if there is some curve to close
2084 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
2085 if (kLine_Verb == this->autoClose(ptsParam)) {
2086 return kLine_Verb;
2087 }
2088 fNeedClose = false;
2089 return kClose_Verb;
2090 }
2091 return kDone_Verb;
2092 }
2093
2094 // fVerbs is one beyond the current verb, decrement first
2095 unsigned verb = *(--fVerbs);
2096 const SkPoint* SK_RESTRICT srcPts = fPts;
2097 SkPoint* SK_RESTRICT pts = ptsParam;
2098
2099 switch (verb) {
2100 case kMove_Verb:
2101 if (fNeedClose) {
2102 fVerbs++; // move back one verb
2103 verb = this->autoClose(pts);
2104 if (verb == kClose_Verb) {
2105 fNeedClose = false;
2106 }
2107 return (Verb)verb;
2108 }
2109 if (fVerbs == fVerbStop) { // might be a trailing moveto
2110 return kDone_Verb;
2111 }
2112 fMoveTo = *srcPts;
2113 pts[0] = *srcPts;
2114 srcPts += 1;
2115 fSegmentState = kAfterMove_SegmentState;
2116 fLastPt = fMoveTo;
2117 fNeedClose = fForceClose;
2118 break;
2119 case kLine_Verb:
2120 pts[0] = this->cons_moveTo();
2121 pts[1] = srcPts[0];
2122 fLastPt = srcPts[0];
2123 fCloseLine = false;
2124 srcPts += 1;
2125 break;
2126 case kConic_Verb:
2127 fConicWeights += 1;
2128 // fall-through
2129 case kQuad_Verb:
2130 pts[0] = this->cons_moveTo();
2131 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
2132 fLastPt = srcPts[1];
2133 srcPts += 2;
2134 break;
2135 case kCubic_Verb:
2136 pts[0] = this->cons_moveTo();
2137 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
2138 fLastPt = srcPts[2];
2139 srcPts += 3;
2140 break;
2141 case kClose_Verb:
2142 verb = this->autoClose(pts);
2143 if (verb == kLine_Verb) {
2144 fVerbs++; // move back one verb
2145 } else {
2146 fNeedClose = false;
2147 fSegmentState = kEmptyContour_SegmentState;
2148 }
2149 fLastPt = fMoveTo;
2150 break;
2151 }
2152 fPts = srcPts;
2153 return (Verb)verb;
2154 }
2155
2156 ///////////////////////////////////////////////////////////////////////////////
2157
2158 #include "SkString.h"
2159 #include "SkStringUtils.h"
2160 #include "SkStream.h"
2161
append_params(SkString * str,const char label[],const SkPoint pts[],int count,SkScalarAsStringType strType,SkScalar conicWeight=-12345)2162 static void append_params(SkString* str, const char label[], const SkPoint pts[],
2163 int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) {
2164 str->append(label);
2165 str->append("(");
2166
2167 const SkScalar* values = &pts[0].fX;
2168 count *= 2;
2169
2170 for (int i = 0; i < count; ++i) {
2171 SkAppendScalar(str, values[i], strType);
2172 if (i < count - 1) {
2173 str->append(", ");
2174 }
2175 }
2176 if (conicWeight != -12345) {
2177 str->append(", ");
2178 SkAppendScalar(str, conicWeight, strType);
2179 }
2180 str->append(");");
2181 if (kHex_SkScalarAsStringType == strType) {
2182 str->append(" // ");
2183 for (int i = 0; i < count; ++i) {
2184 SkAppendScalarDec(str, values[i]);
2185 if (i < count - 1) {
2186 str->append(", ");
2187 }
2188 }
2189 if (conicWeight >= 0) {
2190 str->append(", ");
2191 SkAppendScalarDec(str, conicWeight);
2192 }
2193 }
2194 str->append("\n");
2195 }
2196
dump(SkWStream * wStream,bool forceClose,bool dumpAsHex) const2197 void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
2198 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
2199 Iter iter(*this, forceClose);
2200 SkPoint pts[4];
2201 Verb verb;
2202
2203 SkString builder;
2204 char const * const gFillTypeStrs[] = {
2205 "Winding",
2206 "EvenOdd",
2207 "InverseWinding",
2208 "InverseEvenOdd",
2209 };
2210 builder.printf("path.setFillType(SkPath::k%s_FillType);\n",
2211 gFillTypeStrs[(int) this->getFillType()]);
2212 while ((verb = iter.next(pts, false)) != kDone_Verb) {
2213 switch (verb) {
2214 case kMove_Verb:
2215 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
2216 break;
2217 case kLine_Verb:
2218 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
2219 break;
2220 case kQuad_Verb:
2221 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
2222 break;
2223 case kConic_Verb:
2224 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2225 break;
2226 case kCubic_Verb:
2227 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
2228 break;
2229 case kClose_Verb:
2230 builder.append("path.close();\n");
2231 break;
2232 default:
2233 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
2234 verb = kDone_Verb; // stop the loop
2235 break;
2236 }
2237 if (!wStream && builder.size()) {
2238 SkDebugf("%s", builder.c_str());
2239 builder.reset();
2240 }
2241 }
2242 if (wStream) {
2243 wStream->writeText(builder.c_str());
2244 }
2245 }
2246
dump() const2247 void SkPath::dump() const {
2248 this->dump(nullptr, false, false);
2249 }
2250
dumpHex() const2251 void SkPath::dumpHex() const {
2252 this->dump(nullptr, false, true);
2253 }
2254
2255
isValidImpl() const2256 bool SkPath::isValidImpl() const {
2257 if ((fFillType & ~3) != 0) {
2258 return false;
2259 }
2260
2261 #ifdef SK_DEBUG_PATH
2262 if (!fBoundsIsDirty) {
2263 SkRect bounds;
2264
2265 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2266 if (SkToBool(fIsFinite) != isFinite) {
2267 return false;
2268 }
2269
2270 if (fPathRef->countPoints() <= 1) {
2271 // if we're empty, fBounds may be empty but translated, so we can't
2272 // necessarily compare to bounds directly
2273 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2274 // be [2, 2, 2, 2]
2275 if (!bounds.isEmpty() || !fBounds.isEmpty()) {
2276 return false;
2277 }
2278 } else {
2279 if (bounds.isEmpty()) {
2280 if (!fBounds.isEmpty()) {
2281 return false;
2282 }
2283 } else {
2284 if (!fBounds.isEmpty()) {
2285 if (!fBounds.contains(bounds)) {
2286 return false;
2287 }
2288 }
2289 }
2290 }
2291 }
2292 #endif // SK_DEBUG_PATH
2293 return true;
2294 }
2295
2296 ///////////////////////////////////////////////////////////////////////////////
2297
2298 #ifdef SK_LEGACY_PATH_CONVEXITY // for rebaselining Chrome
2299
sign(SkScalar x)2300 static int sign(SkScalar x) { return x < 0; }
2301 #define kValueNeverReturnedBySign 2
2302
2303 enum DirChange {
2304 kLeft_DirChange,
2305 kRight_DirChange,
2306 kStraight_DirChange,
2307 kBackwards_DirChange,
2308
2309 kInvalid_DirChange
2310 };
2311
2312
almost_equal(SkScalar compA,SkScalar compB)2313 static bool almost_equal(SkScalar compA, SkScalar compB) {
2314 // The error epsilon was empirically derived; worse case round rects
2315 // with a mid point outset by 2x float epsilon in tests had an error
2316 // of 12.
2317 const int epsilon = 16;
2318 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2319 return false;
2320 }
2321 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2322 int aBits = SkFloatAs2sCompliment(compA);
2323 int bBits = SkFloatAs2sCompliment(compB);
2324 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2325 }
2326
approximately_zero_when_compared_to(double x,double y)2327 static bool approximately_zero_when_compared_to(double x, double y) {
2328 return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
2329 }
2330
2331
2332 // only valid for a single contour
2333 struct Convexicator {
ConvexicatorConvexicator2334 Convexicator()
2335 : fPtCount(0)
2336 , fConvexity(SkPath::kConvex_Convexity)
2337 , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
2338 , fIsFinite(true)
2339 , fIsCurve(false)
2340 , fBackwards(false) {
2341 fExpectedDir = kInvalid_DirChange;
2342 // warnings
2343 fPriorPt.set(0,0);
2344 fLastPt.set(0, 0);
2345 fCurrPt.set(0, 0);
2346 fLastVec.set(0, 0);
2347 fFirstVec.set(0, 0);
2348
2349 fDx = fDy = 0;
2350 fSx = fSy = kValueNeverReturnedBySign;
2351 }
2352
getConvexityConvexicator2353 SkPath::Convexity getConvexity() const { return fConvexity; }
2354
2355 /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2356 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2357
addPtConvexicator2358 void addPt(const SkPoint& pt) {
2359 if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
2360 return;
2361 }
2362
2363 if (0 == fPtCount) {
2364 fCurrPt = pt;
2365 ++fPtCount;
2366 } else {
2367 SkVector vec = pt - fCurrPt;
2368 SkScalar lengthSqd = SkPointPriv::LengthSqd(vec);
2369 if (!SkScalarIsFinite(lengthSqd)) {
2370 fIsFinite = false;
2371 } else if (lengthSqd) {
2372 fPriorPt = fLastPt;
2373 fLastPt = fCurrPt;
2374 fCurrPt = pt;
2375 if (++fPtCount == 2) {
2376 fFirstVec = fLastVec = vec;
2377 } else {
2378 SkASSERT(fPtCount > 2);
2379 this->addVec(vec);
2380 }
2381
2382 int sx = sign(vec.fX);
2383 int sy = sign(vec.fY);
2384 fDx += (sx != fSx);
2385 fDy += (sy != fSy);
2386 fSx = sx;
2387 fSy = sy;
2388
2389 if (fDx > 3 || fDy > 3) {
2390 fConvexity = SkPath::kConcave_Convexity;
2391 }
2392 }
2393 }
2394 }
2395
closeConvexicator2396 void close() {
2397 if (fPtCount > 2) {
2398 this->addVec(fFirstVec);
2399 }
2400 }
2401
directionChangeConvexicator2402 DirChange directionChange(const SkVector& curVec) {
2403 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2404
2405 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2406 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2407 largest = SkTMax(largest, -smallest);
2408
2409 if (!almost_equal(largest, largest + cross)) {
2410 int sign = SkScalarSignAsInt(cross);
2411 if (sign) {
2412 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2413 }
2414 }
2415
2416 if (cross) {
2417 double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
2418 double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
2419 double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
2420 double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
2421 double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
2422 if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
2423 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
2424 if (sign) {
2425 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2426 }
2427 }
2428 }
2429
2430 if (!SkScalarNearlyZero(SkPointPriv::LengthSqd(fLastVec),
2431 SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2432 !SkScalarNearlyZero(SkPointPriv::LengthSqd(curVec),
2433 SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2434 fLastVec.dot(curVec) < 0.0f) {
2435 return kBackwards_DirChange;
2436 }
2437
2438 return kStraight_DirChange;
2439 }
2440
hasBackwardsConvexicator2441 bool hasBackwards() const {
2442 return fBackwards;
2443 }
2444
isFiniteConvexicator2445 bool isFinite() const {
2446 return fIsFinite;
2447 }
2448
setCurveConvexicator2449 void setCurve(bool isCurve) {
2450 fIsCurve = isCurve;
2451 }
2452
2453 private:
addVecConvexicator2454 void addVec(const SkVector& vec) {
2455 SkASSERT(vec.fX || vec.fY);
2456 DirChange dir = this->directionChange(vec);
2457 switch (dir) {
2458 case kLeft_DirChange: // fall through
2459 case kRight_DirChange:
2460 if (kInvalid_DirChange == fExpectedDir) {
2461 fExpectedDir = dir;
2462 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2463 : SkPathPriv::kCCW_FirstDirection;
2464 } else if (dir != fExpectedDir) {
2465 fConvexity = SkPath::kConcave_Convexity;
2466 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2467 }
2468 fLastVec = vec;
2469 break;
2470 case kStraight_DirChange:
2471 break;
2472 case kBackwards_DirChange:
2473 if (fIsCurve) {
2474 // If any of the subsequent dir is non-backward, it'll be concave.
2475 // Otherwise, it's still convex.
2476 fExpectedDir = dir;
2477 }
2478 fLastVec = vec;
2479 fBackwards = true;
2480 break;
2481 case kInvalid_DirChange:
2482 SK_ABORT("Use of invalid direction change flag");
2483 break;
2484 }
2485 }
2486
2487 SkPoint fPriorPt;
2488 SkPoint fLastPt;
2489 SkPoint fCurrPt;
2490 // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
2491 // value with the current vec is deemed to be of a significant value.
2492 SkVector fLastVec, fFirstVec;
2493 int fPtCount; // non-degenerate points
2494 DirChange fExpectedDir;
2495 SkPath::Convexity fConvexity;
2496 SkPathPriv::FirstDirection fFirstDirection;
2497 int fDx, fDy, fSx, fSy;
2498 bool fIsFinite;
2499 bool fIsCurve;
2500 bool fBackwards;
2501 };
2502
internalGetConvexity() const2503 SkPath::Convexity SkPath::internalGetConvexity() const {
2504 // Sometimes we think we need to calculate convexity but another thread already did.
2505 auto c = this->getConvexityOrUnknown();
2506 if (c != kUnknown_Convexity) {
2507 return c;
2508 }
2509
2510 SkPoint pts[4];
2511 SkPath::Verb verb;
2512 SkPath::Iter iter(*this, true);
2513
2514 int contourCount = 0;
2515 int count;
2516 Convexicator state;
2517
2518 if (!isFinite()) {
2519 return kUnknown_Convexity;
2520 }
2521 while ((verb = iter.next(pts, false, false)) != SkPath::kDone_Verb) {
2522 switch (verb) {
2523 case kMove_Verb:
2524 if (++contourCount > 1) {
2525 this->setConvexity(kConcave_Convexity);
2526 return kConcave_Convexity;
2527 }
2528 pts[1] = pts[0];
2529 // fall through
2530 case kLine_Verb:
2531 count = 1;
2532 state.setCurve(false);
2533 break;
2534 case kQuad_Verb:
2535 // fall through
2536 case kConic_Verb:
2537 // fall through
2538 case kCubic_Verb:
2539 count = 2 + (kCubic_Verb == verb);
2540 // As an additional enhancement, this could set curve true only
2541 // if the curve is nonlinear
2542 state.setCurve(true);
2543 break;
2544 case kClose_Verb:
2545 state.setCurve(false);
2546 state.close();
2547 count = 0;
2548 break;
2549 default:
2550 SkDEBUGFAIL("bad verb");
2551 this->setConvexity(kConcave_Convexity);
2552 return kConcave_Convexity;
2553 }
2554
2555 for (int i = 1; i <= count; i++) {
2556 state.addPt(pts[i]);
2557 }
2558 // early exit
2559 if (!state.isFinite()) {
2560 return kUnknown_Convexity;
2561 }
2562 if (kConcave_Convexity == state.getConvexity()) {
2563 this->setConvexity(kConcave_Convexity);
2564 return kConcave_Convexity;
2565 }
2566 }
2567 this->setConvexity(state.getConvexity());
2568
2569 if (this->getConvexityOrUnknown() == kConvex_Convexity &&
2570 this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
2571
2572 if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
2573 && !this->getBounds().isEmpty()
2574 && !state.hasBackwards()) {
2575 this->setConvexity(Convexity::kConcave_Convexity);
2576 } else {
2577 this->setFirstDirection(state.getFirstDirection());
2578 }
2579 }
2580 return this->getConvexityOrUnknown();
2581 }
2582
2583 #else
2584
sign(SkScalar x1,SkScalar x2)2585 static int sign(SkScalar x1, SkScalar x2) { SkASSERT(x1 != x2); return x2 < x1; }
sign(SkScalar x)2586 static int sign(SkScalar x) { return x < 0; }
2587 #define kValueNeverReturnedBySign 2
2588
2589 enum DirChange {
2590 kUnknown_DirChange,
2591 kLeft_DirChange,
2592 kRight_DirChange,
2593 kStraight_DirChange,
2594 kConcave_DirChange, // if cross on diagonal is too small, assume concave
2595 kBackwards_DirChange, // if double back, allow simple lines to be convex
2596 kInvalid_DirChange
2597 };
2598
2599
almost_equal(SkScalar compA,SkScalar compB)2600 static bool almost_equal(SkScalar compA, SkScalar compB) {
2601 // The error epsilon was empirically derived; worse case round rects
2602 // with a mid point outset by 2x float epsilon in tests had an error
2603 // of 12.
2604 const int epsilon = 16;
2605 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2606 return false;
2607 }
2608 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2609 int aBits = SkFloatAs2sCompliment(compA);
2610 int bBits = SkFloatAs2sCompliment(compB);
2611 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2612 }
2613
same_sign(SkScalar curr,SkScalar last,SkScalar prior)2614 static DirChange same_sign(SkScalar curr, SkScalar last, SkScalar prior) {
2615 return sign(curr, last) == sign(last, prior) ? kStraight_DirChange : kBackwards_DirChange;
2616 }
2617
2618 // only valid for a single contour
2619 struct Convexicator {
2620
2621 /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2622 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2623
setMovePtConvexicator2624 void setMovePt(const SkPoint& pt) {
2625 fPriorPt = fLastPt = fCurrPt = pt;
2626 }
2627
addPtConvexicator2628 bool addPt(const SkPoint& pt) {
2629 if (fCurrPt == pt) {
2630 return true;
2631 }
2632 fCurrPt = pt;
2633 if (fPriorPt == fLastPt) { // should only be true for first non-zero vector
2634 fFirstPt = pt;
2635 fCurrAligned = pt.fX == fLastPt.fX || pt.fY == fLastPt.fY;
2636 } else if (!this->addVec()) {
2637 return false;
2638 }
2639 fPriorPt = fLastPt;
2640 fLastPt = fCurrPt;
2641 fLastAligned = fCurrAligned;
2642 return true;
2643 }
2644
BySignConvexicator2645 static SkPath::Convexity BySign(const SkPoint points[], int count) {
2646 const SkPoint* last = points + count;
2647 SkPoint currPt = *points++;
2648 SkPoint firstPt = currPt;
2649 int dxes = 0;
2650 int dyes = 0;
2651 int lastSx = kValueNeverReturnedBySign;
2652 int lastSy = kValueNeverReturnedBySign;
2653 for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) {
2654 while (points != last) {
2655 SkVector vec = *points - currPt;
2656 if (!vec.isZero()) {
2657 // give up if vector construction failed
2658 if (!vec.isFinite()) {
2659 return SkPath::kUnknown_Convexity;
2660 }
2661 int sx = sign(vec.fX);
2662 int sy = sign(vec.fY);
2663 dxes += (sx != lastSx);
2664 dyes += (sy != lastSy);
2665 if (dxes > 3 || dyes > 3) {
2666 return SkPath::kConcave_Convexity;
2667 }
2668 lastSx = sx;
2669 lastSy = sy;
2670 }
2671 currPt = *points++;
2672 if (outerLoop) {
2673 break;
2674 }
2675 }
2676 points = &firstPt;
2677 }
2678 return SkPath::kConvex_Convexity; // that is, it may be convex, don't know yet
2679 }
2680
closeConvexicator2681 bool close() {
2682 return this->addPt(fFirstPt);
2683 }
2684
isFiniteConvexicator2685 bool isFinite() const {
2686 return fIsFinite;
2687 }
2688
reversalsConvexicator2689 int reversals() const {
2690 return fReversals;
2691 }
2692
2693 private:
directionChangeConvexicator2694 DirChange directionChange() {
2695 // if both vectors are axis-aligned, don't do cross product
2696 fCurrAligned = fCurrPt.fX == fLastPt.fX || fCurrPt.fY == fLastPt.fY;
2697 if (fLastAligned && fCurrAligned) {
2698 bool noYChange = fCurrPt.fY == fLastPt.fY && fLastPt.fY == fPriorPt.fY;
2699 if (fCurrPt.fX == fLastPt.fX && fLastPt.fX == fPriorPt.fX) {
2700 if (noYChange) {
2701 return kStraight_DirChange;
2702 }
2703 return same_sign(fCurrPt.fY, fLastPt.fY, fPriorPt.fY);
2704 }
2705 if (!noYChange) { // must be turn to left or right
2706 bool flip = fCurrPt.fX != fLastPt.fX;
2707 SkASSERT(flip ? fCurrPt.fY == fLastPt.fY &&
2708 fLastPt.fY != fPriorPt.fY && fLastPt.fX == fPriorPt.fX :
2709 fCurrPt.fY != fLastPt.fY &&
2710 fLastPt.fY == fPriorPt.fY && fLastPt.fX != fPriorPt.fX);
2711 bool product = flip ? (fCurrPt.fX > fLastPt.fX) != (fLastPt.fY > fPriorPt.fY) :
2712 (fCurrPt.fY > fLastPt.fY) == (fLastPt.fX > fPriorPt.fX);
2713 SkDEBUGCODE(SkVector lastV = fLastPt - fPriorPt);
2714 SkDEBUGCODE(SkVector curV = fCurrPt - fLastPt);
2715 SkDEBUGCODE(SkScalar crossV = SkPoint::CrossProduct(lastV, curV));
2716 SkDEBUGCODE(int signV = SkScalarSignAsInt(crossV));
2717 SkASSERT(!signV || signV == (product ? 1 : -1));
2718 return product ? kRight_DirChange : kLeft_DirChange;
2719 }
2720 return same_sign(fCurrPt.fX, fLastPt.fX, fPriorPt.fX);
2721 }
2722 // there are no subtractions above this line; axis aligned paths
2723 // are robust and can handle arbitrary values
2724 SkVector lastVec = fLastPt - fPriorPt;
2725 SkVector curVec = fCurrPt - fLastPt;
2726 SkScalar cross = SkPoint::CrossProduct(lastVec, curVec);
2727 if (!SkScalarIsFinite(cross)) {
2728 return kUnknown_DirChange;
2729 }
2730 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2731 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2732 largest = SkTMax(largest, -smallest);
2733
2734 if (almost_equal(largest, largest + cross)) {
2735 #if SK_TREAT_COLINEAR_DIAGONAL_POINTS_AS_CONCAVE
2736 // colinear diagonals are not allowed; they aren't numerically stable
2737 #define COLINEAR_POINT_DIR_CHANGE kConcave_DirChange
2738 #else
2739 // colinear diagonals are allowed; we can survive dealing with 'close enough'
2740 #define COLINEAR_POINT_DIR_CHANGE kStraight_DirChange
2741 #endif
2742
2743 SkScalar dot = lastVec.dot(curVec);
2744 return dot < 0 ? kBackwards_DirChange : COLINEAR_POINT_DIR_CHANGE;
2745 }
2746 return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange;
2747 }
2748
addVecConvexicator2749 bool addVec() {
2750 DirChange dir = this->directionChange();
2751 switch (dir) {
2752 case kLeft_DirChange: // fall through
2753 case kRight_DirChange:
2754 if (kInvalid_DirChange == fExpectedDir) {
2755 fExpectedDir = dir;
2756 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2757 : SkPathPriv::kCCW_FirstDirection;
2758 } else if (dir != fExpectedDir) {
2759 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2760 return false;
2761 }
2762 break;
2763 case kStraight_DirChange:
2764 break;
2765 case kConcave_DirChange:
2766 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2767 return false;
2768 case kBackwards_DirChange:
2769 // allow path to reverse direction twice
2770 // Given path.moveTo(0, 0); path.lineTo(1, 1);
2771 // - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0)
2772 // - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1)
2773 return ++fReversals < 3;
2774 case kUnknown_DirChange:
2775 return (fIsFinite = false);
2776 case kInvalid_DirChange:
2777 SK_ABORT("Use of invalid direction change flag");
2778 break;
2779 }
2780 return true;
2781 }
2782
2783 SkPoint fFirstPt {0, 0};
2784 SkPoint fPriorPt {0, 0};
2785 SkPoint fLastPt {0, 0};
2786 SkPoint fCurrPt {0, 0};
2787 DirChange fExpectedDir { kInvalid_DirChange };
2788 SkPathPriv::FirstDirection fFirstDirection { SkPathPriv::kUnknown_FirstDirection };
2789 int fReversals { 0 };
2790 bool fIsFinite { true };
2791 bool fLastAligned { true };
2792 bool fCurrAligned { true };
2793 };
2794
internalGetConvexity() const2795 SkPath::Convexity SkPath::internalGetConvexity() const {
2796 SkPoint pts[4];
2797 SkPath::Verb verb;
2798 SkPath::Iter iter(*this, true);
2799 auto setComputedConvexity = [=](Convexity convexity){
2800 SkASSERT(kUnknown_Convexity != convexity);
2801 this->setConvexity(convexity);
2802 return convexity;
2803 };
2804
2805 // Check to see if path changes direction more than three times as quick concave test
2806 int pointCount = this->countPoints();
2807 // last moveTo index may exceed point count if data comes from fuzzer (via SkImageFilter)
2808 if (0 < fLastMoveToIndex && fLastMoveToIndex < pointCount) {
2809 pointCount = fLastMoveToIndex;
2810 }
2811 if (pointCount > 3) {
2812 const SkPoint* points = fPathRef->points();
2813 const SkPoint* last = &points[pointCount];
2814 // only consider the last of the initial move tos
2815 while (SkPath::kMove_Verb == iter.next(pts, false, false)) {
2816 ++points;
2817 }
2818 --points;
2819 SkPath::Convexity convexity = Convexicator::BySign(points, (int) (last - points));
2820 if (SkPath::kConcave_Convexity == convexity) {
2821 return setComputedConvexity(SkPath::kConcave_Convexity);
2822 } else if (SkPath::kUnknown_Convexity == convexity) {
2823 return SkPath::kUnknown_Convexity;
2824 }
2825 iter.setPath(*this, true);
2826 } else if (!this->isFinite()) {
2827 return kUnknown_Convexity;
2828 }
2829
2830 int contourCount = 0;
2831 int count;
2832 Convexicator state;
2833 auto setFail = [=](){
2834 if (!state.isFinite()) {
2835 return SkPath::kUnknown_Convexity;
2836 }
2837 return setComputedConvexity(SkPath::kConcave_Convexity);
2838 };
2839
2840 while ((verb = iter.next(pts, false, false)) != SkPath::kDone_Verb) {
2841 switch (verb) {
2842 case kMove_Verb:
2843 if (++contourCount > 1) {
2844 return setComputedConvexity(kConcave_Convexity);
2845 }
2846 state.setMovePt(pts[0]);
2847 count = 0;
2848 break;
2849 case kLine_Verb:
2850 count = 1;
2851 break;
2852 case kQuad_Verb:
2853 // fall through
2854 case kConic_Verb:
2855 count = 2;
2856 break;
2857 case kCubic_Verb:
2858 count = 3;
2859 break;
2860 case kClose_Verb:
2861 if (!state.close()) {
2862 return setFail();
2863 }
2864 count = 0;
2865 break;
2866 default:
2867 SkDEBUGFAIL("bad verb");
2868 return setComputedConvexity(kConcave_Convexity);
2869 }
2870 for (int i = 1; i <= count; i++) {
2871 if (!state.addPt(pts[i])) {
2872 return setFail();
2873 }
2874 }
2875 }
2876
2877 if (this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
2878 if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
2879 && !this->getBounds().isEmpty()) {
2880 return setComputedConvexity(state.reversals() < 3 ?
2881 kConvex_Convexity : kConcave_Convexity);
2882 }
2883 this->setFirstDirection(state.getFirstDirection());
2884 }
2885 return setComputedConvexity(kConvex_Convexity);
2886 }
2887
IsConvex(const SkPoint points[],int count)2888 bool SkPathPriv::IsConvex(const SkPoint points[], int count) {
2889 SkPath::Convexity convexity = Convexicator::BySign(points, count);
2890 if (SkPath::kConvex_Convexity != convexity) {
2891 return false;
2892 }
2893 Convexicator state;
2894 state.setMovePt(points[0]);
2895 for (int i = 1; i < count; i++) {
2896 if (!state.addPt(points[i])) {
2897 return false;
2898 }
2899 }
2900 if (!state.addPt(points[0])) {
2901 return false;
2902 }
2903 if (!state.close()) {
2904 return false;
2905 }
2906 return state.getFirstDirection() != SkPathPriv::kUnknown_FirstDirection
2907 || state.reversals() < 3;
2908 }
2909
2910 #endif
2911
2912 ///////////////////////////////////////////////////////////////////////////////
2913
2914 class ContourIter {
2915 public:
2916 ContourIter(const SkPathRef& pathRef);
2917
done() const2918 bool done() const { return fDone; }
2919 // if !done() then these may be called
count() const2920 int count() const { return fCurrPtCount; }
pts() const2921 const SkPoint* pts() const { return fCurrPt; }
2922 void next();
2923
2924 private:
2925 int fCurrPtCount;
2926 const SkPoint* fCurrPt;
2927 const uint8_t* fCurrVerb;
2928 const uint8_t* fStopVerbs;
2929 const SkScalar* fCurrConicWeight;
2930 bool fDone;
2931 SkDEBUGCODE(int fContourCounter;)
2932 };
2933
ContourIter(const SkPathRef & pathRef)2934 ContourIter::ContourIter(const SkPathRef& pathRef) {
2935 fStopVerbs = pathRef.verbsMemBegin();
2936 fDone = false;
2937 fCurrPt = pathRef.points();
2938 fCurrVerb = pathRef.verbs();
2939 fCurrConicWeight = pathRef.conicWeights();
2940 fCurrPtCount = 0;
2941 SkDEBUGCODE(fContourCounter = 0;)
2942 this->next();
2943 }
2944
next()2945 void ContourIter::next() {
2946 if (fCurrVerb <= fStopVerbs) {
2947 fDone = true;
2948 }
2949 if (fDone) {
2950 return;
2951 }
2952
2953 // skip pts of prev contour
2954 fCurrPt += fCurrPtCount;
2955
2956 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
2957 int ptCount = 1; // moveTo
2958 const uint8_t* verbs = fCurrVerb;
2959
2960 for (--verbs; verbs > fStopVerbs; --verbs) {
2961 switch (verbs[~0]) {
2962 case SkPath::kMove_Verb:
2963 goto CONTOUR_END;
2964 case SkPath::kLine_Verb:
2965 ptCount += 1;
2966 break;
2967 case SkPath::kConic_Verb:
2968 fCurrConicWeight += 1;
2969 // fall-through
2970 case SkPath::kQuad_Verb:
2971 ptCount += 2;
2972 break;
2973 case SkPath::kCubic_Verb:
2974 ptCount += 3;
2975 break;
2976 case SkPath::kClose_Verb:
2977 break;
2978 default:
2979 SkDEBUGFAIL("unexpected verb");
2980 break;
2981 }
2982 }
2983 CONTOUR_END:
2984 fCurrPtCount = ptCount;
2985 fCurrVerb = verbs;
2986 SkDEBUGCODE(++fContourCounter;)
2987 }
2988
2989 // returns cross product of (p1 - p0) and (p2 - p0)
cross_prod(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)2990 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2991 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2992 // We may get 0 when the above subtracts underflow. We expect this to be
2993 // very rare and lazily promote to double.
2994 if (0 == cross) {
2995 double p0x = SkScalarToDouble(p0.fX);
2996 double p0y = SkScalarToDouble(p0.fY);
2997
2998 double p1x = SkScalarToDouble(p1.fX);
2999 double p1y = SkScalarToDouble(p1.fY);
3000
3001 double p2x = SkScalarToDouble(p2.fX);
3002 double p2y = SkScalarToDouble(p2.fY);
3003
3004 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
3005 (p1y - p0y) * (p2x - p0x));
3006
3007 }
3008 return cross;
3009 }
3010
3011 // Returns the first pt with the maximum Y coordinate
find_max_y(const SkPoint pts[],int count)3012 static int find_max_y(const SkPoint pts[], int count) {
3013 SkASSERT(count > 0);
3014 SkScalar max = pts[0].fY;
3015 int firstIndex = 0;
3016 for (int i = 1; i < count; ++i) {
3017 SkScalar y = pts[i].fY;
3018 if (y > max) {
3019 max = y;
3020 firstIndex = i;
3021 }
3022 }
3023 return firstIndex;
3024 }
3025
find_diff_pt(const SkPoint pts[],int index,int n,int inc)3026 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
3027 int i = index;
3028 for (;;) {
3029 i = (i + inc) % n;
3030 if (i == index) { // we wrapped around, so abort
3031 break;
3032 }
3033 if (pts[index] != pts[i]) { // found a different point, success!
3034 break;
3035 }
3036 }
3037 return i;
3038 }
3039
3040 /**
3041 * Starting at index, and moving forward (incrementing), find the xmin and
3042 * xmax of the contiguous points that have the same Y.
3043 */
find_min_max_x_at_y(const SkPoint pts[],int index,int n,int * maxIndexPtr)3044 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
3045 int* maxIndexPtr) {
3046 const SkScalar y = pts[index].fY;
3047 SkScalar min = pts[index].fX;
3048 SkScalar max = min;
3049 int minIndex = index;
3050 int maxIndex = index;
3051 for (int i = index + 1; i < n; ++i) {
3052 if (pts[i].fY != y) {
3053 break;
3054 }
3055 SkScalar x = pts[i].fX;
3056 if (x < min) {
3057 min = x;
3058 minIndex = i;
3059 } else if (x > max) {
3060 max = x;
3061 maxIndex = i;
3062 }
3063 }
3064 *maxIndexPtr = maxIndex;
3065 return minIndex;
3066 }
3067
crossToDir(SkScalar cross,SkPathPriv::FirstDirection * dir)3068 static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
3069 *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
3070 }
3071
3072 /*
3073 * We loop through all contours, and keep the computed cross-product of the
3074 * contour that contained the global y-max. If we just look at the first
3075 * contour, we may find one that is wound the opposite way (correctly) since
3076 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
3077 * that is outer most (or at least has the global y-max) before we can consider
3078 * its cross product.
3079 */
CheapComputeFirstDirection(const SkPath & path,FirstDirection * dir)3080 bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
3081 auto d = path.getFirstDirection();
3082 if (d != kUnknown_FirstDirection) {
3083 *dir = static_cast<FirstDirection>(d);
3084 return true;
3085 }
3086
3087 // We don't want to pay the cost for computing convexity if it is unknown,
3088 // so we call getConvexityOrUnknown() instead of isConvex().
3089 if (path.getConvexityOrUnknown() == SkPath::kConvex_Convexity) {
3090 SkASSERT(path.getFirstDirection() == kUnknown_FirstDirection);
3091 *dir = static_cast<FirstDirection>(path.getFirstDirection());
3092 return false;
3093 }
3094
3095 ContourIter iter(*path.fPathRef.get());
3096
3097 // initialize with our logical y-min
3098 SkScalar ymax = path.getBounds().fTop;
3099 SkScalar ymaxCross = 0;
3100
3101 for (; !iter.done(); iter.next()) {
3102 int n = iter.count();
3103 if (n < 3) {
3104 continue;
3105 }
3106
3107 const SkPoint* pts = iter.pts();
3108 SkScalar cross = 0;
3109 int index = find_max_y(pts, n);
3110 if (pts[index].fY < ymax) {
3111 continue;
3112 }
3113
3114 // If there is more than 1 distinct point at the y-max, we take the
3115 // x-min and x-max of them and just subtract to compute the dir.
3116 if (pts[(index + 1) % n].fY == pts[index].fY) {
3117 int maxIndex;
3118 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
3119 if (minIndex == maxIndex) {
3120 goto TRY_CROSSPROD;
3121 }
3122 SkASSERT(pts[minIndex].fY == pts[index].fY);
3123 SkASSERT(pts[maxIndex].fY == pts[index].fY);
3124 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
3125 // we just subtract the indices, and let that auto-convert to
3126 // SkScalar, since we just want - or + to signal the direction.
3127 cross = minIndex - maxIndex;
3128 } else {
3129 TRY_CROSSPROD:
3130 // Find a next and prev index to use for the cross-product test,
3131 // but we try to find pts that form non-zero vectors from pts[index]
3132 //
3133 // Its possible that we can't find two non-degenerate vectors, so
3134 // we have to guard our search (e.g. all the pts could be in the
3135 // same place).
3136
3137 // we pass n - 1 instead of -1 so we don't foul up % operator by
3138 // passing it a negative LH argument.
3139 int prev = find_diff_pt(pts, index, n, n - 1);
3140 if (prev == index) {
3141 // completely degenerate, skip to next contour
3142 continue;
3143 }
3144 int next = find_diff_pt(pts, index, n, 1);
3145 SkASSERT(next != index);
3146 cross = cross_prod(pts[prev], pts[index], pts[next]);
3147 // if we get a zero and the points are horizontal, then we look at the spread in
3148 // x-direction. We really should continue to walk away from the degeneracy until
3149 // there is a divergence.
3150 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
3151 // construct the subtract so we get the correct Direction below
3152 cross = pts[index].fX - pts[next].fX;
3153 }
3154 }
3155
3156 if (cross) {
3157 // record our best guess so far
3158 ymax = pts[index].fY;
3159 ymaxCross = cross;
3160 }
3161 }
3162 if (ymaxCross) {
3163 crossToDir(ymaxCross, dir);
3164 path.setFirstDirection(*dir);
3165 return true;
3166 } else {
3167 return false;
3168 }
3169 }
3170
3171 ///////////////////////////////////////////////////////////////////////////////
3172
between(SkScalar a,SkScalar b,SkScalar c)3173 static bool between(SkScalar a, SkScalar b, SkScalar c) {
3174 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
3175 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
3176 return (a - b) * (c - b) <= 0;
3177 }
3178
eval_cubic_pts(SkScalar c0,SkScalar c1,SkScalar c2,SkScalar c3,SkScalar t)3179 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
3180 SkScalar t) {
3181 SkScalar A = c3 + 3*(c1 - c2) - c0;
3182 SkScalar B = 3*(c2 - c1 - c1 + c0);
3183 SkScalar C = 3*(c1 - c0);
3184 SkScalar D = c0;
3185 return poly_eval(A, B, C, D, t);
3186 }
3187
find_minmax(const SkPoint pts[],SkScalar * minPtr,SkScalar * maxPtr)3188 template <size_t N> static void find_minmax(const SkPoint pts[],
3189 SkScalar* minPtr, SkScalar* maxPtr) {
3190 SkScalar min, max;
3191 min = max = pts[0].fX;
3192 for (size_t i = 1; i < N; ++i) {
3193 min = SkMinScalar(min, pts[i].fX);
3194 max = SkMaxScalar(max, pts[i].fX);
3195 }
3196 *minPtr = min;
3197 *maxPtr = max;
3198 }
3199
checkOnCurve(SkScalar x,SkScalar y,const SkPoint & start,const SkPoint & end)3200 static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
3201 if (start.fY == end.fY) {
3202 return between(start.fX, x, end.fX) && x != end.fX;
3203 } else {
3204 return x == start.fX && y == start.fY;
3205 }
3206 }
3207
winding_mono_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3208 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3209 SkScalar y0 = pts[0].fY;
3210 SkScalar y3 = pts[3].fY;
3211
3212 int dir = 1;
3213 if (y0 > y3) {
3214 using std::swap;
3215 swap(y0, y3);
3216 dir = -1;
3217 }
3218 if (y < y0 || y > y3) {
3219 return 0;
3220 }
3221 if (checkOnCurve(x, y, pts[0], pts[3])) {
3222 *onCurveCount += 1;
3223 return 0;
3224 }
3225 if (y == y3) {
3226 return 0;
3227 }
3228
3229 // quickreject or quickaccept
3230 SkScalar min, max;
3231 find_minmax<4>(pts, &min, &max);
3232 if (x < min) {
3233 return 0;
3234 }
3235 if (x > max) {
3236 return dir;
3237 }
3238
3239 // compute the actual x(t) value
3240 SkScalar t;
3241 if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
3242 return 0;
3243 }
3244 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
3245 if (SkScalarNearlyEqual(xt, x)) {
3246 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points
3247 *onCurveCount += 1;
3248 return 0;
3249 }
3250 }
3251 return xt < x ? dir : 0;
3252 }
3253
winding_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3254 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3255 SkPoint dst[10];
3256 int n = SkChopCubicAtYExtrema(pts, dst);
3257 int w = 0;
3258 for (int i = 0; i <= n; ++i) {
3259 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
3260 }
3261 return w;
3262 }
3263
conic_eval_numerator(const SkScalar src[],SkScalar w,SkScalar t)3264 static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
3265 SkASSERT(src);
3266 SkASSERT(t >= 0 && t <= 1);
3267 SkScalar src2w = src[2] * w;
3268 SkScalar C = src[0];
3269 SkScalar A = src[4] - 2 * src2w + C;
3270 SkScalar B = 2 * (src2w - C);
3271 return poly_eval(A, B, C, t);
3272 }
3273
3274
conic_eval_denominator(SkScalar w,SkScalar t)3275 static double conic_eval_denominator(SkScalar w, SkScalar t) {
3276 SkScalar B = 2 * (w - 1);
3277 SkScalar C = 1;
3278 SkScalar A = -B;
3279 return poly_eval(A, B, C, t);
3280 }
3281
winding_mono_conic(const SkConic & conic,SkScalar x,SkScalar y,int * onCurveCount)3282 static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
3283 const SkPoint* pts = conic.fPts;
3284 SkScalar y0 = pts[0].fY;
3285 SkScalar y2 = pts[2].fY;
3286
3287 int dir = 1;
3288 if (y0 > y2) {
3289 using std::swap;
3290 swap(y0, y2);
3291 dir = -1;
3292 }
3293 if (y < y0 || y > y2) {
3294 return 0;
3295 }
3296 if (checkOnCurve(x, y, pts[0], pts[2])) {
3297 *onCurveCount += 1;
3298 return 0;
3299 }
3300 if (y == y2) {
3301 return 0;
3302 }
3303
3304 SkScalar roots[2];
3305 SkScalar A = pts[2].fY;
3306 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
3307 SkScalar C = pts[0].fY;
3308 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3309 B -= C; // B = b*w - w * yCept + yCept - a
3310 C -= y;
3311 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3312 SkASSERT(n <= 1);
3313 SkScalar xt;
3314 if (0 == n) {
3315 // zero roots are returned only when y0 == y
3316 // Need [0] if dir == 1
3317 // and [2] if dir == -1
3318 xt = pts[1 - dir].fX;
3319 } else {
3320 SkScalar t = roots[0];
3321 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
3322 }
3323 if (SkScalarNearlyEqual(xt, x)) {
3324 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
3325 *onCurveCount += 1;
3326 return 0;
3327 }
3328 }
3329 return xt < x ? dir : 0;
3330 }
3331
is_mono_quad(SkScalar y0,SkScalar y1,SkScalar y2)3332 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
3333 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
3334 if (y0 == y1) {
3335 return true;
3336 }
3337 if (y0 < y1) {
3338 return y1 <= y2;
3339 } else {
3340 return y1 >= y2;
3341 }
3342 }
3343
winding_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar weight,int * onCurveCount)3344 static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
3345 int* onCurveCount) {
3346 SkConic conic(pts, weight);
3347 SkConic chopped[2];
3348 // If the data points are very large, the conic may not be monotonic but may also
3349 // fail to chop. Then, the chopper does not split the original conic in two.
3350 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
3351 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
3352 if (!isMono) {
3353 w += winding_mono_conic(chopped[1], x, y, onCurveCount);
3354 }
3355 return w;
3356 }
3357
winding_mono_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3358 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3359 SkScalar y0 = pts[0].fY;
3360 SkScalar y2 = pts[2].fY;
3361
3362 int dir = 1;
3363 if (y0 > y2) {
3364 using std::swap;
3365 swap(y0, y2);
3366 dir = -1;
3367 }
3368 if (y < y0 || y > y2) {
3369 return 0;
3370 }
3371 if (checkOnCurve(x, y, pts[0], pts[2])) {
3372 *onCurveCount += 1;
3373 return 0;
3374 }
3375 if (y == y2) {
3376 return 0;
3377 }
3378 // bounds check on X (not required. is it faster?)
3379 #if 0
3380 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
3381 return 0;
3382 }
3383 #endif
3384
3385 SkScalar roots[2];
3386 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3387 2 * (pts[1].fY - pts[0].fY),
3388 pts[0].fY - y,
3389 roots);
3390 SkASSERT(n <= 1);
3391 SkScalar xt;
3392 if (0 == n) {
3393 // zero roots are returned only when y0 == y
3394 // Need [0] if dir == 1
3395 // and [2] if dir == -1
3396 xt = pts[1 - dir].fX;
3397 } else {
3398 SkScalar t = roots[0];
3399 SkScalar C = pts[0].fX;
3400 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3401 SkScalar B = 2 * (pts[1].fX - C);
3402 xt = poly_eval(A, B, C, t);
3403 }
3404 if (SkScalarNearlyEqual(xt, x)) {
3405 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
3406 *onCurveCount += 1;
3407 return 0;
3408 }
3409 }
3410 return xt < x ? dir : 0;
3411 }
3412
winding_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3413 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3414 SkPoint dst[5];
3415 int n = 0;
3416
3417 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
3418 n = SkChopQuadAtYExtrema(pts, dst);
3419 pts = dst;
3420 }
3421 int w = winding_mono_quad(pts, x, y, onCurveCount);
3422 if (n > 0) {
3423 w += winding_mono_quad(&pts[2], x, y, onCurveCount);
3424 }
3425 return w;
3426 }
3427
winding_line(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3428 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3429 SkScalar x0 = pts[0].fX;
3430 SkScalar y0 = pts[0].fY;
3431 SkScalar x1 = pts[1].fX;
3432 SkScalar y1 = pts[1].fY;
3433
3434 SkScalar dy = y1 - y0;
3435
3436 int dir = 1;
3437 if (y0 > y1) {
3438 using std::swap;
3439 swap(y0, y1);
3440 dir = -1;
3441 }
3442 if (y < y0 || y > y1) {
3443 return 0;
3444 }
3445 if (checkOnCurve(x, y, pts[0], pts[1])) {
3446 *onCurveCount += 1;
3447 return 0;
3448 }
3449 if (y == y1) {
3450 return 0;
3451 }
3452 SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
3453
3454 if (!cross) {
3455 // zero cross means the point is on the line, and since the case where
3456 // y of the query point is at the end point is handled above, we can be
3457 // sure that we're on the line (excluding the end point) here
3458 if (x != x1 || y != pts[1].fY) {
3459 *onCurveCount += 1;
3460 }
3461 dir = 0;
3462 } else if (SkScalarSignAsInt(cross) == dir) {
3463 dir = 0;
3464 }
3465 return dir;
3466 }
3467
tangent_cubic(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3468 static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
3469 SkTDArray<SkVector>* tangents) {
3470 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
3471 && !between(pts[2].fY, y, pts[3].fY)) {
3472 return;
3473 }
3474 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
3475 && !between(pts[2].fX, x, pts[3].fX)) {
3476 return;
3477 }
3478 SkPoint dst[10];
3479 int n = SkChopCubicAtYExtrema(pts, dst);
3480 for (int i = 0; i <= n; ++i) {
3481 SkPoint* c = &dst[i * 3];
3482 SkScalar t;
3483 if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
3484 continue;
3485 }
3486 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
3487 if (!SkScalarNearlyEqual(x, xt)) {
3488 continue;
3489 }
3490 SkVector tangent;
3491 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
3492 tangents->push_back(tangent);
3493 }
3494 }
3495
tangent_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar w,SkTDArray<SkVector> * tangents)3496 static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
3497 SkTDArray<SkVector>* tangents) {
3498 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3499 return;
3500 }
3501 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3502 return;
3503 }
3504 SkScalar roots[2];
3505 SkScalar A = pts[2].fY;
3506 SkScalar B = pts[1].fY * w - y * w + y;
3507 SkScalar C = pts[0].fY;
3508 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3509 B -= C; // B = b*w - w * yCept + yCept - a
3510 C -= y;
3511 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3512 for (int index = 0; index < n; ++index) {
3513 SkScalar t = roots[index];
3514 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
3515 if (!SkScalarNearlyEqual(x, xt)) {
3516 continue;
3517 }
3518 SkConic conic(pts, w);
3519 tangents->push_back(conic.evalTangentAt(t));
3520 }
3521 }
3522
tangent_quad(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3523 static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
3524 SkTDArray<SkVector>* tangents) {
3525 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3526 return;
3527 }
3528 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3529 return;
3530 }
3531 SkScalar roots[2];
3532 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3533 2 * (pts[1].fY - pts[0].fY),
3534 pts[0].fY - y,
3535 roots);
3536 for (int index = 0; index < n; ++index) {
3537 SkScalar t = roots[index];
3538 SkScalar C = pts[0].fX;
3539 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3540 SkScalar B = 2 * (pts[1].fX - C);
3541 SkScalar xt = poly_eval(A, B, C, t);
3542 if (!SkScalarNearlyEqual(x, xt)) {
3543 continue;
3544 }
3545 tangents->push_back(SkEvalQuadTangentAt(pts, t));
3546 }
3547 }
3548
tangent_line(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3549 static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
3550 SkTDArray<SkVector>* tangents) {
3551 SkScalar y0 = pts[0].fY;
3552 SkScalar y1 = pts[1].fY;
3553 if (!between(y0, y, y1)) {
3554 return;
3555 }
3556 SkScalar x0 = pts[0].fX;
3557 SkScalar x1 = pts[1].fX;
3558 if (!between(x0, x, x1)) {
3559 return;
3560 }
3561 SkScalar dx = x1 - x0;
3562 SkScalar dy = y1 - y0;
3563 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3564 return;
3565 }
3566 SkVector v;
3567 v.set(dx, dy);
3568 tangents->push_back(v);
3569 }
3570
contains_inclusive(const SkRect & r,SkScalar x,SkScalar y)3571 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3572 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3573 }
3574
contains(SkScalar x,SkScalar y) const3575 bool SkPath::contains(SkScalar x, SkScalar y) const {
3576 bool isInverse = this->isInverseFillType();
3577 if (this->isEmpty()) {
3578 return isInverse;
3579 }
3580
3581 if (!contains_inclusive(this->getBounds(), x, y)) {
3582 return isInverse;
3583 }
3584
3585 SkPath::Iter iter(*this, true);
3586 bool done = false;
3587 int w = 0;
3588 int onCurveCount = 0;
3589 do {
3590 SkPoint pts[4];
3591 switch (iter.next(pts, false)) {
3592 case SkPath::kMove_Verb:
3593 case SkPath::kClose_Verb:
3594 break;
3595 case SkPath::kLine_Verb:
3596 w += winding_line(pts, x, y, &onCurveCount);
3597 break;
3598 case SkPath::kQuad_Verb:
3599 w += winding_quad(pts, x, y, &onCurveCount);
3600 break;
3601 case SkPath::kConic_Verb:
3602 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3603 break;
3604 case SkPath::kCubic_Verb:
3605 w += winding_cubic(pts, x, y, &onCurveCount);
3606 break;
3607 case SkPath::kDone_Verb:
3608 done = true;
3609 break;
3610 }
3611 } while (!done);
3612 bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
3613 || SkPath::kInverseEvenOdd_FillType == this->getFillType();
3614 if (evenOddFill) {
3615 w &= 1;
3616 }
3617 if (w) {
3618 return !isInverse;
3619 }
3620 if (onCurveCount <= 1) {
3621 return SkToBool(onCurveCount) ^ isInverse;
3622 }
3623 if ((onCurveCount & 1) || evenOddFill) {
3624 return SkToBool(onCurveCount & 1) ^ isInverse;
3625 }
3626 // If the point touches an even number of curves, and the fill is winding, check for
3627 // coincidence. Count coincidence as places where the on curve points have identical tangents.
3628 iter.setPath(*this, true);
3629 done = false;
3630 SkTDArray<SkVector> tangents;
3631 do {
3632 SkPoint pts[4];
3633 int oldCount = tangents.count();
3634 switch (iter.next(pts, false)) {
3635 case SkPath::kMove_Verb:
3636 case SkPath::kClose_Verb:
3637 break;
3638 case SkPath::kLine_Verb:
3639 tangent_line(pts, x, y, &tangents);
3640 break;
3641 case SkPath::kQuad_Verb:
3642 tangent_quad(pts, x, y, &tangents);
3643 break;
3644 case SkPath::kConic_Verb:
3645 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3646 break;
3647 case SkPath::kCubic_Verb:
3648 tangent_cubic(pts, x, y, &tangents);
3649 break;
3650 case SkPath::kDone_Verb:
3651 done = true;
3652 break;
3653 }
3654 if (tangents.count() > oldCount) {
3655 int last = tangents.count() - 1;
3656 const SkVector& tangent = tangents[last];
3657 if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) {
3658 tangents.remove(last);
3659 } else {
3660 for (int index = 0; index < last; ++index) {
3661 const SkVector& test = tangents[index];
3662 if (SkScalarNearlyZero(test.cross(tangent))
3663 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3664 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3665 tangents.remove(last);
3666 tangents.removeShuffle(index);
3667 break;
3668 }
3669 }
3670 }
3671 }
3672 } while (!done);
3673 return SkToBool(tangents.count()) ^ isInverse;
3674 }
3675
ConvertConicToQuads(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,SkScalar w,SkPoint pts[],int pow2)3676 int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3677 SkScalar w, SkPoint pts[], int pow2) {
3678 const SkConic conic(p0, p1, p2, w);
3679 return conic.chopIntoQuadsPOW2(pts, pow2);
3680 }
3681
IsSimpleClosedRect(const SkPath & path,SkRect * rect,SkPath::Direction * direction,unsigned * start)3682 bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
3683 unsigned* start) {
3684 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3685 return false;
3686 }
3687 SkPath::RawIter iter(path);
3688 SkPoint verbPts[4];
3689 SkPath::Verb v;
3690 SkPoint rectPts[5];
3691 int rectPtCnt = 0;
3692 while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
3693 switch (v) {
3694 case SkPath::kMove_Verb:
3695 if (0 != rectPtCnt) {
3696 return false;
3697 }
3698 rectPts[0] = verbPts[0];
3699 ++rectPtCnt;
3700 break;
3701 case SkPath::kLine_Verb:
3702 if (5 == rectPtCnt) {
3703 return false;
3704 }
3705 rectPts[rectPtCnt] = verbPts[1];
3706 ++rectPtCnt;
3707 break;
3708 case SkPath::kClose_Verb:
3709 if (4 == rectPtCnt) {
3710 rectPts[4] = rectPts[0];
3711 rectPtCnt = 5;
3712 }
3713 break;
3714 default:
3715 return false;
3716 }
3717 }
3718 if (rectPtCnt < 5) {
3719 return false;
3720 }
3721 if (rectPts[0] != rectPts[4]) {
3722 return false;
3723 }
3724 // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
3725 // and pts 1 and 2 the opposite vertical or horizontal edge).
3726 bool vec03IsVertical;
3727 if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
3728 rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
3729 // Make sure it has non-zero width and height
3730 if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
3731 return false;
3732 }
3733 vec03IsVertical = true;
3734 } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
3735 rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
3736 // Make sure it has non-zero width and height
3737 if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
3738 return false;
3739 }
3740 vec03IsVertical = false;
3741 } else {
3742 return false;
3743 }
3744 // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
3745 // set if it is on the bottom edge.
3746 unsigned sortFlags =
3747 ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
3748 ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
3749 switch (sortFlags) {
3750 case 0b00:
3751 rect->set(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
3752 *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3753 *start = 0;
3754 break;
3755 case 0b01:
3756 rect->set(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
3757 *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3758 *start = 1;
3759 break;
3760 case 0b10:
3761 rect->set(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
3762 *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3763 *start = 3;
3764 break;
3765 case 0b11:
3766 rect->set(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
3767 *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3768 *start = 2;
3769 break;
3770 }
3771 return true;
3772 }
3773
DrawArcIsConvex(SkScalar sweepAngle,bool useCenter,bool isFillNoPathEffect)3774 bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3775 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3776 // This gets converted to an oval.
3777 return true;
3778 }
3779 if (useCenter) {
3780 // This is a pie wedge. It's convex if the angle is <= 180.
3781 return SkScalarAbs(sweepAngle) <= 180.f;
3782 }
3783 // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
3784 // to a secant, i.e. convex.
3785 return SkScalarAbs(sweepAngle) <= 360.f;
3786 }
3787
CreateDrawArcPath(SkPath * path,const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,bool isFillNoPathEffect)3788 void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
3789 SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3790 SkASSERT(!oval.isEmpty());
3791 SkASSERT(sweepAngle);
3792
3793 path->reset();
3794 path->setIsVolatile(true);
3795 path->setFillType(SkPath::kWinding_FillType);
3796 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3797 path->addOval(oval);
3798 SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
3799 return;
3800 }
3801 if (useCenter) {
3802 path->moveTo(oval.centerX(), oval.centerY());
3803 }
3804 auto firstDir =
3805 sweepAngle > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
3806 bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
3807 // Arc to mods at 360 and drawArc is not supposed to.
3808 bool forceMoveTo = !useCenter;
3809 while (sweepAngle <= -360.f) {
3810 path->arcTo(oval, startAngle, -180.f, forceMoveTo);
3811 startAngle -= 180.f;
3812 path->arcTo(oval, startAngle, -180.f, false);
3813 startAngle -= 180.f;
3814 forceMoveTo = false;
3815 sweepAngle += 360.f;
3816 }
3817 while (sweepAngle >= 360.f) {
3818 path->arcTo(oval, startAngle, 180.f, forceMoveTo);
3819 startAngle += 180.f;
3820 path->arcTo(oval, startAngle, 180.f, false);
3821 startAngle += 180.f;
3822 forceMoveTo = false;
3823 sweepAngle -= 360.f;
3824 }
3825 path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
3826 if (useCenter) {
3827 path->close();
3828 }
3829 path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
3830 path->setFirstDirection(firstDir);
3831 }
3832
3833 ///////////////////////////////////////////////////////////////////////////////////////////////////
3834 #include "SkNx.h"
3835
compute_quad_extremas(const SkPoint src[3],SkPoint extremas[3])3836 static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
3837 SkScalar ts[2];
3838 int n = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
3839 n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
3840 SkASSERT(n >= 0 && n <= 2);
3841 for (int i = 0; i < n; ++i) {
3842 extremas[i] = SkEvalQuadAt(src, ts[i]);
3843 }
3844 extremas[n] = src[2];
3845 return n + 1;
3846 }
3847
compute_conic_extremas(const SkPoint src[3],SkScalar w,SkPoint extremas[3])3848 static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
3849 SkConic conic(src[0], src[1], src[2], w);
3850 SkScalar ts[2];
3851 int n = conic.findXExtrema(ts);
3852 n += conic.findYExtrema(&ts[n]);
3853 SkASSERT(n >= 0 && n <= 2);
3854 for (int i = 0; i < n; ++i) {
3855 extremas[i] = conic.evalAt(ts[i]);
3856 }
3857 extremas[n] = src[2];
3858 return n + 1;
3859 }
3860
compute_cubic_extremas(const SkPoint src[4],SkPoint extremas[5])3861 static int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) {
3862 SkScalar ts[4];
3863 int n = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
3864 n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
3865 SkASSERT(n >= 0 && n <= 4);
3866 for (int i = 0; i < n; ++i) {
3867 SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
3868 }
3869 extremas[n] = src[3];
3870 return n + 1;
3871 }
3872
computeTightBounds() const3873 SkRect SkPath::computeTightBounds() const {
3874 if (0 == this->countVerbs()) {
3875 return SkRect::MakeEmpty();
3876 }
3877
3878 if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
3879 return this->getBounds();
3880 }
3881
3882 SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
3883 SkPoint pts[4];
3884 SkPath::RawIter iter(*this);
3885
3886 // initial with the first MoveTo, so we don't have to check inside the switch
3887 Sk2s min, max;
3888 min = max = from_point(this->getPoint(0));
3889 for (;;) {
3890 int count = 0;
3891 switch (iter.next(pts)) {
3892 case SkPath::kMove_Verb:
3893 extremas[0] = pts[0];
3894 count = 1;
3895 break;
3896 case SkPath::kLine_Verb:
3897 extremas[0] = pts[1];
3898 count = 1;
3899 break;
3900 case SkPath::kQuad_Verb:
3901 count = compute_quad_extremas(pts, extremas);
3902 break;
3903 case SkPath::kConic_Verb:
3904 count = compute_conic_extremas(pts, iter.conicWeight(), extremas);
3905 break;
3906 case SkPath::kCubic_Verb:
3907 count = compute_cubic_extremas(pts, extremas);
3908 break;
3909 case SkPath::kClose_Verb:
3910 break;
3911 case SkPath::kDone_Verb:
3912 goto DONE;
3913 }
3914 for (int i = 0; i < count; ++i) {
3915 Sk2s tmp = from_point(extremas[i]);
3916 min = Sk2s::Min(min, tmp);
3917 max = Sk2s::Max(max, tmp);
3918 }
3919 }
3920 DONE:
3921 SkRect bounds;
3922 min.store((SkPoint*)&bounds.fLeft);
3923 max.store((SkPoint*)&bounds.fRight);
3924 return bounds;
3925 }
3926
IsLineDegenerate(const SkPoint & p1,const SkPoint & p2,bool exact)3927 bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
3928 return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2);
3929 }
3930
IsQuadDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,bool exact)3931 bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
3932 const SkPoint& p3, bool exact) {
3933 return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3934 SkPointPriv::EqualsWithinTolerance(p2, p3);
3935 }
3936
IsCubicDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,const SkPoint & p4,bool exact)3937 bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
3938 const SkPoint& p3, const SkPoint& p4, bool exact) {
3939 return exact ? p1 == p2 && p2 == p3 && p3 == p4 :
3940 SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3941 SkPointPriv::EqualsWithinTolerance(p2, p3) &&
3942 SkPointPriv::EqualsWithinTolerance(p3, p4);
3943 }
3944