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