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