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