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