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