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