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