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