• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPath.h"
9 
10 #include "include/core/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(
1141             oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1));
1142     const SkScalar kConicWeight = SK_ScalarRoot2Over2;
1143 
1144     this->moveTo(ovalIter.current());
1145     for (unsigned i = 0; i < 4; ++i) {
1146         this->conicTo(rectIter.next(), ovalIter.next(), kConicWeight);
1147     }
1148     this->close();
1149 
1150     SkASSERT(fPathRef->countVerbs() == initialVerbCount + kVerbs);
1151     SkASSERT(fPathRef->countPoints() == initialPointCount + kPoints);
1152     SkASSERT(fPathRef->countWeights() == initialWeightCount + kWeights);
1153 
1154     if (isOval) {
1155         SkPathRef::Editor ed(&fPathRef);
1156         ed.setIsOval(SkPathDirection::kCCW == dir, startPointIndex % 4, /*isClosed=*/true);
1157     }
1158     return *this;
1159 }
1160 
addOpenOval(const SkRect & oval,SkPathDirection dir,unsigned startPointIndex)1161 SkPath& SkPath::addOpenOval(const SkRect &oval, SkPathDirection dir, unsigned startPointIndex) {
1162     assert_known_direction(dir);
1163 
1164     // Canvas2D semantics are different than our standard addOval. In the case of an empty
1165     // initial path, we inject a moveTo. In any other case, we actually start with a lineTo
1166     // (as if we simply called arcTo). Thus, we only treat the result as an (open) oval for
1167     // future queries if we started out empty.
1168     bool wasEmpty = this->isEmpty();
1169     if (wasEmpty) {
1170         this->setFirstDirection((SkPathFirstDirection)dir);
1171     } else {
1172         this->setFirstDirection(SkPathFirstDirection::kUnknown);
1173     }
1174 
1175     SkAutoDisableDirectionCheck addc(this);
1176     SkAutoPathBoundsUpdate apbu(this, oval);
1177 
1178     SkDEBUGCODE(int initialVerbCount = fPathRef->countVerbs());
1179     SkDEBUGCODE(int initialPointCount = fPathRef->countPoints());
1180     SkDEBUGCODE(int initialWeightCount = fPathRef->countWeights());
1181     // We reserve space for one extra verb. The caller usually adds kClose immediately
1182     const int kVerbs = 6; // moveTo/lineTo + 4x conicTo + (kClose)?
1183     const int kPoints = 9;
1184     const int kWeights = 4;
1185     this->incReserve(kPoints, kVerbs, kWeights);
1186 
1187     SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex);
1188     // The corner iterator pts are tracking "behind" the oval/radii pts.
1189     SkPath_RectPointIterator rectIter(
1190             oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1));
1191     const SkScalar kConicWeight = SK_ScalarRoot2Over2;
1192 
1193     if (wasEmpty) {
1194         this->moveTo(ovalIter.current());
1195     } else {
1196         this->lineTo(ovalIter.current());
1197     }
1198     for (unsigned i = 0; i < 4; ++i) {
1199         this->conicTo(rectIter.next(), ovalIter.next(), kConicWeight);
1200     }
1201 
1202     SkASSERT(fPathRef->countVerbs() == initialVerbCount + kVerbs - 1); // See comment above
1203     SkASSERT(fPathRef->countPoints() == initialPointCount + kPoints);
1204     SkASSERT(fPathRef->countWeights() == initialWeightCount + kWeights);
1205 
1206     if (wasEmpty) {
1207         SkPathRef::Editor ed(&fPathRef);
1208         ed.setIsOval(SkPathDirection::kCCW == dir, startPointIndex % 4, /*isClosed=*/false);
1209     }
1210     return *this;
1211 }
1212 
addCircle(SkScalar x,SkScalar y,SkScalar r,SkPathDirection dir)1213 SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
1214     if (r > 0) {
1215         this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1216     }
1217     return *this;
1218 }
1219 
arcTo(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)1220 SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1221                       bool forceMoveTo) {
1222     if (oval.width() < 0 || oval.height() < 0) {
1223         return *this;
1224     }
1225 
1226     startAngle = SkScalarMod(startAngle, 360.0f);
1227 
1228     if (fPathRef->countVerbs() == 0) {
1229         forceMoveTo = true;
1230     }
1231 
1232     SkPoint lonePt;
1233     if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1234         return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1235     }
1236 
1237     SkVector startV, stopV;
1238     SkRotationDirection dir;
1239     angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1240 
1241     SkPoint singlePt;
1242 
1243     bool isArc = this->hasOnlyMoveTos();
1244 
1245     // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
1246     // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
1247     // arcs from the same oval.
1248     auto addPt = [&forceMoveTo, &isArc, this](const SkPoint& pt) {
1249         SkPoint lastPt;
1250         if (forceMoveTo) {
1251             this->moveTo(pt);
1252         } else if (!this->getLastPt(&lastPt) ||
1253                    !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
1254                    !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
1255             this->lineTo(pt);
1256             isArc = false;
1257         }
1258     };
1259 
1260     // At this point, we know that the arc is not a lone point, but startV == stopV
1261     // indicates that the sweepAngle is too small such that angles_to_unit_vectors
1262     // cannot handle it.
1263     if (startV == stopV) {
1264         SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
1265         SkScalar radiusX = oval.width() / 2;
1266         SkScalar radiusY = oval.height() / 2;
1267         // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
1268         // is very small and radius is huge, the expected behavior here is to draw a line. But
1269         // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
1270         singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
1271                      oval.centerY() + radiusY * SkScalarSin(endAngle));
1272         addPt(singlePt);
1273         return *this;
1274     }
1275 
1276     SkConic conics[SkConic::kMaxConicsForArc];
1277     int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1278     if (count) {
1279         // Conics take two points. Add one to the verb in case there is a moveto.
1280         this->incReserve(count * 2 + 1, count + 1, count);
1281         const SkPoint& pt = conics[0].fPts[0];
1282         addPt(pt);
1283         for (int i = 0; i < count; ++i) {
1284             this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1285         }
1286         if (isArc) {
1287             SkPathRef::Editor ed(&fPathRef);
1288             ed.setIsArc(SkArc::Make(oval, startAngle, sweepAngle, SkArc::Type::kArc));
1289         }
1290     } else {
1291         addPt(singlePt);
1292     }
1293     return *this;
1294 }
1295 
1296 // This converts the SVG arc to conics.
1297 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1298 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1299 // See also SVG implementation notes:
1300 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1301 // 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)1302 SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1303                       SkPathDirection arcSweep, SkScalar x, SkScalar y) {
1304     this->injectMoveToIfNeeded();
1305     SkPoint srcPts[2];
1306     this->getLastPt(&srcPts[0]);
1307     // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1308     // joining the endpoints.
1309     // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1310     if (!rx || !ry) {
1311         return this->lineTo(x, y);
1312     }
1313     // If the current point and target point for the arc are identical, it should be treated as a
1314     // zero length path. This ensures continuity in animations.
1315     srcPts[1].set(x, y);
1316     if (srcPts[0] == srcPts[1]) {
1317         return this->lineTo(x, y);
1318     }
1319     rx = SkScalarAbs(rx);
1320     ry = SkScalarAbs(ry);
1321     SkVector midPointDistance = srcPts[0] - srcPts[1];
1322     midPointDistance *= 0.5f;
1323 
1324     SkMatrix pointTransform;
1325     pointTransform.setRotate(-angle);
1326 
1327     SkPoint transformedMidPoint;
1328     pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1329     SkScalar squareRx = rx * rx;
1330     SkScalar squareRy = ry * ry;
1331     SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1332     SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1333 
1334     // Check if the radii are big enough to draw the arc, scale radii if not.
1335     // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1336     SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1337     if (radiiScale > 1) {
1338         radiiScale = SkScalarSqrt(radiiScale);
1339         rx *= radiiScale;
1340         ry *= radiiScale;
1341     }
1342 
1343     pointTransform.setScale(1 / rx, 1 / ry);
1344     pointTransform.preRotate(-angle);
1345 
1346     SkPoint unitPts[2];
1347     pointTransform.mapPoints(unitPts, srcPts, (int) std::size(unitPts));
1348     SkVector delta = unitPts[1] - unitPts[0];
1349 
1350     SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1351     SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
1352 
1353     SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1354     if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) {  // flipped from the original implementation
1355         scaleFactor = -scaleFactor;
1356     }
1357     delta.scale(scaleFactor);
1358     SkPoint centerPoint = unitPts[0] + unitPts[1];
1359     centerPoint *= 0.5f;
1360     centerPoint.offset(-delta.fY, delta.fX);
1361     unitPts[0] -= centerPoint;
1362     unitPts[1] -= centerPoint;
1363     SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1364     SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1365     SkScalar thetaArc = theta2 - theta1;
1366     if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
1367         thetaArc += SK_ScalarPI * 2;
1368     } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
1369         thetaArc -= SK_ScalarPI * 2;
1370     }
1371 
1372     // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
1373     // so we do a quick check here. The precise tolerance amount is just made up.
1374     // PI/million happens to fix the bug in 9272, but a larger value is probably
1375     // ok too.
1376     if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
1377         return this->lineTo(x, y);
1378     }
1379 
1380     pointTransform.setRotate(angle);
1381     pointTransform.preScale(rx, ry);
1382 
1383     // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
1384     int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
1385     SkScalar thetaWidth = thetaArc / segments;
1386     SkScalar t = SkScalarTan(0.5f * thetaWidth);
1387     if (!SkIsFinite(t)) {
1388         return *this;
1389     }
1390     SkScalar startTheta = theta1;
1391     SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1392     auto scalar_is_integer = [](SkScalar scalar) -> bool {
1393         return scalar == SkScalarFloorToScalar(scalar);
1394     };
1395     bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
1396         scalar_is_integer(rx) && scalar_is_integer(ry) &&
1397         scalar_is_integer(x) && scalar_is_integer(y);
1398 
1399     for (int i = 0; i < segments; ++i) {
1400         SkScalar endTheta    = startTheta + thetaWidth,
1401                  sinEndTheta = SkScalarSinSnapToZero(endTheta),
1402                  cosEndTheta = SkScalarCosSnapToZero(endTheta);
1403 
1404         unitPts[1].set(cosEndTheta, sinEndTheta);
1405         unitPts[1] += centerPoint;
1406         unitPts[0] = unitPts[1];
1407         unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1408         SkPoint mapped[2];
1409         pointTransform.mapPoints(mapped, unitPts, (int) std::size(unitPts));
1410         /*
1411         Computing the arc width introduces rounding errors that cause arcs to start
1412         outside their marks. A round rect may lose convexity as a result. If the input
1413         values are on integers, place the conic on integers as well.
1414          */
1415         if (expectIntegers) {
1416             for (SkPoint& point : mapped) {
1417                 point.fX = SkScalarRoundToScalar(point.fX);
1418                 point.fY = SkScalarRoundToScalar(point.fY);
1419             }
1420         }
1421         this->conicTo(mapped[0], mapped[1], w);
1422         startTheta = endTheta;
1423     }
1424 
1425     // The final point should match the input point (by definition); replace it to
1426     // ensure that rounding errors in the above math don't cause any problems.
1427     this->setLastPt(x, y);
1428     return *this;
1429 }
1430 
rArcTo(SkScalar rx,SkScalar ry,SkScalar xAxisRotate,SkPath::ArcSize largeArc,SkPathDirection sweep,SkScalar dx,SkScalar dy)1431 SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1432                        SkPathDirection sweep, SkScalar dx, SkScalar dy) {
1433     SkPoint currentPoint;
1434     this->getLastPt(&currentPoint);
1435     return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep,
1436                        currentPoint.fX + dx, currentPoint.fY + dy);
1437 }
1438 
addArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)1439 SkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1440     if (oval.isEmpty() || 0 == sweepAngle) {
1441         return *this;
1442     }
1443 
1444     const SkScalar kFullCircleAngle = SkIntToScalar(360);
1445 
1446     if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1447         // We can treat the arc as an oval if it begins at one of our legal starting positions.
1448         // See SkPath::addOval() docs.
1449         SkScalar startOver90 = startAngle / 90.f;
1450         SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
1451         SkScalar error = startOver90 - startOver90I;
1452         if (SkScalarNearlyEqual(error, 0)) {
1453             // Index 1 is at startAngle == 0.
1454             SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
1455             startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
1456             return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
1457                                  (unsigned) startIndex);
1458         }
1459     }
1460     return this->arcTo(oval, startAngle, sweepAngle, true);
1461 }
1462 
1463 /*
1464     Need to handle the case when the angle is sharp, and our computed end-points
1465     for the arc go behind pt1 and/or p2...
1466 */
arcTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)1467 SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1468     this->injectMoveToIfNeeded();
1469 
1470     if (radius == 0) {
1471         return this->lineTo(x1, y1);
1472     }
1473 
1474     // need to know our prev pt so we can construct tangent vectors
1475     SkPoint start;
1476     this->getLastPt(&start);
1477 
1478     // need double precision for these calcs.
1479     skvx::double2 befored = normalize(skvx::double2{x1 - start.fX, y1 - start.fY});
1480     skvx::double2 afterd = normalize(skvx::double2{x2 - x1, y2 - y1});
1481     double cosh = dot(befored, afterd);
1482     double sinh = cross(befored, afterd);
1483 
1484     // If the previous point equals the first point, befored will be denormalized.
1485     // If the two points equal, afterd will be denormalized.
1486     // If the second point equals the first point, sinh will be zero.
1487     // In all these cases, we cannot construct an arc, so we construct a line to the first point.
1488     if (!isfinite(befored) || !isfinite(afterd) || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
1489         return this->lineTo(x1, y1);
1490     }
1491 
1492     // safe to convert back to floats now
1493     SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
1494     SkScalar xx = x1 - dist * befored[0];
1495     SkScalar yy = y1 - dist * befored[1];
1496 
1497     SkVector after = SkVector::Make(afterd[0], afterd[1]);
1498     after.setLength(dist);
1499     this->lineTo(xx, yy);
1500     SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
1501     return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1502 }
1503 
1504 ///////////////////////////////////////////////////////////////////////////////
1505 
addPath(const SkPath & path,SkScalar dx,SkScalar dy,AddPathMode mode)1506 SkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1507     SkMatrix matrix;
1508 
1509     matrix.setTranslate(dx, dy);
1510     return this->addPath(path, matrix, mode);
1511 }
1512 
addPath(const SkPath & srcPath,const SkMatrix & matrix,AddPathMode mode)1513 SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) {
1514     if (srcPath.isEmpty()) {
1515         return *this;
1516     }
1517 
1518     if (this->isEmpty() && matrix.isIdentity()) {
1519         const uint8_t fillType = fFillType;
1520         *this = srcPath;
1521         fFillType = fillType;
1522         return *this;
1523     }
1524 
1525     // Detect if we're trying to add ourself
1526     const SkPath* src = &srcPath;
1527     SkTLazy<SkPath> tmp;
1528     if (this == src) {
1529         src = tmp.set(srcPath);
1530     }
1531 
1532     if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) {
1533         if (src->fLastMoveToIndex >= 0) {
1534             fLastMoveToIndex = src->fLastMoveToIndex + this->countPoints();
1535         } else {
1536             fLastMoveToIndex = src->fLastMoveToIndex - this->countPoints();
1537         }
1538         SkPathRef::Editor ed(&fPathRef);
1539         auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef);
1540         matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints());
1541         if (int numWeights = src->fPathRef->countWeights()) {
1542             memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0]));
1543         }
1544         return this->dirtyAfterEdit();
1545     }
1546 
1547     SkMatrixPriv::MapPtsProc mapPtsProc = SkMatrixPriv::GetMapPtsProc(matrix);
1548     bool firstVerb = true;
1549     for (auto [verb, pts, w] : SkPathPriv::Iterate(*src)) {
1550         SkPoint mappedPts[3];
1551         switch (verb) {
1552             case SkPathVerb::kMove:
1553                 mapPtsProc(matrix, mappedPts, &pts[0], 1);
1554                 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1555                     injectMoveToIfNeeded(); // In case last contour is closed
1556                     SkPoint lastPt;
1557                     // don't add lineTo if it is degenerate
1558                     if (!this->getLastPt(&lastPt) || lastPt != mappedPts[0]) {
1559                         this->lineTo(mappedPts[0]);
1560                     }
1561                 } else {
1562                     this->moveTo(mappedPts[0]);
1563                 }
1564                 break;
1565             case SkPathVerb::kLine:
1566                 mapPtsProc(matrix, mappedPts, &pts[1], 1);
1567                 this->lineTo(mappedPts[0]);
1568                 break;
1569             case SkPathVerb::kQuad:
1570                 mapPtsProc(matrix, mappedPts, &pts[1], 2);
1571                 this->quadTo(mappedPts[0], mappedPts[1]);
1572                 break;
1573             case SkPathVerb::kConic:
1574                 mapPtsProc(matrix, mappedPts, &pts[1], 2);
1575                 this->conicTo(mappedPts[0], mappedPts[1], *w);
1576                 break;
1577             case SkPathVerb::kCubic:
1578                 mapPtsProc(matrix, mappedPts, &pts[1], 3);
1579                 this->cubicTo(mappedPts[0], mappedPts[1], mappedPts[2]);
1580                 break;
1581             case SkPathVerb::kClose:
1582                 this->close();
1583                 break;
1584         }
1585         firstVerb = false;
1586     }
1587     return *this;
1588 }
1589 
1590 ///////////////////////////////////////////////////////////////////////////////
1591 
1592 // ignore the last point of the 1st contour
reversePathTo(const SkPath & path)1593 SkPath& SkPath::reversePathTo(const SkPath& path) {
1594     if (path.fPathRef->fVerbs.empty()) {
1595         return *this;
1596     }
1597 
1598     const uint8_t* verbs = path.fPathRef->verbsEnd();
1599     const uint8_t* verbsBegin = path.fPathRef->verbsBegin();
1600     SkASSERT(verbsBegin[0] == kMove_Verb);
1601     const SkPoint*  pts = path.fPathRef->pointsEnd() - 1;
1602     const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
1603 
1604     while (verbs > verbsBegin) {
1605         uint8_t v = *--verbs;
1606         pts -= SkPathPriv::PtsInVerb(v);
1607         switch (v) {
1608             case kMove_Verb:
1609                 // if the path has multiple contours, stop after reversing the last
1610                 return *this;
1611             case kLine_Verb:
1612                 this->lineTo(pts[0]);
1613                 break;
1614             case kQuad_Verb:
1615                 this->quadTo(pts[1], pts[0]);
1616                 break;
1617             case kConic_Verb:
1618                 this->conicTo(pts[1], pts[0], *--conicWeights);
1619                 break;
1620             case kCubic_Verb:
1621                 this->cubicTo(pts[2], pts[1], pts[0]);
1622                 break;
1623             case kClose_Verb:
1624                 break;
1625             default:
1626                 SkDEBUGFAIL("bad verb");
1627                 break;
1628         }
1629     }
1630     return *this;
1631 }
1632 
reverseAddPath(const SkPath & srcPath)1633 SkPath& SkPath::reverseAddPath(const SkPath& srcPath) {
1634     // Detect if we're trying to add ourself
1635     const SkPath* src = &srcPath;
1636     SkTLazy<SkPath> tmp;
1637     if (this == src) {
1638         src = tmp.set(srcPath);
1639     }
1640 
1641     const uint8_t* verbsBegin = src->fPathRef->verbsBegin();
1642     const uint8_t* verbs = src->fPathRef->verbsEnd();
1643     const SkPoint* pts = src->fPathRef->pointsEnd();
1644     const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd();
1645 
1646     bool needMove = true;
1647     bool needClose = false;
1648     while (verbs > verbsBegin) {
1649         uint8_t v = *--verbs;
1650         int n = SkPathPriv::PtsInVerb(v);
1651 
1652         if (needMove) {
1653             --pts;
1654             this->moveTo(pts->fX, pts->fY);
1655             needMove = false;
1656         }
1657         pts -= n;
1658         switch (v) {
1659             case kMove_Verb:
1660                 if (needClose) {
1661                     this->close();
1662                     needClose = false;
1663                 }
1664                 needMove = true;
1665                 pts += 1;   // so we see the point in "if (needMove)" above
1666                 break;
1667             case kLine_Verb:
1668                 this->lineTo(pts[0]);
1669                 break;
1670             case kQuad_Verb:
1671                 this->quadTo(pts[1], pts[0]);
1672                 break;
1673             case kConic_Verb:
1674                 this->conicTo(pts[1], pts[0], *--conicWeights);
1675                 break;
1676             case kCubic_Verb:
1677                 this->cubicTo(pts[2], pts[1], pts[0]);
1678                 break;
1679             case kClose_Verb:
1680                 needClose = true;
1681                 break;
1682             default:
1683                 SkDEBUGFAIL("unexpected verb");
1684         }
1685     }
1686     return *this;
1687 }
1688 
1689 ///////////////////////////////////////////////////////////////////////////////
1690 
offset(SkScalar dx,SkScalar dy,SkPath * dst) const1691 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1692     SkMatrix    matrix;
1693 
1694     matrix.setTranslate(dx, dy);
1695     this->transform(matrix, dst);
1696 }
1697 
subdivide_cubic_to(SkPath * path,const SkPoint pts[4],int level=2)1698 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1699                                int level = 2) {
1700     if (--level >= 0) {
1701         SkPoint tmp[7];
1702 
1703         SkChopCubicAtHalf(pts, tmp);
1704         subdivide_cubic_to(path, &tmp[0], level);
1705         subdivide_cubic_to(path, &tmp[3], level);
1706     } else {
1707         path->cubicTo(pts[1], pts[2], pts[3]);
1708     }
1709 }
1710 
transform(const SkMatrix & matrix,SkPath * dst,SkApplyPerspectiveClip pc) const1711 void SkPath::transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip pc) const {
1712     if (matrix.isIdentity()) {
1713         if (dst != nullptr && dst != this) {
1714             *dst = *this;
1715         }
1716         return;
1717     }
1718 
1719     SkDEBUGCODE(this->validate();)
1720     if (dst == nullptr) {
1721         dst = const_cast<SkPath*>(this);
1722     }
1723 
1724     if (matrix.hasPerspective()) {
1725         SkPath  tmp;
1726         tmp.fFillType = fFillType;
1727 
1728         SkPath clipped;
1729         const SkPath* src = this;
1730         if (pc == SkApplyPerspectiveClip::kYes &&
1731             SkPathPriv::PerspectiveClip(*this, matrix, &clipped))
1732         {
1733             src = &clipped;
1734         }
1735 
1736         SkPath::Iter    iter(*src, false);
1737         SkPoint         pts[4];
1738         SkPath::Verb    verb;
1739 
1740         while ((verb = iter.next(pts)) != kDone_Verb) {
1741             switch (verb) {
1742                 case kMove_Verb:
1743                     tmp.moveTo(pts[0]);
1744                     break;
1745                 case kLine_Verb:
1746                     tmp.lineTo(pts[1]);
1747                     break;
1748                 case kQuad_Verb:
1749                     // promote the quad to a conic
1750                     tmp.conicTo(pts[1], pts[2],
1751                                 SkConic::TransformW(pts, SK_Scalar1, matrix));
1752                     break;
1753                 case kConic_Verb:
1754                     tmp.conicTo(pts[1], pts[2],
1755                                 SkConic::TransformW(pts, iter.conicWeight(), matrix));
1756                     break;
1757                 case kCubic_Verb:
1758                     subdivide_cubic_to(&tmp, pts);
1759                     break;
1760                 case kClose_Verb:
1761                     tmp.close();
1762                     break;
1763                 default:
1764                     SkDEBUGFAIL("unknown verb");
1765                     break;
1766             }
1767         }
1768 
1769         dst->swap(tmp);
1770         SkPathRef::Editor ed(&dst->fPathRef);
1771         matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints());
1772         dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1773     } else {
1774         SkPathConvexity convexity = this->getConvexityOrUnknown();
1775 
1776         SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef, matrix);
1777 
1778         if (this != dst) {
1779             dst->fLastMoveToIndex = fLastMoveToIndex;
1780             dst->fFillType = fFillType;
1781             dst->fIsVolatile = fIsVolatile;
1782         }
1783 
1784         // Due to finite/fragile float numerics, we can't assume that a convex path remains
1785         // convex after a transformation, so mark it as unknown here.
1786         // However, some transformations are thought to be safe:
1787         //    axis-aligned values under scale/translate.
1788         //
1789         if (convexity == SkPathConvexity::kConvex &&
1790             (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this))) {
1791             // Not safe to still assume we're convex...
1792             convexity = SkPathConvexity::kUnknown;
1793         }
1794         dst->setConvexity(convexity);
1795 
1796         if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) {
1797             dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1798         } else {
1799             SkScalar det2x2 =
1800                 matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
1801                 matrix.get(SkMatrix::kMSkewX)  * matrix.get(SkMatrix::kMSkewY);
1802             if (det2x2 < 0) {
1803                 dst->setFirstDirection(
1804                         SkPathPriv::OppositeFirstDirection(
1805                             (SkPathFirstDirection)this->getFirstDirection()));
1806             } else if (det2x2 > 0) {
1807                 dst->setFirstDirection(this->getFirstDirection());
1808             } else {
1809                 dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1810             }
1811         }
1812 
1813         SkDEBUGCODE(dst->validate();)
1814     }
1815 }
1816 
1817 ///////////////////////////////////////////////////////////////////////////////
1818 ///////////////////////////////////////////////////////////////////////////////
1819 
Iter()1820 SkPath::Iter::Iter() {
1821 #ifdef SK_DEBUG
1822     fPts = nullptr;
1823     fConicWeights = nullptr;
1824     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1825     fForceClose = fCloseLine = false;
1826 #endif
1827     // need to init enough to make next() harmlessly return kDone_Verb
1828     fVerbs = nullptr;
1829     fVerbStop = nullptr;
1830     fNeedClose = false;
1831 }
1832 
Iter(const SkPath & path,bool forceClose)1833 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1834     this->setPath(path, forceClose);
1835 }
1836 
setPath(const SkPath & path,bool forceClose)1837 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1838     fPts = path.fPathRef->points();
1839     fVerbs = path.fPathRef->verbsBegin();
1840     fVerbStop = path.fPathRef->verbsEnd();
1841     fConicWeights = path.fPathRef->conicWeights();
1842     if (fConicWeights) {
1843       fConicWeights -= 1;  // begin one behind
1844     }
1845     fLastPt.fX = fLastPt.fY = 0;
1846     fMoveTo.fX = fMoveTo.fY = 0;
1847     fForceClose = SkToU8(forceClose);
1848     fNeedClose = false;
1849 }
1850 
isClosedContour() const1851 bool SkPath::Iter::isClosedContour() const {
1852     if (fVerbs == nullptr || fVerbs == fVerbStop) {
1853         return false;
1854     }
1855     if (fForceClose) {
1856         return true;
1857     }
1858 
1859     const uint8_t* verbs = fVerbs;
1860     const uint8_t* stop = fVerbStop;
1861 
1862     if (kMove_Verb == *verbs) {
1863         verbs += 1; // skip the initial moveto
1864     }
1865 
1866     while (verbs < stop) {
1867         // verbs points one beyond the current verb, decrement first.
1868         unsigned v = *verbs++;
1869         if (kMove_Verb == v) {
1870             break;
1871         }
1872         if (kClose_Verb == v) {
1873             return true;
1874         }
1875     }
1876     return false;
1877 }
1878 
autoClose(SkPoint pts[2])1879 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1880     SkASSERT(pts);
1881     if (fLastPt != fMoveTo) {
1882         // A special case: if both points are NaN, SkPoint::operation== returns
1883         // false, but the iterator expects that they are treated as the same.
1884         // (consider SkPoint is a 2-dimension float point).
1885         if (SkIsNaN(fLastPt.fX) || SkIsNaN(fLastPt.fY) ||
1886             SkIsNaN(fMoveTo.fX) || SkIsNaN(fMoveTo.fY)) {
1887             return kClose_Verb;
1888         }
1889 
1890         pts[0] = fLastPt;
1891         pts[1] = fMoveTo;
1892         fLastPt = fMoveTo;
1893         fCloseLine = true;
1894         return kLine_Verb;
1895     } else {
1896         pts[0] = fMoveTo;
1897         return kClose_Verb;
1898     }
1899 }
1900 
next(SkPoint ptsParam[4])1901 SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
1902     SkASSERT(ptsParam);
1903 
1904     if (fVerbs == fVerbStop) {
1905         // Close the curve if requested and if there is some curve to close
1906         if (fNeedClose) {
1907             if (kLine_Verb == this->autoClose(ptsParam)) {
1908                 return kLine_Verb;
1909             }
1910             fNeedClose = false;
1911             return kClose_Verb;
1912         }
1913         return kDone_Verb;
1914     }
1915 
1916     unsigned verb = *fVerbs++;
1917     const SkPoint* SK_RESTRICT srcPts = fPts;
1918     SkPoint* SK_RESTRICT       pts = ptsParam;
1919 
1920     switch (verb) {
1921         case kMove_Verb:
1922             if (fNeedClose) {
1923                 fVerbs--; // move back one verb
1924                 verb = this->autoClose(pts);
1925                 if (verb == kClose_Verb) {
1926                     fNeedClose = false;
1927                 }
1928                 return (Verb)verb;
1929             }
1930             if (fVerbs == fVerbStop) {    // might be a trailing moveto
1931                 return kDone_Verb;
1932             }
1933             fMoveTo = *srcPts;
1934             pts[0] = *srcPts;
1935             srcPts += 1;
1936             fLastPt = fMoveTo;
1937             fNeedClose = fForceClose;
1938             break;
1939         case kLine_Verb:
1940             pts[0] = fLastPt;
1941             pts[1] = srcPts[0];
1942             fLastPt = srcPts[0];
1943             fCloseLine = false;
1944             srcPts += 1;
1945             break;
1946         case kConic_Verb:
1947             fConicWeights += 1;
1948             [[fallthrough]];
1949         case kQuad_Verb:
1950             pts[0] = fLastPt;
1951             memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1952             fLastPt = srcPts[1];
1953             srcPts += 2;
1954             break;
1955         case kCubic_Verb:
1956             pts[0] = fLastPt;
1957             memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1958             fLastPt = srcPts[2];
1959             srcPts += 3;
1960             break;
1961         case kClose_Verb:
1962             verb = this->autoClose(pts);
1963             if (verb == kLine_Verb) {
1964                 fVerbs--; // move back one verb
1965             } else {
1966                 fNeedClose = false;
1967             }
1968             fLastPt = fMoveTo;
1969             break;
1970     }
1971     fPts = srcPts;
1972     return (Verb)verb;
1973 }
1974 
setPath(const SkPath & path)1975 void SkPath::RawIter::setPath(const SkPath& path) {
1976     SkPathPriv::Iterate iterate(path);
1977     fIter = iterate.begin();
1978     fEnd = iterate.end();
1979 }
1980 
next(SkPoint pts[4])1981 SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
1982     if (!(fIter != fEnd)) {
1983         return kDone_Verb;
1984     }
1985     auto [verb, iterPts, weights] = *fIter;
1986     int numPts;
1987     switch (verb) {
1988         case SkPathVerb::kMove: numPts = 1; break;
1989         case SkPathVerb::kLine: numPts = 2; break;
1990         case SkPathVerb::kQuad: numPts = 3; break;
1991         case SkPathVerb::kConic:
1992             numPts = 3;
1993             fConicWeight = *weights;
1994             break;
1995         case SkPathVerb::kCubic: numPts = 4; break;
1996         case SkPathVerb::kClose: numPts = 0; break;
1997     }
1998     memcpy(pts, iterPts, sizeof(SkPoint) * numPts);
1999     ++fIter;
2000     return (Verb) verb;
2001 }
2002 
2003 ///////////////////////////////////////////////////////////////////////////////
2004 
append_params(SkString * str,const char label[],const SkPoint pts[],int count,SkScalarAsStringType strType,SkScalar conicWeight=-12345)2005 static void append_params(SkString* str, const char label[], const SkPoint pts[],
2006                           int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) {
2007     str->append(label);
2008     str->append("(");
2009 
2010     const SkScalar* values = &pts[0].fX;
2011     count *= 2;
2012 
2013     for (int i = 0; i < count; ++i) {
2014         SkAppendScalar(str, values[i], strType);
2015         if (i < count - 1) {
2016             str->append(", ");
2017         }
2018     }
2019     if (conicWeight != -12345) {
2020         str->append(", ");
2021         SkAppendScalar(str, conicWeight, strType);
2022     }
2023     str->append(");");
2024     if (kHex_SkScalarAsStringType == strType) {
2025         str->append("  // ");
2026         for (int i = 0; i < count; ++i) {
2027             SkAppendScalarDec(str, values[i]);
2028             if (i < count - 1) {
2029                 str->append(", ");
2030             }
2031         }
2032         if (conicWeight >= 0) {
2033             str->append(", ");
2034             SkAppendScalarDec(str, conicWeight);
2035         }
2036     }
2037     str->append("\n");
2038 }
2039 
dump(SkWStream * wStream,bool dumpAsHex) const2040 void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const {
2041     SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
2042     Iter    iter(*this, false);
2043     SkPoint pts[4];
2044     Verb    verb;
2045 
2046     SkString builder;
2047     char const * const gFillTypeStrs[] = {
2048         "Winding",
2049         "EvenOdd",
2050         "InverseWinding",
2051         "InverseEvenOdd",
2052     };
2053     builder.printf("path.setFillType(SkPathFillType::k%s);\n",
2054             gFillTypeStrs[(int) this->getFillType()]);
2055     while ((verb = iter.next(pts)) != kDone_Verb) {
2056         switch (verb) {
2057             case kMove_Verb:
2058                 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
2059                 break;
2060             case kLine_Verb:
2061                 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
2062                 break;
2063             case kQuad_Verb:
2064                 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
2065                 break;
2066             case kConic_Verb:
2067                 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2068                 break;
2069             case kCubic_Verb:
2070                 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
2071                 break;
2072             case kClose_Verb:
2073                 builder.append("path.close();\n");
2074                 break;
2075             default:
2076                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
2077                 verb = kDone_Verb;  // stop the loop
2078                 break;
2079         }
2080         if (!wStream && builder.size()) {
2081             SkDebugf("%s", builder.c_str());
2082             builder.reset();
2083         }
2084     }
2085     if (wStream) {
2086         wStream->writeText(builder.c_str());
2087     }
2088 }
2089 
dumpArrays(SkWStream * wStream,bool dumpAsHex) const2090 void SkPath::dumpArrays(SkWStream* wStream, bool dumpAsHex) const {
2091     SkString builder;
2092 
2093     auto bool_str = [](bool v) { return v ? "true" : "false"; };
2094 
2095     builder.appendf("// fBoundsIsDirty = %s\n", bool_str(fPathRef->fBoundsIsDirty));
2096     builder.appendf("// fGenerationID = %u\n", fPathRef->fGenerationID);
2097     builder.appendf("// fSegmentMask = %d\n", fPathRef->fSegmentMask);
2098 
2099     const char* gTypeStrs[] = {
2100         "General", "Oval", "RRect",
2101     };
2102     builder.appendf("// fType = %s\n", gTypeStrs[static_cast<int>(fPathRef->fType)]);
2103 
2104     auto append_scalar = [&](SkScalar v) {
2105         if (dumpAsHex) {
2106             builder.appendf("SkBits2Float(0x%08X) /* %g */", SkFloat2Bits(v), v);
2107         } else {
2108             builder.appendf("%g", v);
2109         }
2110     };
2111 
2112     builder.append("const SkPoint path_points[] = {\n");
2113     for (int i = 0; i < this->countPoints(); ++i) {
2114         SkPoint p = this->getPoint(i);
2115         builder.append("    { ");
2116         append_scalar(p.fX);
2117         builder.append(", ");
2118         append_scalar(p.fY);
2119         builder.append(" },\n");
2120     }
2121     builder.append("};\n");
2122 
2123     const char* gVerbStrs[] = {
2124         "Move", "Line", "Quad", "Conic", "Cubic", "Close"
2125     };
2126     builder.append("const uint8_t path_verbs[] = {\n    ");
2127     for (auto v = fPathRef->verbsBegin(); v != fPathRef->verbsEnd(); ++v) {
2128         builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[*v]);
2129     }
2130     builder.append("\n};\n");
2131 
2132     const int nConics = fPathRef->conicWeightsEnd() - fPathRef->conicWeights();
2133     if (nConics) {
2134         builder.append("const SkScalar path_conics[] = {\n    ");
2135         for (auto c = fPathRef->conicWeights(); c != fPathRef->conicWeightsEnd(); ++c) {
2136             append_scalar(*c);
2137             builder.append(", ");
2138         }
2139         builder.append("\n};\n");
2140     }
2141 
2142     char const * const gFillTypeStrs[] = {
2143         "Winding",
2144         "EvenOdd",
2145         "InverseWinding",
2146         "InverseEvenOdd",
2147     };
2148 
2149     builder.appendf("SkPath path = SkPath::Make(path_points, %d, path_verbs, %d, %s, %d,\n",
2150                     this->countPoints(), this->countVerbs(),
2151                     nConics ? "path_conics" : "nullptr", nConics);
2152     builder.appendf("                           SkPathFillType::k%s, %s);\n",
2153                     gFillTypeStrs[(int)this->getFillType()],
2154                     bool_str(fIsVolatile));
2155 
2156     if (wStream) {
2157         wStream->writeText(builder.c_str());
2158     } else {
2159         SkDebugf("%s\n", builder.c_str());
2160     }
2161 }
2162 
isValidImpl() const2163 bool SkPath::isValidImpl() const {
2164     if ((fFillType & ~3) != 0) {
2165         return false;
2166     }
2167 
2168 #ifdef SK_DEBUG_PATH
2169     if (!fBoundsIsDirty) {
2170         SkRect bounds;
2171 
2172         bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2173         if (SkToBool(fIsFinite) != isFinite) {
2174             return false;
2175         }
2176 
2177         if (fPathRef->countPoints() <= 1) {
2178             // if we're empty, fBounds may be empty but translated, so we can't
2179             // necessarily compare to bounds directly
2180             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2181             // be [2, 2, 2, 2]
2182             if (!bounds.isEmpty() || !fBounds.isEmpty()) {
2183                 return false;
2184             }
2185         } else {
2186             if (bounds.isEmpty()) {
2187                 if (!fBounds.isEmpty()) {
2188                     return false;
2189                 }
2190             } else {
2191                 if (!fBounds.isEmpty()) {
2192                     if (!fBounds.contains(bounds)) {
2193                         return false;
2194                     }
2195                 }
2196             }
2197         }
2198     }
2199 #endif // SK_DEBUG_PATH
2200     return true;
2201 }
2202 
2203 ///////////////////////////////////////////////////////////////////////////////
2204 
sign(SkScalar x)2205 static int sign(SkScalar x) { return x < 0; }
2206 #define kValueNeverReturnedBySign   2
2207 
2208 enum DirChange {
2209     kUnknown_DirChange,
2210     kLeft_DirChange,
2211     kRight_DirChange,
2212     kStraight_DirChange,
2213     kBackwards_DirChange, // if double back, allow simple lines to be convex
2214     kInvalid_DirChange
2215 };
2216 
2217 // only valid for a single contour
2218 struct Convexicator {
2219 
2220     /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2221     SkPathFirstDirection getFirstDirection() const { return fFirstDirection; }
2222 
setMovePtConvexicator2223     void setMovePt(const SkPoint& pt) {
2224         fFirstPt = fLastPt = pt;
2225         fExpectedDir = kInvalid_DirChange;
2226     }
2227 
addPtConvexicator2228     bool addPt(const SkPoint& pt) {
2229         if (fLastPt == pt) {
2230             return true;
2231         }
2232         // should only be true for first non-zero vector after setMovePt was called. It is possible
2233         // we doubled backed at the start so need to check if fLastVec is zero or not.
2234         if (fFirstPt == fLastPt && fExpectedDir == kInvalid_DirChange && fLastVec.equals(0,0)) {
2235             fLastVec = pt - fLastPt;
2236             fFirstVec = fLastVec;
2237         } else if (!this->addVec(pt - fLastPt)) {
2238             return false;
2239         }
2240         fLastPt = pt;
2241         return true;
2242     }
2243 
BySignConvexicator2244     static SkPathConvexity BySign(const SkPoint points[], int count) {
2245         if (count <= 3) {
2246             // point, line, or triangle are always convex
2247             return SkPathConvexity::kConvex;
2248         }
2249 
2250         const SkPoint* last = points + count;
2251         SkPoint currPt = *points++;
2252         SkPoint firstPt = currPt;
2253         int dxes = 0;
2254         int dyes = 0;
2255         int lastSx = kValueNeverReturnedBySign;
2256         int lastSy = kValueNeverReturnedBySign;
2257         for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) {
2258             while (points != last) {
2259                 SkVector vec = *points - currPt;
2260                 if (!vec.isZero()) {
2261                     // give up if vector construction failed
2262                     if (!vec.isFinite()) {
2263                         return SkPathConvexity::kUnknown;
2264                     }
2265                     int sx = sign(vec.fX);
2266                     int sy = sign(vec.fY);
2267                     dxes += (sx != lastSx);
2268                     dyes += (sy != lastSy);
2269                     if (dxes > 3 || dyes > 3) {
2270                         return SkPathConvexity::kConcave;
2271                     }
2272                     lastSx = sx;
2273                     lastSy = sy;
2274                 }
2275                 currPt = *points++;
2276                 if (outerLoop) {
2277                     break;
2278                 }
2279             }
2280             points = &firstPt;
2281         }
2282         return SkPathConvexity::kConvex;  // that is, it may be convex, don't know yet
2283     }
2284 
closeConvexicator2285     bool close() {
2286         // If this was an explicit close, there was already a lineTo to fFirstPoint, so this
2287         // addPt() is a no-op. Otherwise, the addPt implicitly closes the contour. In either case,
2288         // we have to check the direction change along the first vector in case it is concave.
2289         return this->addPt(fFirstPt) && this->addVec(fFirstVec);
2290     }
2291 
isFiniteConvexicator2292     bool isFinite() const {
2293         return fIsFinite;
2294     }
2295 
reversalsConvexicator2296     int reversals() const {
2297         return fReversals;
2298     }
2299 
2300 private:
directionChangeConvexicator2301     DirChange directionChange(const SkVector& curVec) {
2302         SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2303         if (!SkIsFinite(cross)) {
2304             return kUnknown_DirChange;
2305         }
2306         if (cross == 0) {
2307             return fLastVec.dot(curVec) < 0 ? kBackwards_DirChange : kStraight_DirChange;
2308         }
2309         return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange;
2310     }
2311 
addVecConvexicator2312     bool addVec(const SkVector& curVec) {
2313         DirChange dir = this->directionChange(curVec);
2314         switch (dir) {
2315             case kLeft_DirChange:       // fall through
2316             case kRight_DirChange:
2317                 if (kInvalid_DirChange == fExpectedDir) {
2318                     fExpectedDir = dir;
2319                     fFirstDirection = (kRight_DirChange == dir) ? SkPathFirstDirection::kCW
2320                                                                 : SkPathFirstDirection::kCCW;
2321                 } else if (dir != fExpectedDir) {
2322                     fFirstDirection = SkPathFirstDirection::kUnknown;
2323                     return false;
2324                 }
2325                 fLastVec = curVec;
2326                 break;
2327             case kStraight_DirChange:
2328                 break;
2329             case kBackwards_DirChange:
2330                 //  allow path to reverse direction twice
2331                 //    Given path.moveTo(0, 0); path.lineTo(1, 1);
2332                 //    - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0)
2333                 //    - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1)
2334                 fLastVec = curVec;
2335                 return ++fReversals < 3;
2336             case kUnknown_DirChange:
2337                 return (fIsFinite = false);
2338             case kInvalid_DirChange:
2339                 SK_ABORT("Use of invalid direction change flag");
2340                 break;
2341         }
2342         return true;
2343     }
2344 
2345     SkPoint              fFirstPt {0, 0};  // The first point of the contour, e.g. moveTo(x,y)
2346     SkVector             fFirstVec {0, 0}; // The direction leaving fFirstPt to the next vertex
2347 
2348     SkPoint              fLastPt {0, 0};   // The last point passed to addPt()
2349     SkVector             fLastVec {0, 0};  // The direction that brought the path to fLastPt
2350 
2351     DirChange            fExpectedDir { kInvalid_DirChange };
2352     SkPathFirstDirection fFirstDirection { SkPathFirstDirection::kUnknown };
2353     int                  fReversals { 0 };
2354     bool                 fIsFinite { true };
2355 };
2356 
computeConvexity() const2357 SkPathConvexity SkPath::computeConvexity() const {
2358     auto setComputedConvexity = [&](SkPathConvexity convexity) {
2359         SkASSERT(SkPathConvexity::kUnknown != convexity);
2360         this->setConvexity(convexity);
2361         return convexity;
2362     };
2363 
2364     auto setFail = [&]() { return setComputedConvexity(SkPathConvexity::kConcave); };
2365 
2366     if (!this->isFinite()) {
2367         return setFail();
2368     }
2369 
2370     // pointCount potentially includes a block of leading moveTos and trailing moveTos. Convexity
2371     // only cares about the last of the initial moveTos and the verbs before the final moveTos.
2372     int pointCount = this->countPoints();
2373     int skipCount = SkPathPriv::LeadingMoveToCount(*this) - 1;
2374 
2375     if (fLastMoveToIndex >= 0) {
2376         if (fLastMoveToIndex == pointCount - 1) {
2377             // Find the last real verb that affects convexity
2378             auto verbs = fPathRef->verbsEnd() - 1;
2379             while(verbs > fPathRef->verbsBegin() && *verbs == Verb::kMove_Verb) {
2380                 verbs--;
2381                 pointCount--;
2382             }
2383         } else if (fLastMoveToIndex != skipCount) {
2384             // There's an additional moveTo between two blocks of other verbs, so the path must have
2385             // more than one contour and cannot be convex.
2386             return setComputedConvexity(SkPathConvexity::kConcave);
2387         } // else no trailing or intermediate moveTos to worry about
2388     }
2389     const SkPoint* points = fPathRef->points();
2390     if (skipCount > 0) {
2391         points += skipCount;
2392         pointCount -= skipCount;
2393     }
2394 
2395     // Check to see if path changes direction more than three times as quick concave test
2396     SkPathConvexity convexity = Convexicator::BySign(points, pointCount);
2397     if (SkPathConvexity::kConvex != convexity) {
2398         return setComputedConvexity(SkPathConvexity::kConcave);
2399     }
2400 
2401     int contourCount = 0;
2402     bool needsClose = false;
2403     Convexicator state;
2404 
2405     for (auto [verb, pts, wt] : SkPathPriv::Iterate(*this)) {
2406         // Looking for the last moveTo before non-move verbs start
2407         if (contourCount == 0) {
2408             if (verb == SkPathVerb::kMove) {
2409                 state.setMovePt(pts[0]);
2410             } else {
2411                 // Starting the actual contour, fall through to c=1 to add the points
2412                 contourCount++;
2413                 needsClose = true;
2414             }
2415         }
2416         // Accumulating points into the Convexicator until we hit a close or another move
2417         if (contourCount == 1) {
2418             if (verb == SkPathVerb::kClose || verb == SkPathVerb::kMove) {
2419                 if (!state.close()) {
2420                     return setFail();
2421                 }
2422                 needsClose = false;
2423                 contourCount++;
2424             } else {
2425                 // lines add 1 point, cubics add 3, conics and quads add 2
2426                 int count = SkPathPriv::PtsInVerb((unsigned) verb);
2427                 SkASSERT(count > 0);
2428                 for (int i = 1; i <= count; ++i) {
2429                     if (!state.addPt(pts[i])) {
2430                         return setFail();
2431                     }
2432                 }
2433             }
2434         } else {
2435             // The first contour has closed and anything other than spurious trailing moves means
2436             // there's multiple contours and the path can't be convex
2437             if (verb != SkPathVerb::kMove) {
2438                 return setFail();
2439             }
2440         }
2441     }
2442 
2443     // If the path isn't explicitly closed do so implicitly
2444     if (needsClose && !state.close()) {
2445         return setFail();
2446     }
2447 
2448     if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) {
2449         if (state.getFirstDirection() == SkPathFirstDirection::kUnknown
2450                 && !this->getBounds().isEmpty()) {
2451             return setComputedConvexity(state.reversals() < 3 ?
2452                     SkPathConvexity::kConvex : SkPathConvexity::kConcave);
2453         }
2454         this->setFirstDirection(state.getFirstDirection());
2455     }
2456     return setComputedConvexity(SkPathConvexity::kConvex);
2457 }
2458 
2459 ///////////////////////////////////////////////////////////////////////////////
2460 
2461 class ContourIter {
2462 public:
2463     ContourIter(const SkPathRef& pathRef);
2464 
done() const2465     bool done() const { return fDone; }
2466     // if !done() then these may be called
count() const2467     int count() const { return fCurrPtCount; }
pts() const2468     const SkPoint* pts() const { return fCurrPt; }
2469     void next();
2470 
2471 private:
2472     int fCurrPtCount;
2473     const SkPoint* fCurrPt;
2474     const uint8_t* fCurrVerb;
2475     const uint8_t* fStopVerbs;
2476     const SkScalar* fCurrConicWeight;
2477     bool fDone;
2478     SkDEBUGCODE(int fContourCounter;)
2479 };
2480 
ContourIter(const SkPathRef & pathRef)2481 ContourIter::ContourIter(const SkPathRef& pathRef) {
2482     fStopVerbs = pathRef.verbsEnd();
2483     fDone = false;
2484     fCurrPt = pathRef.points();
2485     fCurrVerb = pathRef.verbsBegin();
2486     fCurrConicWeight = pathRef.conicWeights();
2487     fCurrPtCount = 0;
2488     SkDEBUGCODE(fContourCounter = 0;)
2489     this->next();
2490 }
2491 
next()2492 void ContourIter::next() {
2493     if (fCurrVerb >= fStopVerbs) {
2494         fDone = true;
2495     }
2496     if (fDone) {
2497         return;
2498     }
2499 
2500     // skip pts of prev contour
2501     fCurrPt += fCurrPtCount;
2502 
2503     SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
2504     int ptCount = 1;    // moveTo
2505     const uint8_t* verbs = fCurrVerb;
2506 
2507     for (verbs++; verbs < fStopVerbs; verbs++) {
2508         switch (*verbs) {
2509             case SkPath::kMove_Verb:
2510                 goto CONTOUR_END;
2511             case SkPath::kLine_Verb:
2512                 ptCount += 1;
2513                 break;
2514             case SkPath::kConic_Verb:
2515                 fCurrConicWeight += 1;
2516                 [[fallthrough]];
2517             case SkPath::kQuad_Verb:
2518                 ptCount += 2;
2519                 break;
2520             case SkPath::kCubic_Verb:
2521                 ptCount += 3;
2522                 break;
2523             case SkPath::kClose_Verb:
2524                 break;
2525             default:
2526                 SkDEBUGFAIL("unexpected verb");
2527                 break;
2528         }
2529     }
2530 CONTOUR_END:
2531     fCurrPtCount = ptCount;
2532     fCurrVerb = verbs;
2533     SkDEBUGCODE(++fContourCounter;)
2534 }
2535 
2536 // returns cross product of (p1 - p0) and (p2 - p0)
cross_prod(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)2537 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2538     SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2539     // We may get 0 when the above subtracts underflow. We expect this to be
2540     // very rare and lazily promote to double.
2541     if (0 == cross) {
2542         double p0x = SkScalarToDouble(p0.fX);
2543         double p0y = SkScalarToDouble(p0.fY);
2544 
2545         double p1x = SkScalarToDouble(p1.fX);
2546         double p1y = SkScalarToDouble(p1.fY);
2547 
2548         double p2x = SkScalarToDouble(p2.fX);
2549         double p2y = SkScalarToDouble(p2.fY);
2550 
2551         cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2552                                  (p1y - p0y) * (p2x - p0x));
2553 
2554     }
2555     return cross;
2556 }
2557 
2558 // Returns the first pt with the maximum Y coordinate
find_max_y(const SkPoint pts[],int count)2559 static int find_max_y(const SkPoint pts[], int count) {
2560     SkASSERT(count > 0);
2561     SkScalar max = pts[0].fY;
2562     int firstIndex = 0;
2563     for (int i = 1; i < count; ++i) {
2564         SkScalar y = pts[i].fY;
2565         if (y > max) {
2566             max = y;
2567             firstIndex = i;
2568         }
2569     }
2570     return firstIndex;
2571 }
2572 
find_diff_pt(const SkPoint pts[],int index,int n,int inc)2573 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2574     int i = index;
2575     for (;;) {
2576         i = (i + inc) % n;
2577         if (i == index) {   // we wrapped around, so abort
2578             break;
2579         }
2580         if (pts[index] != pts[i]) { // found a different point, success!
2581             break;
2582         }
2583     }
2584     return i;
2585 }
2586 
2587 /**
2588  *  Starting at index, and moving forward (incrementing), find the xmin and
2589  *  xmax of the contiguous points that have the same Y.
2590  */
find_min_max_x_at_y(const SkPoint pts[],int index,int n,int * maxIndexPtr)2591 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2592                                int* maxIndexPtr) {
2593     const SkScalar y = pts[index].fY;
2594     SkScalar min = pts[index].fX;
2595     SkScalar max = min;
2596     int minIndex = index;
2597     int maxIndex = index;
2598     for (int i = index + 1; i < n; ++i) {
2599         if (pts[i].fY != y) {
2600             break;
2601         }
2602         SkScalar x = pts[i].fX;
2603         if (x < min) {
2604             min = x;
2605             minIndex = i;
2606         } else if (x > max) {
2607             max = x;
2608             maxIndex = i;
2609         }
2610     }
2611     *maxIndexPtr = maxIndex;
2612     return minIndex;
2613 }
2614 
crossToDir(SkScalar cross)2615 static SkPathFirstDirection crossToDir(SkScalar cross) {
2616     return cross > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW;
2617 }
2618 
2619 /*
2620  *  We loop through all contours, and keep the computed cross-product of the
2621  *  contour that contained the global y-max. If we just look at the first
2622  *  contour, we may find one that is wound the opposite way (correctly) since
2623  *  it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2624  *  that is outer most (or at least has the global y-max) before we can consider
2625  *  its cross product.
2626  */
ComputeFirstDirection(const SkPath & path)2627 SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) {
2628     auto d = path.getFirstDirection();
2629     if (d != SkPathFirstDirection::kUnknown) {
2630         return d;
2631     }
2632 
2633     // We don't want to pay the cost for computing convexity if it is unknown,
2634     // so we call getConvexityOrUnknown() instead of isConvex().
2635     if (path.getConvexityOrUnknown() == SkPathConvexity::kConvex) {
2636         SkASSERT(d == SkPathFirstDirection::kUnknown);
2637         return d;
2638     }
2639 
2640     ContourIter iter(*path.fPathRef);
2641 
2642     // initialize with our logical y-min
2643     SkScalar ymax = path.getBounds().fTop;
2644     SkScalar ymaxCross = 0;
2645 
2646     for (; !iter.done(); iter.next()) {
2647         int n = iter.count();
2648         if (n < 3) {
2649             continue;
2650         }
2651 
2652         const SkPoint* pts = iter.pts();
2653         SkScalar cross = 0;
2654         int index = find_max_y(pts, n);
2655         if (pts[index].fY < ymax) {
2656             continue;
2657         }
2658 
2659         // If there is more than 1 distinct point at the y-max, we take the
2660         // x-min and x-max of them and just subtract to compute the dir.
2661         if (pts[(index + 1) % n].fY == pts[index].fY) {
2662             int maxIndex;
2663             int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2664             if (minIndex == maxIndex) {
2665                 goto TRY_CROSSPROD;
2666             }
2667             SkASSERT(pts[minIndex].fY == pts[index].fY);
2668             SkASSERT(pts[maxIndex].fY == pts[index].fY);
2669             SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2670             // we just subtract the indices, and let that auto-convert to
2671             // SkScalar, since we just want - or + to signal the direction.
2672             cross = minIndex - maxIndex;
2673         } else {
2674             TRY_CROSSPROD:
2675             // Find a next and prev index to use for the cross-product test,
2676             // but we try to find pts that form non-zero vectors from pts[index]
2677             //
2678             // Its possible that we can't find two non-degenerate vectors, so
2679             // we have to guard our search (e.g. all the pts could be in the
2680             // same place).
2681 
2682             // we pass n - 1 instead of -1 so we don't foul up % operator by
2683             // passing it a negative LH argument.
2684             int prev = find_diff_pt(pts, index, n, n - 1);
2685             if (prev == index) {
2686                 // completely degenerate, skip to next contour
2687                 continue;
2688             }
2689             int next = find_diff_pt(pts, index, n, 1);
2690             SkASSERT(next != index);
2691             cross = cross_prod(pts[prev], pts[index], pts[next]);
2692             // if we get a zero and the points are horizontal, then we look at the spread in
2693             // x-direction. We really should continue to walk away from the degeneracy until
2694             // there is a divergence.
2695             if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2696                 // construct the subtract so we get the correct Direction below
2697                 cross = pts[index].fX - pts[next].fX;
2698             }
2699         }
2700 
2701         if (cross) {
2702             // record our best guess so far
2703             ymax = pts[index].fY;
2704             ymaxCross = cross;
2705         }
2706     }
2707     if (ymaxCross) {
2708         d = crossToDir(ymaxCross);
2709         path.setFirstDirection(d);
2710     }
2711     return d;   // may still be kUnknown
2712 }
2713 
2714 ///////////////////////////////////////////////////////////////////////////////
2715 
between(SkScalar a,SkScalar b,SkScalar c)2716 static bool between(SkScalar a, SkScalar b, SkScalar c) {
2717     SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
2718             || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
2719     return (a - b) * (c - b) <= 0;
2720 }
2721 
eval_cubic_pts(SkScalar c0,SkScalar c1,SkScalar c2,SkScalar c3,SkScalar t)2722 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2723                                SkScalar t) {
2724     SkScalar A = c3 + 3*(c1 - c2) - c0;
2725     SkScalar B = 3*(c2 - c1 - c1 + c0);
2726     SkScalar C = 3*(c1 - c0);
2727     SkScalar D = c0;
2728     return poly_eval(A, B, C, D, t);
2729 }
2730 
find_minmax(const SkPoint pts[],SkScalar * minPtr,SkScalar * maxPtr)2731 template <size_t N> static void find_minmax(const SkPoint pts[],
2732                                             SkScalar* minPtr, SkScalar* maxPtr) {
2733     SkScalar min, max;
2734     min = max = pts[0].fX;
2735     for (size_t i = 1; i < N; ++i) {
2736         min = std::min(min, pts[i].fX);
2737         max = std::max(max, pts[i].fX);
2738     }
2739     *minPtr = min;
2740     *maxPtr = max;
2741 }
2742 
checkOnCurve(SkScalar x,SkScalar y,const SkPoint & start,const SkPoint & end)2743 static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
2744     if (start.fY == end.fY) {
2745         return between(start.fX, x, end.fX) && x != end.fX;
2746     } else {
2747         return x == start.fX && y == start.fY;
2748     }
2749 }
2750 
winding_mono_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2751 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2752     SkScalar y0 = pts[0].fY;
2753     SkScalar y3 = pts[3].fY;
2754 
2755     int dir = 1;
2756     if (y0 > y3) {
2757         using std::swap;
2758         swap(y0, y3);
2759         dir = -1;
2760     }
2761     if (y < y0 || y > y3) {
2762         return 0;
2763     }
2764     if (checkOnCurve(x, y, pts[0], pts[3])) {
2765         *onCurveCount += 1;
2766         return 0;
2767     }
2768     if (y == y3) {
2769         return 0;
2770     }
2771 
2772     // quickreject or quickaccept
2773     SkScalar min, max;
2774     find_minmax<4>(pts, &min, &max);
2775     if (x < min) {
2776         return 0;
2777     }
2778     if (x > max) {
2779         return dir;
2780     }
2781 
2782     // compute the actual x(t) value
2783     SkScalar t;
2784     if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
2785         return 0;
2786     }
2787     SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
2788     if (SkScalarNearlyEqual(xt, x)) {
2789         if (x != pts[3].fX || y != pts[3].fY) {  // don't test end points; they're start points
2790             *onCurveCount += 1;
2791             return 0;
2792         }
2793     }
2794     return xt < x ? dir : 0;
2795 }
2796 
winding_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2797 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2798     SkPoint dst[10];
2799     int n = SkChopCubicAtYExtrema(pts, dst);
2800     int w = 0;
2801     for (int i = 0; i <= n; ++i) {
2802         w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
2803     }
2804     return w;
2805 }
2806 
conic_eval_numerator(const SkScalar src[],SkScalar w,SkScalar t)2807 static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
2808     SkASSERT(src);
2809     SkASSERT(t >= 0 && t <= 1);
2810     SkScalar src2w = src[2] * w;
2811     SkScalar C = src[0];
2812     SkScalar A = src[4] - 2 * src2w + C;
2813     SkScalar B = 2 * (src2w - C);
2814     return poly_eval(A, B, C, t);
2815 }
2816 
2817 
conic_eval_denominator(SkScalar w,SkScalar t)2818 static double conic_eval_denominator(SkScalar w, SkScalar t) {
2819     SkScalar B = 2 * (w - 1);
2820     SkScalar C = 1;
2821     SkScalar A = -B;
2822     return poly_eval(A, B, C, t);
2823 }
2824 
winding_mono_conic(const SkConic & conic,SkScalar x,SkScalar y,int * onCurveCount)2825 static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
2826     const SkPoint* pts = conic.fPts;
2827     SkScalar y0 = pts[0].fY;
2828     SkScalar y2 = pts[2].fY;
2829 
2830     int dir = 1;
2831     if (y0 > y2) {
2832         using std::swap;
2833         swap(y0, y2);
2834         dir = -1;
2835     }
2836     if (y < y0 || y > y2) {
2837         return 0;
2838     }
2839     if (checkOnCurve(x, y, pts[0], pts[2])) {
2840         *onCurveCount += 1;
2841         return 0;
2842     }
2843     if (y == y2) {
2844         return 0;
2845     }
2846 
2847     SkScalar roots[2];
2848     SkScalar A = pts[2].fY;
2849     SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
2850     SkScalar C = pts[0].fY;
2851     A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
2852     B -= C;  // B = b*w - w * yCept + yCept - a
2853     C -= y;
2854     int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
2855     SkASSERT(n <= 1);
2856     SkScalar xt;
2857     if (0 == n) {
2858         // zero roots are returned only when y0 == y
2859         // Need [0] if dir == 1
2860         // and  [2] if dir == -1
2861         xt = pts[1 - dir].fX;
2862     } else {
2863         SkScalar t = roots[0];
2864         xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
2865     }
2866     if (SkScalarNearlyEqual(xt, x)) {
2867         if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
2868             *onCurveCount += 1;
2869             return 0;
2870         }
2871     }
2872     return xt < x ? dir : 0;
2873 }
2874 
is_mono_quad(SkScalar y0,SkScalar y1,SkScalar y2)2875 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
2876     //    return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
2877     if (y0 == y1) {
2878         return true;
2879     }
2880     if (y0 < y1) {
2881         return y1 <= y2;
2882     } else {
2883         return y1 >= y2;
2884     }
2885 }
2886 
winding_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar weight,int * onCurveCount)2887 static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
2888                          int* onCurveCount) {
2889     SkConic conic(pts, weight);
2890     SkConic chopped[2];
2891     // If the data points are very large, the conic may not be monotonic but may also
2892     // fail to chop. Then, the chopper does not split the original conic in two.
2893     bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
2894     int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
2895     if (!isMono) {
2896         w += winding_mono_conic(chopped[1], x, y, onCurveCount);
2897     }
2898     return w;
2899 }
2900 
winding_mono_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2901 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2902     SkScalar y0 = pts[0].fY;
2903     SkScalar y2 = pts[2].fY;
2904 
2905     int dir = 1;
2906     if (y0 > y2) {
2907         using std::swap;
2908         swap(y0, y2);
2909         dir = -1;
2910     }
2911     if (y < y0 || y > y2) {
2912         return 0;
2913     }
2914     if (checkOnCurve(x, y, pts[0], pts[2])) {
2915         *onCurveCount += 1;
2916         return 0;
2917     }
2918     if (y == y2) {
2919         return 0;
2920     }
2921     // bounds check on X (not required. is it faster?)
2922 #if 0
2923     if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2924         return 0;
2925     }
2926 #endif
2927 
2928     SkScalar roots[2];
2929     int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2930                                 2 * (pts[1].fY - pts[0].fY),
2931                                 pts[0].fY - y,
2932                                 roots);
2933     SkASSERT(n <= 1);
2934     SkScalar xt;
2935     if (0 == n) {
2936         // zero roots are returned only when y0 == y
2937         // Need [0] if dir == 1
2938         // and  [2] if dir == -1
2939         xt = pts[1 - dir].fX;
2940     } else {
2941         SkScalar t = roots[0];
2942         SkScalar C = pts[0].fX;
2943         SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2944         SkScalar B = 2 * (pts[1].fX - C);
2945         xt = poly_eval(A, B, C, t);
2946     }
2947     if (SkScalarNearlyEqual(xt, x)) {
2948         if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
2949             *onCurveCount += 1;
2950             return 0;
2951         }
2952     }
2953     return xt < x ? dir : 0;
2954 }
2955 
winding_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2956 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2957     SkPoint dst[5];
2958     int     n = 0;
2959 
2960     if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
2961         n = SkChopQuadAtYExtrema(pts, dst);
2962         pts = dst;
2963     }
2964     int w = winding_mono_quad(pts, x, y, onCurveCount);
2965     if (n > 0) {
2966         w += winding_mono_quad(&pts[2], x, y, onCurveCount);
2967     }
2968     return w;
2969 }
2970 
winding_line(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2971 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2972     SkScalar x0 = pts[0].fX;
2973     SkScalar y0 = pts[0].fY;
2974     SkScalar x1 = pts[1].fX;
2975     SkScalar y1 = pts[1].fY;
2976 
2977     SkScalar dy = y1 - y0;
2978 
2979     int dir = 1;
2980     if (y0 > y1) {
2981         using std::swap;
2982         swap(y0, y1);
2983         dir = -1;
2984     }
2985     if (y < y0 || y > y1) {
2986         return 0;
2987     }
2988     if (checkOnCurve(x, y, pts[0], pts[1])) {
2989         *onCurveCount += 1;
2990         return 0;
2991     }
2992     if (y == y1) {
2993         return 0;
2994     }
2995     SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
2996 
2997     if (!cross) {
2998         // zero cross means the point is on the line, and since the case where
2999         // y of the query point is at the end point is handled above, we can be
3000         // sure that we're on the line (excluding the end point) here
3001         if (x != x1 || y != pts[1].fY) {
3002             *onCurveCount += 1;
3003         }
3004         dir = 0;
3005     } else if (SkScalarSignAsInt(cross) == dir) {
3006         dir = 0;
3007     }
3008     return dir;
3009 }
3010 
tangent_cubic(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3011 static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
3012         SkTDArray<SkVector>* tangents) {
3013     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
3014              && !between(pts[2].fY, y, pts[3].fY)) {
3015         return;
3016     }
3017     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
3018              && !between(pts[2].fX, x, pts[3].fX)) {
3019         return;
3020     }
3021     SkPoint dst[10];
3022     int n = SkChopCubicAtYExtrema(pts, dst);
3023     for (int i = 0; i <= n; ++i) {
3024         SkPoint* c = &dst[i * 3];
3025         SkScalar t;
3026         if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
3027             continue;
3028         }
3029         SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
3030         if (!SkScalarNearlyEqual(x, xt)) {
3031             continue;
3032         }
3033         SkVector tangent;
3034         SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
3035         tangents->push_back(tangent);
3036     }
3037 }
3038 
tangent_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar w,SkTDArray<SkVector> * tangents)3039 static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
3040             SkTDArray<SkVector>* tangents) {
3041     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3042         return;
3043     }
3044     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3045         return;
3046     }
3047     SkScalar roots[2];
3048     SkScalar A = pts[2].fY;
3049     SkScalar B = pts[1].fY * w - y * w + y;
3050     SkScalar C = pts[0].fY;
3051     A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
3052     B -= C;  // B = b*w - w * yCept + yCept - a
3053     C -= y;
3054     int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3055     for (int index = 0; index < n; ++index) {
3056         SkScalar t = roots[index];
3057         SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
3058         if (!SkScalarNearlyEqual(x, xt)) {
3059             continue;
3060         }
3061         SkConic conic(pts, w);
3062         tangents->push_back(conic.evalTangentAt(t));
3063     }
3064 }
3065 
tangent_quad(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3066 static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
3067         SkTDArray<SkVector>* tangents) {
3068     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3069         return;
3070     }
3071     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3072         return;
3073     }
3074     SkScalar roots[2];
3075     int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3076                                 2 * (pts[1].fY - pts[0].fY),
3077                                 pts[0].fY - y,
3078                                 roots);
3079     for (int index = 0; index < n; ++index) {
3080         SkScalar t = roots[index];
3081         SkScalar C = pts[0].fX;
3082         SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3083         SkScalar B = 2 * (pts[1].fX - C);
3084         SkScalar xt = poly_eval(A, B, C, t);
3085         if (!SkScalarNearlyEqual(x, xt)) {
3086             continue;
3087         }
3088         tangents->push_back(SkEvalQuadTangentAt(pts, t));
3089     }
3090 }
3091 
tangent_line(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3092 static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
3093         SkTDArray<SkVector>* tangents) {
3094     SkScalar y0 = pts[0].fY;
3095     SkScalar y1 = pts[1].fY;
3096     if (!between(y0, y, y1)) {
3097         return;
3098     }
3099     SkScalar x0 = pts[0].fX;
3100     SkScalar x1 = pts[1].fX;
3101     if (!between(x0, x, x1)) {
3102         return;
3103     }
3104     SkScalar dx = x1 - x0;
3105     SkScalar dy = y1 - y0;
3106     if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3107         return;
3108     }
3109     SkVector v;
3110     v.set(dx, dy);
3111     tangents->push_back(v);
3112 }
3113 
contains_inclusive(const SkRect & r,SkScalar x,SkScalar y)3114 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3115     return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3116 }
3117 
contains(SkScalar x,SkScalar y) const3118 bool SkPath::contains(SkScalar x, SkScalar y) const {
3119     bool isInverse = this->isInverseFillType();
3120     if (this->isEmpty()) {
3121         return isInverse;
3122     }
3123 
3124     if (!contains_inclusive(this->getBounds(), x, y)) {
3125         return isInverse;
3126     }
3127 
3128     SkPath::Iter iter(*this, true);
3129     bool done = false;
3130     int w = 0;
3131     int onCurveCount = 0;
3132     do {
3133         SkPoint pts[4];
3134         switch (iter.next(pts)) {
3135             case SkPath::kMove_Verb:
3136             case SkPath::kClose_Verb:
3137                 break;
3138             case SkPath::kLine_Verb:
3139                 w += winding_line(pts, x, y, &onCurveCount);
3140                 break;
3141             case SkPath::kQuad_Verb:
3142                 w += winding_quad(pts, x, y, &onCurveCount);
3143                 break;
3144             case SkPath::kConic_Verb:
3145                 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3146                 break;
3147             case SkPath::kCubic_Verb:
3148                 w += winding_cubic(pts, x, y, &onCurveCount);
3149                 break;
3150             case SkPath::kDone_Verb:
3151                 done = true;
3152                 break;
3153        }
3154     } while (!done);
3155     bool evenOddFill = SkPathFillType::kEvenOdd        == this->getFillType()
3156                     || SkPathFillType::kInverseEvenOdd == this->getFillType();
3157     if (evenOddFill) {
3158         w &= 1;
3159     }
3160     if (w) {
3161         return !isInverse;
3162     }
3163     if (onCurveCount <= 1) {
3164         return SkToBool(onCurveCount) ^ isInverse;
3165     }
3166     if ((onCurveCount & 1) || evenOddFill) {
3167         return SkToBool(onCurveCount & 1) ^ isInverse;
3168     }
3169     // If the point touches an even number of curves, and the fill is winding, check for
3170     // coincidence. Count coincidence as places where the on curve points have identical tangents.
3171     iter.setPath(*this, true);
3172     done = false;
3173     SkTDArray<SkVector> tangents;
3174     do {
3175         SkPoint pts[4];
3176         int oldCount = tangents.size();
3177         switch (iter.next(pts)) {
3178             case SkPath::kMove_Verb:
3179             case SkPath::kClose_Verb:
3180                 break;
3181             case SkPath::kLine_Verb:
3182                 tangent_line(pts, x, y, &tangents);
3183                 break;
3184             case SkPath::kQuad_Verb:
3185                 tangent_quad(pts, x, y, &tangents);
3186                 break;
3187             case SkPath::kConic_Verb:
3188                 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3189                 break;
3190             case SkPath::kCubic_Verb:
3191                 tangent_cubic(pts, x, y, &tangents);
3192                 break;
3193             case SkPath::kDone_Verb:
3194                 done = true;
3195                 break;
3196        }
3197        if (tangents.size() > oldCount) {
3198             int last = tangents.size() - 1;
3199             const SkVector& tangent = tangents[last];
3200             if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) {
3201                 tangents.remove(last);
3202             } else {
3203                 for (int index = 0; index < last; ++index) {
3204                     const SkVector& test = tangents[index];
3205                     if (SkScalarNearlyZero(test.cross(tangent))
3206                             && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3207                             && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3208                         tangents.remove(last);
3209                         tangents.removeShuffle(index);
3210                         break;
3211                     }
3212                 }
3213             }
3214         }
3215     } while (!done);
3216     return SkToBool(tangents.size()) ^ isInverse;
3217 }
3218 
3219 // Sort of like makeSpace(0) but the the additional requirement that we actively shrink the
3220 // allocations to just fit the current needs. makeSpace() will only grow, but never shrinks.
3221 //
shrinkToFit()3222 void SkPath::shrinkToFit() {
3223     // Since this can relocate the allocated arrays, we have to defensively copy ourselves if
3224     // we're not the only owner of the pathref... since relocating the arrays will invalidate
3225     // any existing iterators.
3226     if (!fPathRef->unique()) {
3227         SkPathRef* pr = new SkPathRef;
3228         pr->copy(*fPathRef, 0, 0, 0);
3229         fPathRef.reset(pr);
3230     }
3231     fPathRef->fPoints.shrink_to_fit();
3232     fPathRef->fVerbs.shrink_to_fit();
3233     fPathRef->fConicWeights.shrink_to_fit();
3234     SkDEBUGCODE(fPathRef->validate();)
3235 }
3236 
3237 
ConvertConicToQuads(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,SkScalar w,SkPoint pts[],int pow2)3238 int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3239                                 SkScalar w, SkPoint pts[], int pow2) {
3240     const SkConic conic(p0, p1, p2, w);
3241     return conic.chopIntoQuadsPOW2(pts, pow2);
3242 }
3243 
IsSimpleRect(const SkPath & path,bool isSimpleFill,SkRect * rect,SkPathDirection * direction,unsigned * start)3244 bool SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect,
3245                               SkPathDirection* direction, unsigned* start) {
3246     if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3247         return false;
3248     }
3249     SkPoint rectPts[5];
3250     int rectPtCnt = 0;
3251     bool needsClose = !isSimpleFill;
3252     for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) {
3253         switch (v) {
3254             case SkPathVerb::kMove:
3255                 if (0 != rectPtCnt) {
3256                     return false;
3257                 }
3258                 rectPts[0] = verbPts[0];
3259                 ++rectPtCnt;
3260                 break;
3261             case SkPathVerb::kLine:
3262                 if (5 == rectPtCnt) {
3263                     return false;
3264                 }
3265                 rectPts[rectPtCnt] = verbPts[1];
3266                 ++rectPtCnt;
3267                 break;
3268             case SkPathVerb::kClose:
3269                 if (4 == rectPtCnt) {
3270                     rectPts[4] = rectPts[0];
3271                     rectPtCnt = 5;
3272                 }
3273                 needsClose = false;
3274                 break;
3275             case SkPathVerb::kQuad:
3276             case SkPathVerb::kConic:
3277             case SkPathVerb::kCubic:
3278                 return false;
3279         }
3280     }
3281     if (needsClose) {
3282         return false;
3283     }
3284     if (rectPtCnt < 5) {
3285         return false;
3286     }
3287     if (rectPts[0] != rectPts[4]) {
3288         return false;
3289     }
3290     // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
3291     // and pts 1 and 2 the opposite vertical or horizontal edge).
3292     bool vec03IsVertical;
3293     if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
3294         rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
3295         // Make sure it has non-zero width and height
3296         if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
3297             return false;
3298         }
3299         vec03IsVertical = true;
3300     } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
3301                rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
3302         // Make sure it has non-zero width and height
3303         if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
3304             return false;
3305         }
3306         vec03IsVertical = false;
3307     } else {
3308         return false;
3309     }
3310     // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
3311     // set if it is on the bottom edge.
3312     unsigned sortFlags =
3313             ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
3314             ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
3315     switch (sortFlags) {
3316         case 0b00:
3317             rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
3318             *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
3319             *start = 0;
3320             break;
3321         case 0b01:
3322             rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
3323             *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
3324             *start = 1;
3325             break;
3326         case 0b10:
3327             rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
3328             *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
3329             *start = 3;
3330             break;
3331         case 0b11:
3332             rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
3333             *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
3334             *start = 2;
3335             break;
3336     }
3337     return true;
3338 }
3339 
DrawArcIsConvex(SkScalar sweepAngle,SkArc::Type arcType,bool isFillNoPathEffect)3340 bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle,
3341                                  SkArc::Type arcType,
3342                                  bool isFillNoPathEffect) {
3343     if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3344         // This gets converted to an oval.
3345         return true;
3346     }
3347     if (arcType == SkArc::Type::kWedge) {
3348         // This is a pie wedge. It's convex if the angle is <= 180.
3349         return SkScalarAbs(sweepAngle) <= 180.f;
3350     }
3351     // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
3352     // to a secant, i.e. convex.
3353     return SkScalarAbs(sweepAngle) <= 360.f;
3354 }
3355 
CreateDrawArcPath(SkPath * path,const SkArc & arc,bool isFillNoPathEffect)3356 void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect) {
3357     SkRect oval = arc.fOval;
3358     SkScalar startAngle = arc.fStartAngle, sweepAngle = arc.fSweepAngle;
3359     SkASSERT(!oval.isEmpty());
3360     SkASSERT(sweepAngle);
3361     // We cap the number of total rotations. This keeps the resulting paths simpler. More important,
3362     // it prevents values so large that the loops below never terminate (once ULP > 360).
3363     if (SkScalarAbs(sweepAngle) > 3600.0f) {
3364         sweepAngle = std::copysign(3600.0f, sweepAngle) + std::fmod(sweepAngle, 360.0f);
3365     }
3366     path->reset();
3367     path->setIsVolatile(true);
3368     path->setFillType(SkPathFillType::kWinding);
3369     if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3370         path->addOval(oval);
3371         SkASSERT(path->isConvex() &&
3372                  DrawArcIsConvex(sweepAngle, SkArc::Type::kArc, isFillNoPathEffect));
3373         return;
3374     }
3375     if (arc.isWedge()) {
3376         path->moveTo(oval.centerX(), oval.centerY());
3377     }
3378     auto firstDir =
3379             sweepAngle > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW;
3380     bool convex = DrawArcIsConvex(sweepAngle, arc.fType, isFillNoPathEffect);
3381     // Arc to mods at 360 and drawArc is not supposed to.
3382     bool forceMoveTo = !arc.isWedge();
3383     while (sweepAngle <= -360.f) {
3384         path->arcTo(oval, startAngle, -180.f, forceMoveTo);
3385         startAngle -= 180.f;
3386         path->arcTo(oval, startAngle, -180.f, false);
3387         startAngle -= 180.f;
3388         forceMoveTo = false;
3389         sweepAngle += 360.f;
3390     }
3391     while (sweepAngle >= 360.f) {
3392         path->arcTo(oval, startAngle, 180.f, forceMoveTo);
3393         startAngle += 180.f;
3394         path->arcTo(oval, startAngle, 180.f, false);
3395         startAngle += 180.f;
3396         forceMoveTo = false;
3397         sweepAngle -= 360.f;
3398     }
3399     path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
3400     if (arc.isWedge()) {
3401         path->close();
3402     }
3403     path->setConvexity(convex ? SkPathConvexity::kConvex : SkPathConvexity::kConcave);
3404     path->setFirstDirection(firstDir);
3405 }
3406 
3407 ///////////////////////////////////////////////////////////////////////////////////////////////////
3408 
compute_quad_extremas(const SkPoint src[3],SkPoint extremas[3])3409 static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
3410     SkScalar ts[2];
3411     int n  = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
3412         n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
3413     SkASSERT(n >= 0 && n <= 2);
3414     for (int i = 0; i < n; ++i) {
3415         extremas[i] = SkEvalQuadAt(src, ts[i]);
3416     }
3417     extremas[n] = src[2];
3418     return n + 1;
3419 }
3420 
compute_conic_extremas(const SkPoint src[3],SkScalar w,SkPoint extremas[3])3421 static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
3422     SkConic conic(src[0], src[1], src[2], w);
3423     SkScalar ts[2];
3424     int n  = conic.findXExtrema(ts);
3425         n += conic.findYExtrema(&ts[n]);
3426     SkASSERT(n >= 0 && n <= 2);
3427     for (int i = 0; i < n; ++i) {
3428         extremas[i] = conic.evalAt(ts[i]);
3429     }
3430     extremas[n] = src[2];
3431     return n + 1;
3432 }
3433 
compute_cubic_extremas(const SkPoint src[4],SkPoint extremas[5])3434 static int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) {
3435     SkScalar ts[4];
3436     int n  = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
3437         n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
3438     SkASSERT(n >= 0 && n <= 4);
3439     for (int i = 0; i < n; ++i) {
3440         SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
3441     }
3442     extremas[n] = src[3];
3443     return n + 1;
3444 }
3445 
computeTightBounds() const3446 SkRect SkPath::computeTightBounds() const {
3447     if (0 == this->countVerbs()) {
3448         return SkRect::MakeEmpty();
3449     }
3450 
3451     if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
3452         return this->getBounds();
3453     }
3454 
3455     SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
3456 
3457     // initial with the first MoveTo, so we don't have to check inside the switch
3458     skvx::float2 min, max;
3459     min = max = from_point(this->getPoint(0));
3460     for (auto [verb, pts, w] : SkPathPriv::Iterate(*this)) {
3461         int count = 0;
3462         switch (verb) {
3463             case SkPathVerb::kMove:
3464                 extremas[0] = pts[0];
3465                 count = 1;
3466                 break;
3467             case SkPathVerb::kLine:
3468                 extremas[0] = pts[1];
3469                 count = 1;
3470                 break;
3471             case SkPathVerb::kQuad:
3472                 count = compute_quad_extremas(pts, extremas);
3473                 break;
3474             case SkPathVerb::kConic:
3475                 count = compute_conic_extremas(pts, *w, extremas);
3476                 break;
3477             case SkPathVerb::kCubic:
3478                 count = compute_cubic_extremas(pts, extremas);
3479                 break;
3480             case SkPathVerb::kClose:
3481                 break;
3482         }
3483         for (int i = 0; i < count; ++i) {
3484             skvx::float2 tmp = from_point(extremas[i]);
3485             min = skvx::min(min, tmp);
3486             max = skvx::max(max, tmp);
3487         }
3488     }
3489     SkRect bounds;
3490     min.store((SkPoint*)&bounds.fLeft);
3491     max.store((SkPoint*)&bounds.fRight);
3492     return bounds;
3493 }
3494 
IsLineDegenerate(const SkPoint & p1,const SkPoint & p2,bool exact)3495 bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
3496     return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2);
3497 }
3498 
IsQuadDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,bool exact)3499 bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
3500                                 const SkPoint& p3, bool exact) {
3501     return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3502             SkPointPriv::EqualsWithinTolerance(p2, p3);
3503 }
3504 
IsCubicDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,const SkPoint & p4,bool exact)3505 bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
3506                                 const SkPoint& p3, const SkPoint& p4, bool exact) {
3507     return exact ? p1 == p2 && p2 == p3 && p3 == p4 :
3508             SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3509             SkPointPriv::EqualsWithinTolerance(p2, p3) &&
3510             SkPointPriv::EqualsWithinTolerance(p3, p4);
3511 }
3512 
3513 //////////////////////////////////////////////////////////////////////////////////////////////////
3514 
sk_path_analyze_verbs(const uint8_t vbs[],int verbCount)3515 SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t vbs[], int verbCount) {
3516     SkPathVerbAnalysis info = {false, 0, 0, 0};
3517     bool needMove = true;
3518     bool invalid = false;
3519 
3520     if (verbCount >= (INT_MAX / 3)) SK_UNLIKELY {
3521         // A path with an extremely high number of quad, conic or cubic verbs could cause
3522         // `info.points` to overflow. To prevent against this, we reject extremely large paths. This
3523         // check is conservative and assumes the worst case (in particular, it assumes that every
3524         // verb consumes 3 points, which would only happen for a path composed entirely of cubics).
3525         // This limits us to 700 million verbs, which is large enough for any reasonable use case.
3526         invalid = true;
3527     } else {
3528         for (int i = 0; i < verbCount; ++i) {
3529             switch ((SkPathVerb)vbs[i]) {
3530                 case SkPathVerb::kMove:
3531                     needMove = false;
3532                     info.points += 1;
3533                     break;
3534                 case SkPathVerb::kLine:
3535                     invalid |= needMove;
3536                     info.segmentMask |= kLine_SkPathSegmentMask;
3537                     info.points += 1;
3538                     break;
3539                 case SkPathVerb::kQuad:
3540                     invalid |= needMove;
3541                     info.segmentMask |= kQuad_SkPathSegmentMask;
3542                     info.points += 2;
3543                     break;
3544                 case SkPathVerb::kConic:
3545                     invalid |= needMove;
3546                     info.segmentMask |= kConic_SkPathSegmentMask;
3547                     info.points += 2;
3548                     info.weights += 1;
3549                     break;
3550                 case SkPathVerb::kCubic:
3551                     invalid |= needMove;
3552                     info.segmentMask |= kCubic_SkPathSegmentMask;
3553                     info.points += 3;
3554                     break;
3555                 case SkPathVerb::kClose:
3556                     invalid |= needMove;
3557                     needMove = true;
3558                     break;
3559                 default:
3560                     invalid = true;
3561                     break;
3562             }
3563         }
3564     }
3565     info.valid = !invalid;
3566     return info;
3567 }
3568 
Make(const SkPoint pts[],int pointCount,const uint8_t vbs[],int verbCount,const SkScalar ws[],int wCount,SkPathFillType ft,bool isVolatile)3569 SkPath SkPath::Make(const SkPoint pts[], int pointCount,
3570                     const uint8_t vbs[], int verbCount,
3571                     const SkScalar ws[], int wCount,
3572                     SkPathFillType ft, bool isVolatile) {
3573     if (verbCount <= 0) {
3574         return SkPath();
3575     }
3576 
3577     const auto info = sk_path_analyze_verbs(vbs, verbCount);
3578     if (!info.valid || info.points > pointCount || info.weights > wCount) {
3579         SkDEBUGFAIL("invalid verbs and number of points/weights");
3580         return SkPath();
3581     }
3582 
3583     return MakeInternal(info, pts, vbs, verbCount, ws, ft, isVolatile);
3584 }
3585 
Rect(const SkRect & r,SkPathDirection dir,unsigned startIndex)3586 SkPath SkPath::Rect(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
3587     return SkPathBuilder().addRect(r, dir, startIndex).detach();
3588 }
3589 
Oval(const SkRect & r,SkPathDirection dir)3590 SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir) {
3591     return SkPathBuilder().addOval(r, dir).detach();
3592 }
3593 
Oval(const SkRect & r,SkPathDirection dir,unsigned startIndex)3594 SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
3595     return SkPathBuilder().addOval(r, dir, startIndex).detach();
3596 }
3597 
Circle(SkScalar x,SkScalar y,SkScalar r,SkPathDirection dir)3598 SkPath SkPath::Circle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
3599     return SkPathBuilder().addCircle(x, y, r, dir).detach();
3600 }
3601 
RRect(const SkRRect & rr,SkPathDirection dir)3602 SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir) {
3603     return SkPathBuilder().addRRect(rr, dir).detach();
3604 }
3605 
RRect(const SkRRect & rr,SkPathDirection dir,unsigned startIndex)3606 SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) {
3607     return SkPathBuilder().addRRect(rr, dir, startIndex).detach();
3608 }
3609 
RRect(const SkRect & r,SkScalar rx,SkScalar ry,SkPathDirection dir)3610 SkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection dir) {
3611     return SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, rx, ry), dir).detach();
3612 }
3613 
Polygon(const SkPoint pts[],int count,bool isClosed,SkPathFillType ft,bool isVolatile)3614 SkPath SkPath::Polygon(const SkPoint pts[], int count, bool isClosed,
3615                        SkPathFillType ft, bool isVolatile) {
3616     return SkPathBuilder().addPolygon(pts, count, isClosed)
3617                           .setFillType(ft)
3618                           .setIsVolatile(isVolatile)
3619                           .detach();
3620 }
3621 
MakeInternal(const SkPathVerbAnalysis & analysis,const SkPoint points[],const uint8_t verbs[],int verbCount,const SkScalar conics[],SkPathFillType fillType,bool isVolatile)3622 SkPath SkPath::MakeInternal(const SkPathVerbAnalysis& analysis,
3623                             const SkPoint points[],
3624                             const uint8_t verbs[],
3625                             int verbCount,
3626                             const SkScalar conics[],
3627                             SkPathFillType fillType,
3628                             bool isVolatile) {
3629   return SkPath(sk_sp<SkPathRef>(new SkPathRef(
3630                                      SkSpan(points, analysis.points),
3631                                      SkSpan(verbs, verbCount),
3632                                      SkSpan(conics, analysis.weights),
3633                                      analysis.segmentMask)),
3634                 fillType, isVolatile, SkPathConvexity::kUnknown, SkPathFirstDirection::kUnknown);
3635 }
3636 
3637 //////////////////////////////////////////////////////////////////////////////////////////////////
3638 
IsRectContour(const SkPath & path,bool allowPartial,int * currVerb,const SkPoint ** ptsPtr,bool * isClosed,SkPathDirection * direction,SkRect * rect)3639 bool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb,
3640                                const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
3641                                SkRect* rect) {
3642     int corners = 0;
3643     SkPoint closeXY;  // used to determine if final line falls on a diagonal
3644     SkPoint lineStart;  // used to construct line from previous point
3645     const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves)
3646     const SkPoint* lastPt = nullptr;  // last point in the rect (last of lines or first if closed)
3647     SkPoint firstCorner;
3648     SkPoint thirdCorner;
3649     const SkPoint* pts = *ptsPtr;
3650     const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects
3651     lineStart.set(0, 0);
3652     signed char directions[] = {-1, -1, -1, -1, -1};  // -1 to 3; -1 is uninitialized
3653     bool closedOrMoved = false;
3654     bool autoClose = false;
3655     bool insertClose = false;
3656     int verbCnt = path.fPathRef->countVerbs();
3657     while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
3658         uint8_t verb = insertClose ? (uint8_t) SkPath::kClose_Verb : path.fPathRef->atVerb(*currVerb);
3659         switch (verb) {
3660             case SkPath::kClose_Verb:
3661                 savePts = pts;
3662                 autoClose = true;
3663                 insertClose = false;
3664                 [[fallthrough]];
3665             case SkPath::kLine_Verb: {
3666                 if (SkPath::kClose_Verb != verb) {
3667                     lastPt = pts;
3668                 }
3669                 SkPoint lineEnd = SkPath::kClose_Verb == verb ? *firstPt : *pts++;
3670                 SkVector lineDelta = lineEnd - lineStart;
3671                 if (lineDelta.fX && lineDelta.fY) {
3672                     return false; // diagonal
3673                 }
3674                 if (!lineDelta.isFinite()) {
3675                     return false; // path contains infinity or NaN
3676                 }
3677                 if (lineStart == lineEnd) {
3678                     break; // single point on side OK
3679                 }
3680                 int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3
3681                 if (0 == corners) {
3682                     directions[0] = nextDirection;
3683                     corners = 1;
3684                     closedOrMoved = false;
3685                     lineStart = lineEnd;
3686                     break;
3687                 }
3688                 if (closedOrMoved) {
3689                     return false; // closed followed by a line
3690                 }
3691                 if (autoClose && nextDirection == directions[0]) {
3692                     break; // colinear with first
3693                 }
3694                 closedOrMoved = autoClose;
3695                 if (directions[corners - 1] == nextDirection) {
3696                     if (3 == corners && SkPath::kLine_Verb == verb) {
3697                         thirdCorner = lineEnd;
3698                     }
3699                     lineStart = lineEnd;
3700                     break; // colinear segment
3701                 }
3702                 directions[corners++] = nextDirection;
3703                 // opposite lines must point in opposite directions; xoring them should equal 2
3704                 switch (corners) {
3705                     case 2:
3706                         firstCorner = lineStart;
3707                         break;
3708                     case 3:
3709                         if ((directions[0] ^ directions[2]) != 2) {
3710                             return false;
3711                         }
3712                         thirdCorner = lineEnd;
3713                         break;
3714                     case 4:
3715                         if ((directions[1] ^ directions[3]) != 2) {
3716                             return false;
3717                         }
3718                         break;
3719                     default:
3720                         return false; // too many direction changes
3721                 }
3722                 lineStart = lineEnd;
3723                 break;
3724             }
3725             case SkPath::kQuad_Verb:
3726             case SkPath::kConic_Verb:
3727             case SkPath::kCubic_Verb:
3728                 return false; // quadratic, cubic not allowed
3729             case SkPath::kMove_Verb:
3730                 if (allowPartial && !autoClose && directions[0] >= 0) {
3731                     insertClose = true;
3732                     *currVerb -= 1;  // try move again afterwards
3733                     goto addMissingClose;
3734                 }
3735                 if (!corners) {
3736                     firstPt = pts;
3737                 } else {
3738                     closeXY = *firstPt - *lastPt;
3739                     if (closeXY.fX && closeXY.fY) {
3740                         return false;   // we're diagonal, abort
3741                     }
3742                 }
3743                 lineStart = *pts++;
3744                 closedOrMoved = true;
3745                 break;
3746             default:
3747                 SkDEBUGFAIL("unexpected verb");
3748                 break;
3749         }
3750         *currVerb += 1;
3751     addMissingClose:
3752         ;
3753     }
3754     // Success if 4 corners and first point equals last
3755     if (corners < 3 || corners > 4) {
3756         return false;
3757     }
3758     if (savePts) {
3759         *ptsPtr = savePts;
3760     }
3761     // check if close generates diagonal
3762     closeXY = *firstPt - *lastPt;
3763     if (closeXY.fX && closeXY.fY) {
3764         return false;
3765     }
3766     if (rect) {
3767         rect->set(firstCorner, thirdCorner);
3768     }
3769     if (isClosed) {
3770         *isClosed = autoClose;
3771     }
3772     if (direction) {
3773         *direction = directions[0] == ((directions[1] + 1) & 3) ?
3774                      SkPathDirection::kCW : SkPathDirection::kCCW;
3775     }
3776     return true;
3777 }
3778 
3779 
IsNestedFillRects(const SkPath & path,SkRect rects[2],SkPathDirection dirs[2])3780 bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) {
3781     SkDEBUGCODE(path.validate();)
3782     int currVerb = 0;
3783     const SkPoint* pts = path.fPathRef->points();
3784     SkPathDirection testDirs[2];
3785     SkRect testRects[2];
3786     if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) {
3787         return false;
3788     }
3789     if (IsRectContour(path, false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) {
3790         if (testRects[0].contains(testRects[1])) {
3791             if (rects) {
3792                 rects[0] = testRects[0];
3793                 rects[1] = testRects[1];
3794             }
3795             if (dirs) {
3796                 dirs[0] = testDirs[0];
3797                 dirs[1] = testDirs[1];
3798             }
3799             return true;
3800         }
3801         if (testRects[1].contains(testRects[0])) {
3802             if (rects) {
3803                 rects[0] = testRects[1];
3804                 rects[1] = testRects[0];
3805             }
3806             if (dirs) {
3807                 dirs[0] = testDirs[1];
3808                 dirs[1] = testDirs[0];
3809             }
3810             return true;
3811         }
3812     }
3813     return false;
3814 }
3815 
3816 ///////////////////////////////////////////////////////////////////////////////////////////////////
3817 
3818 struct SkHalfPlane {
3819     SkScalar fA, fB, fC;
3820 
evalSkHalfPlane3821     SkScalar eval(SkScalar x, SkScalar y) const {
3822         return fA * x + fB * y + fC;
3823     }
operator ()SkHalfPlane3824     SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
3825 
normalizeSkHalfPlane3826     bool normalize() {
3827         double a = fA;
3828         double b = fB;
3829         double c = fC;
3830         double dmag = sqrt(a * a + b * b);
3831         // length of initial plane normal is zero
3832         if (dmag == 0) {
3833            fA = fB = 0;
3834            fC = SK_Scalar1;
3835            return true;
3836         }
3837         double dscale = sk_ieee_double_divide(1.0, dmag);
3838         a *= dscale;
3839         b *= dscale;
3840         c *= dscale;
3841         // check if we're not finite, or normal is zero-length
3842         if (!SkIsFinite(a, b, c) ||
3843             (a == 0 && b == 0)) {
3844             fA = fB = 0;
3845             fC = SK_Scalar1;
3846             return false;
3847         }
3848         fA = a;
3849         fB = b;
3850         fC = c;
3851         return true;
3852     }
3853 
3854     enum Result {
3855         kAllNegative,
3856         kAllPositive,
3857         kMixed
3858     };
testSkHalfPlane3859     Result test(const SkRect& bounds) const {
3860         // check whether the diagonal aligned with the normal crosses the plane
3861         SkPoint diagMin, diagMax;
3862         if (fA >= 0) {
3863             diagMin.fX = bounds.fLeft;
3864             diagMax.fX = bounds.fRight;
3865         } else {
3866             diagMin.fX = bounds.fRight;
3867             diagMax.fX = bounds.fLeft;
3868         }
3869         if (fB >= 0) {
3870             diagMin.fY = bounds.fTop;
3871             diagMax.fY = bounds.fBottom;
3872         } else {
3873             diagMin.fY = bounds.fBottom;
3874             diagMax.fY = bounds.fTop;
3875         }
3876         SkScalar test = this->eval(diagMin.fX, diagMin.fY);
3877         SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY);
3878         if (sign > 0) {
3879             // the path is either all on one side of the half-plane or the other
3880             if (test < 0) {
3881                 return kAllNegative;
3882             } else {
3883                 return kAllPositive;
3884             }
3885         }
3886         return kMixed;
3887     }
3888 };
3889 
3890 // assumes plane is pre-normalized
3891 // If we fail in our calculations, we return the empty path
clip(const SkPath & path,const SkHalfPlane & plane)3892 static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
3893     SkMatrix mx, inv;
3894     SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC };
3895     mx.setAll( plane.fB, plane.fA, p0.fX,
3896               -plane.fA, plane.fB, p0.fY,
3897                       0,        0,     1);
3898     if (!mx.invert(&inv)) {
3899         return SkPath();
3900     }
3901 
3902     SkPath rotated;
3903     path.transform(inv, &rotated);
3904     if (!rotated.isFinite()) {
3905         return SkPath();
3906     }
3907 
3908     SkScalar big = SK_ScalarMax;
3909     SkRect clip = {-big, 0, big, big };
3910 
3911     struct Rec {
3912         SkPathBuilder fResult;
3913         SkPoint       fPrev = {0,0};
3914     } rec;
3915 
3916     SkEdgeClipper::ClipPath(rotated, clip, false,
3917                             [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
3918         Rec* rec = (Rec*)ctx;
3919 
3920         bool addLineTo = false;
3921         SkPoint      pts[4];
3922         SkPath::Verb verb;
3923         while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
3924             if (newCtr) {
3925                 rec->fResult.moveTo(pts[0]);
3926                 rec->fPrev = pts[0];
3927                 newCtr = false;
3928             }
3929 
3930             if (addLineTo || pts[0] != rec->fPrev) {
3931                 rec->fResult.lineTo(pts[0]);
3932             }
3933 
3934             switch (verb) {
3935                 case SkPath::kLine_Verb:
3936                     rec->fResult.lineTo(pts[1]);
3937                     rec->fPrev = pts[1];
3938                     break;
3939                 case SkPath::kQuad_Verb:
3940                     rec->fResult.quadTo(pts[1], pts[2]);
3941                     rec->fPrev = pts[2];
3942                     break;
3943                 case SkPath::kCubic_Verb:
3944                     rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
3945                     rec->fPrev = pts[3];
3946                     break;
3947                 default: break;
3948             }
3949             addLineTo = true;
3950         }
3951     }, &rec);
3952 
3953     rec.fResult.setFillType(path.getFillType());
3954     SkPath result = rec.fResult.detach().makeTransform(mx);
3955     if (!result.isFinite()) {
3956         result = SkPath();
3957     }
3958     return result;
3959 }
3960 
3961 // true means we have written to clippedPath
PerspectiveClip(const SkPath & path,const SkMatrix & matrix,SkPath * clippedPath)3962 bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) {
3963     if (!matrix.hasPerspective()) {
3964         return false;
3965     }
3966 
3967     SkHalfPlane plane {
3968         matrix[SkMatrix::kMPersp0],
3969         matrix[SkMatrix::kMPersp1],
3970         matrix[SkMatrix::kMPersp2] - kW0PlaneDistance
3971     };
3972     if (plane.normalize()) {
3973         switch (plane.test(path.getBounds())) {
3974             case SkHalfPlane::kAllPositive:
3975                 return false;
3976             case SkHalfPlane::kMixed: {
3977                 *clippedPath = clip(path, plane);
3978                 return true;
3979             }
3980             default: break; // handled outside of the switch
3981         }
3982     }
3983     // clipped out (or failed)
3984     *clippedPath = SkPath();
3985     return true;
3986 }
3987 
GenIDChangeListenersCount(const SkPath & path)3988 int SkPathPriv::GenIDChangeListenersCount(const SkPath& path) {
3989     return path.fPathRef->genIDChangeListenerCount();
3990 }
3991 
IsAxisAligned(const SkPath & path)3992 bool SkPathPriv::IsAxisAligned(const SkPath& path) {
3993     // Conservative (quick) test to see if all segments are axis-aligned.
3994     // Multiple contours might give a false-negative, but for speed, we ignore that
3995     // and just look at the raw points.
3996 
3997     const SkPoint* pts = path.fPathRef->points();
3998     const int count = path.fPathRef->countPoints();
3999 
4000     for (int i = 1; i < count; ++i) {
4001         if (pts[i-1].fX != pts[i].fX && pts[i-1].fY != pts[i].fY) {
4002             return false;
4003         }
4004     }
4005     return true;
4006 }
4007 
4008 //////////////////////////////////////////////////////////////////////////////////////////////////
4009 
SkPathEdgeIter(const SkPath & path)4010 SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) {
4011     fMoveToPtr = fPts = path.fPathRef->points();
4012     fVerbs = path.fPathRef->verbsBegin();
4013     fVerbsStop = path.fPathRef->verbsEnd();
4014     fConicWeights = path.fPathRef->conicWeights();
4015     if (fConicWeights) {
4016         fConicWeights -= 1;  // begin one behind
4017     }
4018 
4019     fNeedsCloseLine = false;
4020     fNextIsNewContour = false;
4021     SkDEBUGCODE(fIsConic = false;)
4022 }
4023