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