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