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