• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/SkPathRef.h"
9 
10 #include "include/core/SkPath.h"
11 #include "include/private/SkNx.h"
12 #include "include/private/SkOnce.h"
13 #include "include/private/SkTo.h"
14 #include "src/core/SkBuffer.h"
15 #include "src/core/SkPathPriv.h"
16 #include "src/core/SkSafeMath.h"
17 
18 //////////////////////////////////////////////////////////////////////////////
Editor(sk_sp<SkPathRef> * pathRef,int incReserveVerbs,int incReservePoints)19 SkPathRef::Editor::Editor(sk_sp<SkPathRef>* pathRef,
20                           int incReserveVerbs,
21                           int incReservePoints)
22 {
23     SkASSERT(incReserveVerbs >= 0);
24     SkASSERT(incReservePoints >= 0);
25 
26     if ((*pathRef)->unique()) {
27         (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
28     } else {
29         SkPathRef* copy = new SkPathRef;
30         copy->copy(**pathRef, incReserveVerbs, incReservePoints);
31         pathRef->reset(copy);
32     }
33     fPathRef = pathRef->get();
34     fPathRef->callGenIDChangeListeners();
35     fPathRef->fGenerationID = 0;
36     fPathRef->fBoundsIsDirty = true;
37     SkDEBUGCODE(fPathRef->fEditorsAttached++;)
38 }
39 
40 // Sort of like makeSpace(0) but the the additional requirement that we actively shrink the
41 // allocations to just fit the current needs. makeSpace() will only grow, but never shrinks.
42 //
shrinkToFit()43 void SkPath::shrinkToFit() {
44     // Since this can relocate the allocated arrays, we have to defensively copy ourselves if
45     // we're not the only owner of the pathref... since relocating the arrays will invalidate
46     // any existing iterators.
47     if (!fPathRef->unique()) {
48         SkPathRef* pr = new SkPathRef;
49         pr->copy(*fPathRef, 0, 0);
50         fPathRef.reset(pr);
51     }
52     fPathRef->fPoints.shrinkToFit();
53     fPathRef->fVerbs.shrinkToFit();
54     fPathRef->fConicWeights.shrinkToFit();
55     SkDEBUGCODE(fPathRef->validate();)
56 }
57 
58 //////////////////////////////////////////////////////////////////////////////
59 
approximateBytesUsed() const60 size_t SkPathRef::approximateBytesUsed() const {
61     return sizeof(SkPathRef)
62          + fPoints      .reserved() * sizeof(fPoints      [0])
63          + fVerbs       .reserved() * sizeof(fVerbs       [0])
64          + fConicWeights.reserved() * sizeof(fConicWeights[0]);
65 }
66 
~SkPathRef()67 SkPathRef::~SkPathRef() {
68     // Deliberately don't validate() this path ref, otherwise there's no way
69     // to read one that's not valid and then free its memory without asserting.
70     SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;)
71     SkDEBUGCODE(fEditorsAttached.store(0x7777777);)
72 }
73 
74 static SkPathRef* gEmpty = nullptr;
75 
CreateEmpty()76 SkPathRef* SkPathRef::CreateEmpty() {
77     static SkOnce once;
78     once([]{
79         gEmpty = new SkPathRef;
80         gEmpty->computeBounds();   // Avoids races later to be the first to do this.
81     });
82     return SkRef(gEmpty);
83 }
84 
transform_dir_and_start(const SkMatrix & matrix,bool isRRect,bool * isCCW,unsigned * start)85 static void transform_dir_and_start(const SkMatrix& matrix, bool isRRect, bool* isCCW,
86                                     unsigned* start) {
87     int inStart = *start;
88     int rm = 0;
89     if (isRRect) {
90         // Degenerate rrect indices to oval indices and remember the remainder.
91         // Ovals have one index per side whereas rrects have two.
92         rm = inStart & 0b1;
93         inStart /= 2;
94     }
95     // Is the antidiagonal non-zero (otherwise the diagonal is zero)
96     int antiDiag;
97     // Is the non-zero value in the top row (either kMScaleX or kMSkewX) negative
98     int topNeg;
99     // Are the two non-zero diagonal or antidiagonal values the same sign.
100     int sameSign;
101     if (matrix.get(SkMatrix::kMScaleX) != 0) {
102         antiDiag = 0b00;
103         if (matrix.get(SkMatrix::kMScaleX) > 0) {
104             topNeg = 0b00;
105             sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b01 : 0b00;
106         } else {
107             topNeg = 0b10;
108             sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b00 : 0b01;
109         }
110     } else {
111         antiDiag = 0b01;
112         if (matrix.get(SkMatrix::kMSkewX) > 0) {
113             topNeg = 0b00;
114             sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b01 : 0b00;
115         } else {
116             topNeg = 0b10;
117             sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b00 : 0b01;
118         }
119     }
120     if (sameSign != antiDiag) {
121         // This is a rotation (and maybe scale). The direction is unchanged.
122         // Trust me on the start computation (or draw yourself some pictures)
123         *start = (inStart + 4 - (topNeg | antiDiag)) % 4;
124         SkASSERT(*start < 4);
125         if (isRRect) {
126             *start = 2 * *start + rm;
127         }
128     } else {
129         // This is a mirror (and maybe scale). The direction is reversed.
130         *isCCW = !*isCCW;
131         // Trust me on the start computation (or draw yourself some pictures)
132         *start = (6 + (topNeg | antiDiag) - inStart) % 4;
133         SkASSERT(*start < 4);
134         if (isRRect) {
135             *start = 2 * *start + (rm ? 0 : 1);
136         }
137     }
138 }
139 
CreateTransformedCopy(sk_sp<SkPathRef> * dst,const SkPathRef & src,const SkMatrix & matrix)140 void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst,
141                                       const SkPathRef& src,
142                                       const SkMatrix& matrix) {
143     SkDEBUGCODE(src.validate();)
144     if (matrix.isIdentity()) {
145         if (dst->get() != &src) {
146             src.ref();
147             dst->reset(const_cast<SkPathRef*>(&src));
148             SkDEBUGCODE((*dst)->validate();)
149         }
150         return;
151     }
152 
153     sk_sp<const SkPathRef> srcKeepAlive;
154     if (!(*dst)->unique()) {
155         // If dst and src are the same then we are about to drop our only ref on the common path
156         // ref. Some other thread may have owned src when we checked unique() above but it may not
157         // continue to do so. Add another ref so we continue to be an owner until we're done.
158         if (dst->get() == &src) {
159             srcKeepAlive.reset(SkRef(&src));
160         }
161         dst->reset(new SkPathRef);
162     }
163 
164     if (dst->get() != &src) {
165         (*dst)->fVerbs = src.fVerbs;
166         (*dst)->fConicWeights = src.fConicWeights;
167         (*dst)->callGenIDChangeListeners();
168         (*dst)->fGenerationID = 0;  // mark as dirty
169         // don't copy, just allocate the points
170         (*dst)->fPoints.setCount(src.fPoints.count());
171     }
172     matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.count());
173 
174     // Need to check this here in case (&src == dst)
175     bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1;
176 
177     /*
178      *  Here we optimize the bounds computation, by noting if the bounds are
179      *  already known, and if so, we just transform those as well and mark
180      *  them as "known", rather than force the transformed path to have to
181      *  recompute them.
182      *
183      *  Special gotchas if the path is effectively empty (<= 1 point) or
184      *  if it is non-finite. In those cases bounds need to stay empty,
185      *  regardless of the matrix.
186      */
187     if (canXformBounds) {
188         (*dst)->fBoundsIsDirty = false;
189         if (src.fIsFinite) {
190             matrix.mapRect(&(*dst)->fBounds, src.fBounds);
191             if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) {
192                 (*dst)->fBounds.setEmpty();
193             }
194         } else {
195             (*dst)->fIsFinite = false;
196             (*dst)->fBounds.setEmpty();
197         }
198     } else {
199         (*dst)->fBoundsIsDirty = true;
200     }
201 
202     (*dst)->fSegmentMask = src.fSegmentMask;
203 
204     // It's an oval only if it stays a rect.
205     bool rectStaysRect = matrix.rectStaysRect();
206     (*dst)->fIsOval = src.fIsOval && rectStaysRect;
207     (*dst)->fIsRRect = src.fIsRRect && rectStaysRect;
208     if ((*dst)->fIsOval || (*dst)->fIsRRect) {
209         unsigned start = src.fRRectOrOvalStartIdx;
210         bool isCCW = SkToBool(src.fRRectOrOvalIsCCW);
211         transform_dir_and_start(matrix, (*dst)->fIsRRect, &isCCW, &start);
212         (*dst)->fRRectOrOvalIsCCW = isCCW;
213         (*dst)->fRRectOrOvalStartIdx = start;
214     }
215 
216     if (dst->get() == &src) {
217         (*dst)->callGenIDChangeListeners();
218         (*dst)->fGenerationID = 0;
219     }
220 
221     SkDEBUGCODE((*dst)->validate();)
222 }
223 
Rewind(sk_sp<SkPathRef> * pathRef)224 void SkPathRef::Rewind(sk_sp<SkPathRef>* pathRef) {
225     if ((*pathRef)->unique()) {
226         SkDEBUGCODE((*pathRef)->validate();)
227         (*pathRef)->callGenIDChangeListeners();
228         (*pathRef)->fBoundsIsDirty = true;  // this also invalidates fIsFinite
229         (*pathRef)->fGenerationID = 0;
230         (*pathRef)->fPoints.rewind();
231         (*pathRef)->fVerbs.rewind();
232         (*pathRef)->fConicWeights.rewind();
233         (*pathRef)->fSegmentMask = 0;
234         (*pathRef)->fIsOval = false;
235         (*pathRef)->fIsRRect = false;
236         SkDEBUGCODE((*pathRef)->validate();)
237     } else {
238         int oldVCnt = (*pathRef)->countVerbs();
239         int oldPCnt = (*pathRef)->countPoints();
240         pathRef->reset(new SkPathRef);
241         (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
242     }
243 }
244 
operator ==(const SkPathRef & ref) const245 bool SkPathRef::operator== (const SkPathRef& ref) const {
246     SkDEBUGCODE(this->validate();)
247     SkDEBUGCODE(ref.validate();)
248 
249     // We explicitly check fSegmentMask as a quick-reject. We could skip it,
250     // since it is only a cache of info in the fVerbs, but its a fast way to
251     // notice a difference
252     if (fSegmentMask != ref.fSegmentMask) {
253         return false;
254     }
255 
256     bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
257 #ifdef SK_RELEASE
258     if (genIDMatch) {
259         return true;
260     }
261 #endif
262     if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) {
263         SkASSERT(!genIDMatch);
264         return false;
265     }
266     if (ref.fVerbs.count() == 0) {
267         SkASSERT(ref.fPoints.count() == 0);
268     }
269     return true;
270 }
271 
writeToBuffer(SkWBuffer * buffer) const272 void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
273     SkDEBUGCODE(this->validate();)
274     SkDEBUGCODE(size_t beforePos = buffer->pos();)
275 
276     // Call getBounds() to ensure (as a side-effect) that fBounds
277     // and fIsFinite are computed.
278     const SkRect& bounds = this->getBounds();
279 
280     // We store fSegmentMask for older readers, but current readers can't trust it, so they
281     // don't read it.
282     int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) |
283                      (fSegmentMask << kSegmentMask_SerializationShift);
284     buffer->write32(packed);
285 
286     // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
287     // SkWBuffer. Until this is fixed we write 0.
288     buffer->write32(0);
289     buffer->write32(fVerbs.count());
290     buffer->write32(fPoints.count());
291     buffer->write32(fConicWeights.count());
292     buffer->write(fVerbs.begin(), fVerbs.bytes());
293     buffer->write(fPoints.begin(), fVerbs.bytes());
294     buffer->write(fConicWeights.begin(), fConicWeights.bytes());
295     buffer->write(&bounds, sizeof(bounds));
296 
297     SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
298 }
299 
writeSize() const300 uint32_t SkPathRef::writeSize() const {
301     return uint32_t(5 * sizeof(uint32_t) +
302                     fVerbs.bytes() + fPoints.bytes() + fConicWeights.bytes() +
303                     sizeof(SkRect));
304 }
305 
copy(const SkPathRef & ref,int additionalReserveVerbs,int additionalReservePoints)306 void SkPathRef::copy(const SkPathRef& ref,
307                      int additionalReserveVerbs,
308                      int additionalReservePoints) {
309     SkDEBUGCODE(this->validate();)
310     this->resetToSize(ref.fVerbs.count(), ref.fPoints.count(), ref.fConicWeights.count(),
311                       additionalReserveVerbs, additionalReservePoints);
312     fVerbs = ref.fVerbs;
313     fPoints = ref.fPoints;
314     fConicWeights = ref.fConicWeights;
315     fBoundsIsDirty = ref.fBoundsIsDirty;
316     if (!fBoundsIsDirty) {
317         fBounds = ref.fBounds;
318         fIsFinite = ref.fIsFinite;
319     }
320     fSegmentMask = ref.fSegmentMask;
321     fIsOval = ref.fIsOval;
322     fIsRRect = ref.fIsRRect;
323     fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
324     fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
325     SkDEBUGCODE(this->validate();)
326 }
327 
interpolate(const SkPathRef & ending,SkScalar weight,SkPathRef * out) const328 void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
329     const SkScalar* inValues = &ending.getPoints()->fX;
330     SkScalar* outValues = &out->getWritablePoints()->fX;
331     int count = out->countPoints() * 2;
332     for (int index = 0; index < count; ++index) {
333         outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
334     }
335     out->fBoundsIsDirty = true;
336     out->fIsOval = false;
337     out->fIsRRect = false;
338 }
339 
growForVerbsInPath(const SkPathRef & path)340 std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) {
341     SkDEBUGCODE(this->validate();)
342 
343     fSegmentMask |= path.fSegmentMask;
344     fBoundsIsDirty = true;  // this also invalidates fIsFinite
345     fIsOval = false;
346     fIsRRect = false;
347 
348     if (int numVerbs = path.countVerbs()) {
349         memcpy(fVerbs.append(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0]));
350     }
351 
352     SkPoint* pts = nullptr;
353     if (int numPts = path.countPoints()) {
354         pts = fPoints.append(numPts);
355     }
356 
357     SkScalar* weights = nullptr;
358     if (int numConics = path.countWeights()) {
359         weights = fConicWeights.append(numConics);
360     }
361 
362     SkDEBUGCODE(this->validate();)
363     return {pts, weights};
364 }
365 
growForRepeatedVerb(int verb,int numVbs,SkScalar ** weights)366 SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
367                                         int numVbs,
368                                         SkScalar** weights) {
369     SkDEBUGCODE(this->validate();)
370     int pCnt;
371     switch (verb) {
372         case SkPath::kMove_Verb:
373             pCnt = numVbs;
374             break;
375         case SkPath::kLine_Verb:
376             fSegmentMask |= SkPath::kLine_SegmentMask;
377             pCnt = numVbs;
378             break;
379         case SkPath::kQuad_Verb:
380             fSegmentMask |= SkPath::kQuad_SegmentMask;
381             pCnt = 2 * numVbs;
382             break;
383         case SkPath::kConic_Verb:
384             fSegmentMask |= SkPath::kConic_SegmentMask;
385             pCnt = 2 * numVbs;
386             break;
387         case SkPath::kCubic_Verb:
388             fSegmentMask |= SkPath::kCubic_SegmentMask;
389             pCnt = 3 * numVbs;
390             break;
391         case SkPath::kClose_Verb:
392             SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb");
393             pCnt = 0;
394             break;
395         case SkPath::kDone_Verb:
396             SkDEBUGFAIL("growForRepeatedVerb called for kDone");
397             pCnt = 0;
398             break;
399         default:
400             SkDEBUGFAIL("default should not be reached");
401             pCnt = 0;
402             break;
403     }
404 
405     fBoundsIsDirty = true;  // this also invalidates fIsFinite
406     fIsOval = false;
407     fIsRRect = false;
408 
409     memset(fVerbs.append(numVbs), verb, numVbs);
410     if (SkPath::kConic_Verb == verb) {
411         SkASSERT(weights);
412         *weights = fConicWeights.append(numVbs);
413     }
414     SkPoint* pts = fPoints.append(pCnt);
415 
416     SkDEBUGCODE(this->validate();)
417     return pts;
418 }
419 
growForVerb(int verb,SkScalar weight)420 SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
421     SkDEBUGCODE(this->validate();)
422     int pCnt;
423     unsigned mask = 0;
424     switch (verb) {
425         case SkPath::kMove_Verb:
426             pCnt = 1;
427             break;
428         case SkPath::kLine_Verb:
429             mask = SkPath::kLine_SegmentMask;
430             pCnt = 1;
431             break;
432         case SkPath::kQuad_Verb:
433             mask = SkPath::kQuad_SegmentMask;
434             pCnt = 2;
435             break;
436         case SkPath::kConic_Verb:
437             mask = SkPath::kConic_SegmentMask;
438             pCnt = 2;
439             break;
440         case SkPath::kCubic_Verb:
441             mask = SkPath::kCubic_SegmentMask;
442             pCnt = 3;
443             break;
444         case SkPath::kClose_Verb:
445             pCnt = 0;
446             break;
447         case SkPath::kDone_Verb:
448             SkDEBUGFAIL("growForVerb called for kDone");
449             pCnt = 0;
450             break;
451         default:
452             SkDEBUGFAIL("default is not reached");
453             pCnt = 0;
454             break;
455     }
456 
457     fSegmentMask |= mask;
458     fBoundsIsDirty = true;  // this also invalidates fIsFinite
459     fIsOval = false;
460     fIsRRect = false;
461 
462     *fVerbs.append() = verb;
463     if (SkPath::kConic_Verb == verb) {
464         *fConicWeights.append() = weight;
465     }
466     SkPoint* pts = fPoints.append(pCnt);
467 
468     SkDEBUGCODE(this->validate();)
469     return pts;
470 }
471 
genID() const472 uint32_t SkPathRef::genID() const {
473     SkASSERT(fEditorsAttached.load() == 0);
474     static const uint32_t kMask = (static_cast<int64_t>(1) << SkPathPriv::kPathRefGenIDBitCnt) - 1;
475 
476     if (fGenerationID == 0) {
477         if (fPoints.count() == 0 && fVerbs.count() == 0) {
478             fGenerationID = kEmptyGenID;
479         } else {
480             static std::atomic<uint32_t> nextID{kEmptyGenID + 1};
481             do {
482                 fGenerationID = nextID.fetch_add(1, std::memory_order_relaxed) & kMask;
483             } while (fGenerationID == 0 || fGenerationID == kEmptyGenID);
484         }
485     }
486     return fGenerationID;
487 }
488 
addGenIDChangeListener(sk_sp<SkIDChangeListener> listener)489 void SkPathRef::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) {
490     if (this == gEmpty) {
491         return;
492     }
493     fGenIDChangeListeners.add(std::move(listener));
494 }
495 
genIDChangeListenerCount()496 int SkPathRef::genIDChangeListenerCount() { return fGenIDChangeListeners.count(); }
497 
498 // we need to be called *before* the genID gets changed or zerod
callGenIDChangeListeners()499 void SkPathRef::callGenIDChangeListeners() {
500     fGenIDChangeListeners.changed();
501 }
502 
getRRect() const503 SkRRect SkPathRef::getRRect() const {
504     const SkRect& bounds = this->getBounds();
505     SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
506     Iter iter(*this);
507     SkPoint pts[4];
508     uint8_t verb = iter.next(pts);
509     SkASSERT(SkPath::kMove_Verb == verb);
510     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
511         if (SkPath::kConic_Verb == verb) {
512             SkVector v1_0 = pts[1] - pts[0];
513             SkVector v2_1 = pts[2] - pts[1];
514             SkVector dxdy;
515             if (v1_0.fX) {
516                 SkASSERT(!v2_1.fX && !v1_0.fY);
517                 dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY));
518             } else if (!v1_0.fY) {
519                 SkASSERT(!v2_1.fX || !v2_1.fY);
520                 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY));
521             } else {
522                 SkASSERT(!v2_1.fY);
523                 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY));
524             }
525             SkRRect::Corner corner =
526                     pts[1].fX == bounds.fLeft ?
527                         pts[1].fY == bounds.fTop ?
528                             SkRRect::kUpperLeft_Corner : SkRRect::kLowerLeft_Corner :
529                     pts[1].fY == bounds.fTop ?
530                             SkRRect::kUpperRight_Corner : SkRRect::kLowerRight_Corner;
531             SkASSERT(!radii[corner].fX && !radii[corner].fY);
532             radii[corner] = dxdy;
533         } else {
534             SkASSERT((verb == SkPath::kLine_Verb
535                     && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY)))
536                     || verb == SkPath::kClose_Verb);
537         }
538     }
539     SkRRect rrect;
540     rrect.setRectRadii(bounds, radii);
541     return rrect;
542 }
543 
544 ///////////////////////////////////////////////////////////////////////////////
545 
Iter()546 SkPathRef::Iter::Iter() {
547 #ifdef SK_DEBUG
548     fPts = nullptr;
549     fConicWeights = nullptr;
550 #endif
551     // need to init enough to make next() harmlessly return kDone_Verb
552     fVerbs = nullptr;
553     fVerbStop = nullptr;
554 }
555 
Iter(const SkPathRef & path)556 SkPathRef::Iter::Iter(const SkPathRef& path) {
557     this->setPathRef(path);
558 }
559 
setPathRef(const SkPathRef & path)560 void SkPathRef::Iter::setPathRef(const SkPathRef& path) {
561     fPts = path.points();
562     fVerbs = path.verbsBegin();
563     fVerbStop = path.verbsEnd();
564     fConicWeights = path.conicWeights();
565     if (fConicWeights) {
566         fConicWeights -= 1;  // begin one behind
567     }
568 
569     // Don't allow iteration through non-finite points.
570     if (!path.isFinite()) {
571         fVerbStop = fVerbs;
572     }
573 }
574 
next(SkPoint pts[4])575 uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
576     SkASSERT(pts);
577 
578     SkDEBUGCODE(unsigned peekResult = this->peek();)
579 
580     if (fVerbs == fVerbStop) {
581         SkASSERT(peekResult == SkPath::kDone_Verb);
582         return (uint8_t) SkPath::kDone_Verb;
583     }
584 
585     // fVerbs points one beyond next verb so decrement first.
586     unsigned verb = *fVerbs++;
587     const SkPoint* srcPts = fPts;
588 
589     switch (verb) {
590         case SkPath::kMove_Verb:
591             pts[0] = srcPts[0];
592             srcPts += 1;
593             break;
594         case SkPath::kLine_Verb:
595             pts[0] = srcPts[-1];
596             pts[1] = srcPts[0];
597             srcPts += 1;
598             break;
599         case SkPath::kConic_Verb:
600             fConicWeights += 1;
601             [[fallthrough]];
602         case SkPath::kQuad_Verb:
603             pts[0] = srcPts[-1];
604             pts[1] = srcPts[0];
605             pts[2] = srcPts[1];
606             srcPts += 2;
607             break;
608         case SkPath::kCubic_Verb:
609             pts[0] = srcPts[-1];
610             pts[1] = srcPts[0];
611             pts[2] = srcPts[1];
612             pts[3] = srcPts[2];
613             srcPts += 3;
614             break;
615         case SkPath::kClose_Verb:
616             break;
617         case SkPath::kDone_Verb:
618             SkASSERT(fVerbs == fVerbStop);
619             break;
620     }
621     fPts = srcPts;
622     SkASSERT(peekResult == verb);
623     return (uint8_t) verb;
624 }
625 
peek() const626 uint8_t SkPathRef::Iter::peek() const {
627     return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb;
628 }
629 
630 
isValid() const631 bool SkPathRef::isValid() const {
632     if (fIsOval || fIsRRect) {
633         // Currently we don't allow both of these to be set, even though ovals are ro
634         if (fIsOval == fIsRRect) {
635             return false;
636         }
637         if (fIsOval) {
638             if (fRRectOrOvalStartIdx >= 4) {
639                 return false;
640             }
641         } else {
642             if (fRRectOrOvalStartIdx >= 8) {
643                 return false;
644             }
645         }
646     }
647 
648     if (!fBoundsIsDirty && !fBounds.isEmpty()) {
649         bool isFinite = true;
650         Sk2s leftTop = Sk2s(fBounds.fLeft, fBounds.fTop);
651         Sk2s rightBot = Sk2s(fBounds.fRight, fBounds.fBottom);
652         for (int i = 0; i < fPoints.count(); ++i) {
653             Sk2s point = Sk2s(fPoints[i].fX, fPoints[i].fY);
654 #ifdef SK_DEBUG
655             if (fPoints[i].isFinite() &&
656                 ((point < leftTop).anyTrue() || (point > rightBot).anyTrue())) {
657                 SkDebugf("bad SkPathRef bounds: %g %g %g %g\n",
658                          fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
659                 for (int j = 0; j < fPoints.count(); ++j) {
660                     if (i == j) {
661                         SkDebugf("*** bounds do not contain: ");
662                     }
663                     SkDebugf("%g %g\n", fPoints[j].fX, fPoints[j].fY);
664                 }
665                 return false;
666             }
667 #endif
668 
669             if (fPoints[i].isFinite() && (point < leftTop).anyTrue() && !(point > rightBot).anyTrue())
670                 return false;
671             if (!fPoints[i].isFinite()) {
672                 isFinite = false;
673             }
674         }
675         if (SkToBool(fIsFinite) != isFinite) {
676             return false;
677         }
678     }
679     return true;
680 }
681 
dataMatchesVerbs() const682 bool SkPathRef::dataMatchesVerbs() const {
683     const auto info = sk_path_analyze_verbs(fVerbs.begin(), fVerbs.count());
684     return info.valid                          &&
685            info.segmentMask == fSegmentMask    &&
686            info.points      == fPoints.count() &&
687            info.weights     == fConicWeights.count();
688 }
689 //////////////////////////////////////////////////////////////////////////////////////////////////
690 
SkPathEdgeIter(const SkPath & path)691 SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) {
692     fMoveToPtr = fPts = path.fPathRef->points();
693     fVerbs = path.fPathRef->verbsBegin();
694     fVerbsStop = path.fPathRef->verbsEnd();
695     fConicWeights = path.fPathRef->conicWeights();
696     if (fConicWeights) {
697         fConicWeights -= 1;  // begin one behind
698     }
699 
700     fNeedsCloseLine = false;
701     fNextIsNewContour = false;
702     SkDEBUGCODE(fIsConic = false;)
703 }
704