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 "SkBuffer.h"
9 #include "SkOncePtr.h"
10 #include "SkPath.h"
11 #include "SkPathRef.h"
12 #include <limits>
13 
14 //////////////////////////////////////////////////////////////////////////////
Editor(SkAutoTUnref<SkPathRef> * pathRef,int incReserveVerbs,int incReservePoints)15 SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef,
16                           int incReserveVerbs,
17                           int incReservePoints)
18 {
19     if ((*pathRef)->unique()) {
20         (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
21     } else {
22         SkPathRef* copy = new SkPathRef;
23         copy->copy(**pathRef, incReserveVerbs, incReservePoints);
24         pathRef->reset(copy);
25     }
26     fPathRef = *pathRef;
27     fPathRef->callGenIDChangeListeners();
28     fPathRef->fGenerationID = 0;
29     SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);)
30 }
31 
32 //////////////////////////////////////////////////////////////////////////////
33 
~SkPathRef()34 SkPathRef::~SkPathRef() {
35     this->callGenIDChangeListeners();
36     SkDEBUGCODE(this->validate();)
37     sk_free(fPoints);
38 
39     SkDEBUGCODE(fPoints = nullptr;)
40     SkDEBUGCODE(fVerbs = nullptr;)
41     SkDEBUGCODE(fVerbCnt = 0x9999999;)
42     SkDEBUGCODE(fPointCnt = 0xAAAAAAA;)
43     SkDEBUGCODE(fPointCnt = 0xBBBBBBB;)
44     SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;)
45     SkDEBUGCODE(fEditorsAttached = 0x7777777;)
46 }
47 
48 SK_DECLARE_STATIC_ONCE_PTR(SkPathRef, empty);
CreateEmpty()49 SkPathRef* SkPathRef::CreateEmpty() {
50     return SkRef(empty.get([]{
51         SkPathRef* pr = new SkPathRef;
52         pr->computeBounds();   // Avoids races later to be the first to do this.
53         return pr;
54     }));
55 }
56 
CreateTransformedCopy(SkAutoTUnref<SkPathRef> * dst,const SkPathRef & src,const SkMatrix & matrix)57 void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
58                                       const SkPathRef& src,
59                                       const SkMatrix& matrix) {
60     SkDEBUGCODE(src.validate();)
61     if (matrix.isIdentity()) {
62         if (*dst != &src) {
63             src.ref();
64             dst->reset(const_cast<SkPathRef*>(&src));
65             SkDEBUGCODE((*dst)->validate();)
66         }
67         return;
68     }
69 
70     if (!(*dst)->unique()) {
71         dst->reset(new SkPathRef);
72     }
73 
74     if (*dst != &src) {
75         (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count());
76         sk_careful_memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(),
77                            src.fVerbCnt * sizeof(uint8_t));
78         (*dst)->fConicWeights = src.fConicWeights;
79     }
80 
81     SkASSERT((*dst)->countPoints() == src.countPoints());
82     SkASSERT((*dst)->countVerbs() == src.countVerbs());
83     SkASSERT((*dst)->fConicWeights.count() == src.fConicWeights.count());
84 
85     // Need to check this here in case (&src == dst)
86     bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1;
87 
88     matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
89 
90     /*
91         *  Here we optimize the bounds computation, by noting if the bounds are
92         *  already known, and if so, we just transform those as well and mark
93         *  them as "known", rather than force the transformed path to have to
94         *  recompute them.
95         *
96         *  Special gotchas if the path is effectively empty (<= 1 point) or
97         *  if it is non-finite. In those cases bounds need to stay empty,
98         *  regardless of the matrix.
99         */
100     if (canXformBounds) {
101         (*dst)->fBoundsIsDirty = false;
102         if (src.fIsFinite) {
103             matrix.mapRect(&(*dst)->fBounds, src.fBounds);
104             if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) {
105                 (*dst)->fBounds.setEmpty();
106             }
107         } else {
108             (*dst)->fIsFinite = false;
109             (*dst)->fBounds.setEmpty();
110         }
111     } else {
112         (*dst)->fBoundsIsDirty = true;
113     }
114 
115     (*dst)->fSegmentMask = src.fSegmentMask;
116 
117     // It's an oval only if it stays a rect.
118     bool rectStaysRect = matrix.rectStaysRect();
119     (*dst)->fIsOval = src.fIsOval && rectStaysRect;
120     (*dst)->fIsRRect = src.fIsRRect && rectStaysRect;
121 
122     SkDEBUGCODE((*dst)->validate();)
123 }
124 
CreateFromBuffer(SkRBuffer * buffer)125 SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
126     SkPathRef* ref = new SkPathRef;
127 
128     int32_t packed;
129     if (!buffer->readS32(&packed)) {
130         delete ref;
131         return nullptr;
132     }
133 
134     ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
135     uint8_t segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
136     bool isOval  = (packed >> kIsOval_SerializationShift) & 1;
137     bool isRRect  = (packed >> kIsRRect_SerializationShift) & 1;
138 
139     int32_t verbCount, pointCount, conicCount;
140     ptrdiff_t maxPtrDiff = std::numeric_limits<ptrdiff_t>::max();
141     if (!buffer->readU32(&(ref->fGenerationID)) ||
142         !buffer->readS32(&verbCount) ||
143         verbCount < 0 ||
144         static_cast<uint32_t>(verbCount) > maxPtrDiff/sizeof(uint8_t) ||
145         !buffer->readS32(&pointCount) ||
146         pointCount < 0 ||
147         static_cast<uint32_t>(pointCount) > maxPtrDiff/sizeof(SkPoint) ||
148         sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount >
149             static_cast<size_t>(maxPtrDiff) ||
150         !buffer->readS32(&conicCount) ||
151         conicCount < 0) {
152         delete ref;
153         return nullptr;
154     }
155 
156     ref->resetToSize(verbCount, pointCount, conicCount);
157     SkASSERT(verbCount == ref->countVerbs());
158     SkASSERT(pointCount == ref->countPoints());
159     SkASSERT(conicCount == ref->fConicWeights.count());
160 
161     if (!buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)) ||
162         !buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)) ||
163         !buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)) ||
164         !buffer->read(&ref->fBounds, sizeof(SkRect))) {
165         delete ref;
166         return nullptr;
167     }
168     ref->fBoundsIsDirty = false;
169 
170     // resetToSize clears fSegmentMask and fIsOval
171     ref->fSegmentMask = segmentMask;
172     ref->fIsOval = isOval;
173     ref->fIsRRect = isRRect;
174     return ref;
175 }
176 
Rewind(SkAutoTUnref<SkPathRef> * pathRef)177 void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) {
178     if ((*pathRef)->unique()) {
179         SkDEBUGCODE((*pathRef)->validate();)
180         (*pathRef)->callGenIDChangeListeners();
181         (*pathRef)->fBoundsIsDirty = true;  // this also invalidates fIsFinite
182         (*pathRef)->fVerbCnt = 0;
183         (*pathRef)->fPointCnt = 0;
184         (*pathRef)->fFreeSpace = (*pathRef)->currSize();
185         (*pathRef)->fGenerationID = 0;
186         (*pathRef)->fConicWeights.rewind();
187         (*pathRef)->fSegmentMask = 0;
188         (*pathRef)->fIsOval = false;
189         (*pathRef)->fIsRRect = false;
190         SkDEBUGCODE((*pathRef)->validate();)
191     } else {
192         int oldVCnt = (*pathRef)->countVerbs();
193         int oldPCnt = (*pathRef)->countPoints();
194         pathRef->reset(new SkPathRef);
195         (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
196     }
197 }
198 
operator ==(const SkPathRef & ref) const199 bool SkPathRef::operator== (const SkPathRef& ref) const {
200     SkDEBUGCODE(this->validate();)
201     SkDEBUGCODE(ref.validate();)
202 
203     // We explicitly check fSegmentMask as a quick-reject. We could skip it,
204     // since it is only a cache of info in the fVerbs, but its a fast way to
205     // notice a difference
206     if (fSegmentMask != ref.fSegmentMask) {
207         return false;
208     }
209 
210     bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
211 #ifdef SK_RELEASE
212     if (genIDMatch) {
213         return true;
214     }
215 #endif
216     if (fPointCnt != ref.fPointCnt ||
217         fVerbCnt != ref.fVerbCnt) {
218         SkASSERT(!genIDMatch);
219         return false;
220     }
221     if (0 == ref.fVerbCnt) {
222         SkASSERT(0 == ref.fPointCnt);
223         return true;
224     }
225     SkASSERT(this->verbsMemBegin() && ref.verbsMemBegin());
226     if (0 != memcmp(this->verbsMemBegin(),
227                     ref.verbsMemBegin(),
228                     ref.fVerbCnt * sizeof(uint8_t))) {
229         SkASSERT(!genIDMatch);
230         return false;
231     }
232     SkASSERT(this->points() && ref.points());
233     if (0 != memcmp(this->points(),
234                     ref.points(),
235                     ref.fPointCnt * sizeof(SkPoint))) {
236         SkASSERT(!genIDMatch);
237         return false;
238     }
239     if (fConicWeights != ref.fConicWeights) {
240         SkASSERT(!genIDMatch);
241         return false;
242     }
243     return true;
244 }
245 
writeToBuffer(SkWBuffer * buffer) const246 void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
247     SkDEBUGCODE(this->validate();)
248     SkDEBUGCODE(size_t beforePos = buffer->pos();)
249 
250     // Call getBounds() to ensure (as a side-effect) that fBounds
251     // and fIsFinite are computed.
252     const SkRect& bounds = this->getBounds();
253 
254     int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) |
255                      ((fIsOval & 1) << kIsOval_SerializationShift) |
256                      ((fIsRRect & 1) << kIsRRect_SerializationShift) |
257                      (fSegmentMask << kSegmentMask_SerializationShift);
258     buffer->write32(packed);
259 
260     // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
261     // SkWBuffer. Until this is fixed we write 0.
262     buffer->write32(0);
263     buffer->write32(fVerbCnt);
264     buffer->write32(fPointCnt);
265     buffer->write32(fConicWeights.count());
266     buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
267     buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
268     buffer->write(fConicWeights.begin(), fConicWeights.bytes());
269     buffer->write(&bounds, sizeof(bounds));
270 
271     SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
272 }
273 
writeSize() const274 uint32_t SkPathRef::writeSize() const {
275     return uint32_t(5 * sizeof(uint32_t) +
276                     fVerbCnt * sizeof(uint8_t) +
277                     fPointCnt * sizeof(SkPoint) +
278                     fConicWeights.bytes() +
279                     sizeof(SkRect));
280 }
281 
copy(const SkPathRef & ref,int additionalReserveVerbs,int additionalReservePoints)282 void SkPathRef::copy(const SkPathRef& ref,
283                      int additionalReserveVerbs,
284                      int additionalReservePoints) {
285     SkDEBUGCODE(this->validate();)
286     this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(),
287                         additionalReserveVerbs, additionalReservePoints);
288     sk_careful_memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt*sizeof(uint8_t));
289     sk_careful_memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
290     fConicWeights = ref.fConicWeights;
291     fBoundsIsDirty = ref.fBoundsIsDirty;
292     if (!fBoundsIsDirty) {
293         fBounds = ref.fBounds;
294         fIsFinite = ref.fIsFinite;
295     }
296     fSegmentMask = ref.fSegmentMask;
297     fIsOval = ref.fIsOval;
298     fIsRRect = ref.fIsRRect;
299     SkDEBUGCODE(this->validate();)
300 }
301 
302 
interpolate(const SkPathRef & ending,SkScalar weight,SkPathRef * out) const303 void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
304     const SkScalar* inValues = &ending.getPoints()->fX;
305     SkScalar* outValues = &out->getPoints()->fX;
306     int count = out->countPoints() * 2;
307     for (int index = 0; index < count; ++index) {
308         outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
309     }
310     out->fBoundsIsDirty = true;
311     out->fIsOval = false;
312     out->fIsRRect = false;
313 }
314 
growForRepeatedVerb(int verb,int numVbs,SkScalar ** weights)315 SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
316                                         int numVbs,
317                                         SkScalar** weights) {
318     // This value is just made-up for now. When count is 4, calling memset was much
319     // slower than just writing the loop. This seems odd, and hopefully in the
320     // future this will appear to have been a fluke...
321     static const unsigned int kMIN_COUNT_FOR_MEMSET_TO_BE_FAST = 16;
322 
323     SkDEBUGCODE(this->validate();)
324     int pCnt;
325     bool dirtyAfterEdit = true;
326     switch (verb) {
327         case SkPath::kMove_Verb:
328             pCnt = numVbs;
329             dirtyAfterEdit = false;
330             break;
331         case SkPath::kLine_Verb:
332             fSegmentMask |= SkPath::kLine_SegmentMask;
333             pCnt = numVbs;
334             break;
335         case SkPath::kQuad_Verb:
336             fSegmentMask |= SkPath::kQuad_SegmentMask;
337             pCnt = 2 * numVbs;
338             break;
339         case SkPath::kConic_Verb:
340             fSegmentMask |= SkPath::kConic_SegmentMask;
341             pCnt = 2 * numVbs;
342             break;
343         case SkPath::kCubic_Verb:
344             fSegmentMask |= SkPath::kCubic_SegmentMask;
345             pCnt = 3 * numVbs;
346             break;
347         case SkPath::kClose_Verb:
348             SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb");
349             pCnt = 0;
350             dirtyAfterEdit = false;
351             break;
352         case SkPath::kDone_Verb:
353             SkDEBUGFAIL("growForRepeatedVerb called for kDone");
354             // fall through
355         default:
356             SkDEBUGFAIL("default should not be reached");
357             pCnt = 0;
358             dirtyAfterEdit = false;
359     }
360 
361     size_t space = numVbs * sizeof(uint8_t) + pCnt * sizeof (SkPoint);
362     this->makeSpace(space);
363 
364     SkPoint* ret = fPoints + fPointCnt;
365     uint8_t* vb = fVerbs - fVerbCnt;
366 
367     // cast to unsigned, so if kMIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
368     // be 0, the compiler will remove the test/branch entirely.
369     if ((unsigned)numVbs >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
370         memset(vb - numVbs, verb, numVbs);
371     } else {
372         for (int i = 0; i < numVbs; ++i) {
373             vb[~i] = verb;
374         }
375     }
376 
377     fVerbCnt += numVbs;
378     fPointCnt += pCnt;
379     fFreeSpace -= space;
380     fBoundsIsDirty = true;  // this also invalidates fIsFinite
381     if (dirtyAfterEdit) {
382         fIsOval = false;
383         fIsRRect = false;
384     }
385 
386     if (SkPath::kConic_Verb == verb) {
387         SkASSERT(weights);
388         *weights = fConicWeights.append(numVbs);
389     }
390 
391     SkDEBUGCODE(this->validate();)
392     return ret;
393 }
394 
growForVerb(int verb,SkScalar weight)395 SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
396     SkDEBUGCODE(this->validate();)
397     int pCnt;
398     bool dirtyAfterEdit = true;
399     switch (verb) {
400         case SkPath::kMove_Verb:
401             pCnt = 1;
402             dirtyAfterEdit = false;
403             break;
404         case SkPath::kLine_Verb:
405             fSegmentMask |= SkPath::kLine_SegmentMask;
406             pCnt = 1;
407             break;
408         case SkPath::kQuad_Verb:
409             fSegmentMask |= SkPath::kQuad_SegmentMask;
410             pCnt = 2;
411             break;
412         case SkPath::kConic_Verb:
413             fSegmentMask |= SkPath::kConic_SegmentMask;
414             pCnt = 2;
415             break;
416         case SkPath::kCubic_Verb:
417             fSegmentMask |= SkPath::kCubic_SegmentMask;
418             pCnt = 3;
419             break;
420         case SkPath::kClose_Verb:
421             pCnt = 0;
422             dirtyAfterEdit = false;
423             break;
424         case SkPath::kDone_Verb:
425             SkDEBUGFAIL("growForVerb called for kDone");
426             // fall through
427         default:
428             SkDEBUGFAIL("default is not reached");
429             dirtyAfterEdit = false;
430             pCnt = 0;
431     }
432     size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
433     this->makeSpace(space);
434     this->fVerbs[~fVerbCnt] = verb;
435     SkPoint* ret = fPoints + fPointCnt;
436     fVerbCnt += 1;
437     fPointCnt += pCnt;
438     fFreeSpace -= space;
439     fBoundsIsDirty = true;  // this also invalidates fIsFinite
440     if (dirtyAfterEdit) {
441         fIsOval = false;
442         fIsRRect = false;
443     }
444 
445     if (SkPath::kConic_Verb == verb) {
446         *fConicWeights.append() = weight;
447     }
448 
449     SkDEBUGCODE(this->validate();)
450     return ret;
451 }
452 
genID() const453 uint32_t SkPathRef::genID() const {
454     SkASSERT(!fEditorsAttached);
455     static const uint32_t kMask = (static_cast<int64_t>(1) << SkPath::kPathRefGenIDBitCnt) - 1;
456     if (!fGenerationID) {
457         if (0 == fPointCnt && 0 == fVerbCnt) {
458             fGenerationID = kEmptyGenID;
459         } else {
460             static int32_t  gPathRefGenerationID;
461             // do a loop in case our global wraps around, as we never want to return a 0 or the
462             // empty ID
463             do {
464                 fGenerationID = (sk_atomic_inc(&gPathRefGenerationID) + 1) & kMask;
465             } while (fGenerationID <= kEmptyGenID);
466         }
467     }
468     return fGenerationID;
469 }
470 
addGenIDChangeListener(GenIDChangeListener * listener)471 void SkPathRef::addGenIDChangeListener(GenIDChangeListener* listener) {
472     if (nullptr == listener || this == (SkPathRef*)empty) {
473         delete listener;
474         return;
475     }
476     *fGenIDChangeListeners.append() = listener;
477 }
478 
479 // we need to be called *before* the genID gets changed or zerod
callGenIDChangeListeners()480 void SkPathRef::callGenIDChangeListeners() {
481     for (int i = 0; i < fGenIDChangeListeners.count(); i++) {
482         fGenIDChangeListeners[i]->onChange();
483     }
484 
485     // Listeners get at most one shot, so whether these triggered or not, blow them away.
486     fGenIDChangeListeners.deleteAll();
487 }
488 
getRRect() const489 SkRRect SkPathRef::getRRect() const {
490     const SkRect& bounds = this->getBounds();
491     SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
492     Iter iter(*this);
493     SkPoint pts[4];
494     uint8_t verb = iter.next(pts);
495     SkASSERT(SkPath::kMove_Verb == verb);
496     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
497         if (SkPath::kConic_Verb == verb) {
498             SkVector v1_0 = pts[1] - pts[0];
499             SkVector v2_1 = pts[2] - pts[1];
500             SkVector dxdy;
501             if (v1_0.fX) {
502                 SkASSERT(!v2_1.fX && !v1_0.fY);
503                 dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY));
504             } else if (!v1_0.fY) {
505                 SkASSERT(!v2_1.fX || !v2_1.fY);
506                 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY));
507             } else {
508                 SkASSERT(!v2_1.fY);
509                 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY));
510             }
511             SkRRect::Corner corner =
512                     pts[1].fX == bounds.fLeft ?
513                         pts[1].fY == bounds.fTop ?
514                             SkRRect::kUpperLeft_Corner : SkRRect::kLowerLeft_Corner :
515                     pts[1].fY == bounds.fTop ?
516                             SkRRect::kUpperRight_Corner : SkRRect::kLowerRight_Corner;
517             SkASSERT(!radii[corner].fX && !radii[corner].fY);
518             radii[corner] = dxdy;
519         } else {
520             SkASSERT((verb == SkPath::kLine_Verb
521                     && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY)))
522                     || verb == SkPath::kClose_Verb);
523         }
524     }
525     SkRRect rrect;
526     rrect.setRectRadii(bounds, radii);
527     return rrect;
528 }
529 
530 ///////////////////////////////////////////////////////////////////////////////
531 
Iter()532 SkPathRef::Iter::Iter() {
533 #ifdef SK_DEBUG
534     fPts = nullptr;
535     fConicWeights = nullptr;
536 #endif
537     // need to init enough to make next() harmlessly return kDone_Verb
538     fVerbs = nullptr;
539     fVerbStop = nullptr;
540 }
541 
Iter(const SkPathRef & path)542 SkPathRef::Iter::Iter(const SkPathRef& path) {
543     this->setPathRef(path);
544 }
545 
setPathRef(const SkPathRef & path)546 void SkPathRef::Iter::setPathRef(const SkPathRef& path) {
547     fPts = path.points();
548     fVerbs = path.verbs();
549     fVerbStop = path.verbsMemBegin();
550     fConicWeights = path.conicWeights() - 1; // begin one behind
551 }
552 
next(SkPoint pts[4])553 uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
554     SkASSERT(pts);
555     if (fVerbs == fVerbStop) {
556         return (uint8_t) SkPath::kDone_Verb;
557     }
558 
559     // fVerbs points one beyond next verb so decrement first.
560     unsigned verb = *(--fVerbs);
561     const SkPoint* srcPts = fPts;
562 
563     switch (verb) {
564         case SkPath::kMove_Verb:
565             pts[0] = srcPts[0];
566             srcPts += 1;
567             break;
568         case SkPath::kLine_Verb:
569             pts[0] = srcPts[-1];
570             pts[1] = srcPts[0];
571             srcPts += 1;
572             break;
573         case SkPath::kConic_Verb:
574             fConicWeights += 1;
575             // fall-through
576         case SkPath::kQuad_Verb:
577             pts[0] = srcPts[-1];
578             pts[1] = srcPts[0];
579             pts[2] = srcPts[1];
580             srcPts += 2;
581             break;
582         case SkPath::kCubic_Verb:
583             pts[0] = srcPts[-1];
584             pts[1] = srcPts[0];
585             pts[2] = srcPts[1];
586             pts[3] = srcPts[2];
587             srcPts += 3;
588             break;
589         case SkPath::kClose_Verb:
590             break;
591         case SkPath::kDone_Verb:
592             SkASSERT(fVerbs == fVerbStop);
593             break;
594     }
595     fPts = srcPts;
596     return (uint8_t) verb;
597 }
598 
peek() const599 uint8_t SkPathRef::Iter::peek() const {
600     const uint8_t* next = fVerbs - 1;
601     return next <= fVerbStop ? (uint8_t) SkPath::kDone_Verb : *next;
602 }
603 
604 #ifdef SK_DEBUG
validate() const605 void SkPathRef::validate() const {
606     this->INHERITED::validate();
607     SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0);
608     SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0);
609     SkASSERT((nullptr == fPoints) == (nullptr == fVerbs));
610     SkASSERT(!(nullptr == fPoints && 0 != fFreeSpace));
611     SkASSERT(!(nullptr == fPoints && 0 != fFreeSpace));
612     SkASSERT(!(nullptr == fPoints && fPointCnt));
613     SkASSERT(!(nullptr == fVerbs && fVerbCnt));
614     SkASSERT(this->currSize() ==
615                 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
616 
617     if (!fBoundsIsDirty && !fBounds.isEmpty()) {
618         bool isFinite = true;
619         for (int i = 0; i < fPointCnt; ++i) {
620 #ifdef SK_DEBUG
621             if (fPoints[i].isFinite() &&
622                 (fPoints[i].fX < fBounds.fLeft || fPoints[i].fX > fBounds.fRight ||
623                  fPoints[i].fY < fBounds.fTop || fPoints[i].fY > fBounds.fBottom)) {
624                 SkDebugf("bounds: %f %f %f %f\n",
625                          fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
626                 for (int j = 0; j < fPointCnt; ++j) {
627                     if (i == j) {
628                         SkDebugf("*");
629                     }
630                     SkDebugf("%f %f\n", fPoints[j].fX, fPoints[j].fY);
631                 }
632             }
633 #endif
634 
635             SkASSERT(!fPoints[i].isFinite() ||
636 		     (fPoints[i].fX >= fBounds.fLeft && fPoints[i].fX <= fBounds.fRight &&
637 		      fPoints[i].fY >= fBounds.fTop && fPoints[i].fY <= fBounds.fBottom));
638             if (!fPoints[i].isFinite()) {
639                 isFinite = false;
640             }
641         }
642         SkASSERT(SkToBool(fIsFinite) == isFinite);
643     }
644 
645 #ifdef SK_DEBUG_PATH
646     uint32_t mask = 0;
647     for (int i = 0; i < fVerbCnt; ++i) {
648         switch (fVerbs[~i]) {
649             case SkPath::kMove_Verb:
650                 break;
651             case SkPath::kLine_Verb:
652                 mask |= SkPath::kLine_SegmentMask;
653                 break;
654             case SkPath::kQuad_Verb:
655                 mask |= SkPath::kQuad_SegmentMask;
656                 break;
657             case SkPath::kConic_Verb:
658                 mask |= SkPath::kConic_SegmentMask;
659                 break;
660             case SkPath::kCubic_Verb:
661                 mask |= SkPath::kCubic_SegmentMask;
662                 break;
663             case SkPath::kClose_Verb:
664                 break;
665             case SkPath::kDone_Verb:
666                 SkDEBUGFAIL("Done verb shouldn't be recorded.");
667                 break;
668             default:
669                 SkDEBUGFAIL("Unknown Verb");
670                 break;
671         }
672     }
673     SkASSERT(mask == fSegmentMask);
674 #endif // SK_DEBUG_PATH
675 }
676 #endif
677