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