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