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