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