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