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