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