Flutter Engine
The Flutter Engine
SkPathRef.cpp
Go to the documentation of this file.
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
9
11#include "include/core/SkPath.h"
15#include "src/base/SkVx.h"
16
17#include <cstring>
18#include <utility>
19
20#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
21 static constexpr int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
22#else
23 static constexpr int kPathRefGenIDBitCnt = 32;
24#endif
25
26//////////////////////////////////////////////////////////////////////////////
28 int incReserveVerbs,
29 int incReservePoints,
30 int incReserveConics)
31{
32 SkASSERT(incReserveVerbs >= 0);
33 SkASSERT(incReservePoints >= 0);
34
35 if ((*pathRef)->unique()) {
36 (*pathRef)->incReserve(incReserveVerbs, incReservePoints, incReserveConics);
37 } else {
39 // No need to copy if the existing ref is the empty ref (because it doesn't contain
40 // anything).
41 if (!(*pathRef)->isInitialEmptyPathRef()) {
42 copy = new SkPathRef;
43 copy->copy(**pathRef, incReserveVerbs, incReservePoints, incReserveConics);
44 } else {
45 // Size previously empty paths to exactly fit the supplied hints. The assumpion is
46 // the caller knows the exact size they want (as happens in chrome when deserializing
47 // paths).
48 copy = new SkPathRef(incReserveVerbs, incReservePoints, incReserveConics);
49 }
50 pathRef->reset(copy);
51 }
52 fPathRef = pathRef->get();
53 fPathRef->callGenIDChangeListeners();
54 fPathRef->fGenerationID = 0;
55 fPathRef->fBoundsIsDirty = true;
56 SkDEBUGCODE(fPathRef->fEditorsAttached++;)
57}
58
59//////////////////////////////////////////////////////////////////////////////
60
62 return sizeof(SkPathRef)
63 + fPoints .capacity() * sizeof(fPoints [0])
64 + fVerbs .capacity() * sizeof(fVerbs [0])
65 + fConicWeights.capacity() * sizeof(fConicWeights[0]);
66}
67
69 // Deliberately don't validate() this path ref, otherwise there's no way
70 // to read one that's not valid and then free its memory without asserting.
71 SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;)
72 SkDEBUGCODE(fEditorsAttached.store(0x7777777);)
73}
74
75static SkPathRef* gEmpty = nullptr;
76
78 static SkOnce once;
79 once([]{
80 gEmpty = new SkPathRef;
81 gEmpty->computeBounds(); // Avoids races later to be the first to do this.
82 });
83 return SkRef(gEmpty);
84}
85
86static void transform_dir_and_start(const SkMatrix& matrix, bool isRRect, bool* isCCW,
87 unsigned* start) {
88 int inStart = *start;
89 int rm = 0;
90 if (isRRect) {
91 // Degenerate rrect indices to oval indices and remember the remainder.
92 // Ovals have one index per side whereas rrects have two.
93 rm = inStart & 0b1;
94 inStart /= 2;
95 }
96 // Is the antidiagonal non-zero (otherwise the diagonal is zero)
97 int antiDiag;
98 // Is the non-zero value in the top row (either kMScaleX or kMSkewX) negative
99 int topNeg;
100 // Are the two non-zero diagonal or antidiagonal values the same sign.
101 int sameSign;
102 if (matrix.get(SkMatrix::kMScaleX) != 0) {
103 antiDiag = 0b00;
104 if (matrix.get(SkMatrix::kMScaleX) > 0) {
105 topNeg = 0b00;
106 sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b01 : 0b00;
107 } else {
108 topNeg = 0b10;
109 sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b00 : 0b01;
110 }
111 } else {
112 antiDiag = 0b01;
113 if (matrix.get(SkMatrix::kMSkewX) > 0) {
114 topNeg = 0b00;
115 sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b01 : 0b00;
116 } else {
117 topNeg = 0b10;
118 sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b00 : 0b01;
119 }
120 }
121 if (sameSign != antiDiag) {
122 // This is a rotation (and maybe scale). The direction is unchanged.
123 // Trust me on the start computation (or draw yourself some pictures)
124 *start = (inStart + 4 - (topNeg | antiDiag)) % 4;
125 SkASSERT(*start < 4);
126 if (isRRect) {
127 *start = 2 * *start + rm;
128 }
129 } else {
130 // This is a mirror (and maybe scale). The direction is reversed.
131 *isCCW = !*isCCW;
132 // Trust me on the start computation (or draw yourself some pictures)
133 *start = (6 + (topNeg | antiDiag) - inStart) % 4;
134 SkASSERT(*start < 4);
135 if (isRRect) {
136 *start = 2 * *start + (rm ? 0 : 1);
137 }
138 }
139}
140
142 const SkPathRef& src,
143 const SkMatrix& matrix) {
144 SkDEBUGCODE(src.validate();)
145 if (matrix.isIdentity()) {
146 if (dst->get() != &src) {
147 src.ref();
148 dst->reset(const_cast<SkPathRef*>(&src));
149 SkDEBUGCODE((*dst)->validate();)
150 }
151 return;
152 }
153
154 sk_sp<const SkPathRef> srcKeepAlive;
155 if (!(*dst)->unique()) {
156 // If dst and src are the same then we are about to drop our only ref on the common path
157 // ref. Some other thread may have owned src when we checked unique() above but it may not
158 // continue to do so. Add another ref so we continue to be an owner until we're done.
159 if (dst->get() == &src) {
160 srcKeepAlive.reset(SkRef(&src));
161 }
162 dst->reset(new SkPathRef);
163 }
164
165 if (dst->get() != &src) {
166 (*dst)->fVerbs = src.fVerbs;
167 (*dst)->fConicWeights = src.fConicWeights;
168 (*dst)->callGenIDChangeListeners();
169 (*dst)->fGenerationID = 0; // mark as dirty
170 // don't copy, just allocate the points
171 (*dst)->fPoints.resize(src.fPoints.size());
172 }
173 matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.size());
174
175 // Need to check this here in case (&src == dst)
176 bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1;
177
178 /*
179 * Here we optimize the bounds computation, by noting if the bounds are
180 * already known, and if so, we just transform those as well and mark
181 * them as "known", rather than force the transformed path to have to
182 * recompute them.
183 *
184 * Special gotchas if the path is effectively empty (<= 1 point) or
185 * if it is non-finite. In those cases bounds need to stay empty,
186 * regardless of the matrix.
187 */
188 if (canXformBounds) {
189 (*dst)->fBoundsIsDirty = false;
190 if (src.fIsFinite) {
191 matrix.mapRect(&(*dst)->fBounds, src.fBounds);
192 if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) {
193 (*dst)->fBounds.setEmpty();
194 }
195 } else {
196 (*dst)->fIsFinite = false;
197 (*dst)->fBounds.setEmpty();
198 }
199 } else {
200 (*dst)->fBoundsIsDirty = true;
201 }
202
203 (*dst)->fSegmentMask = src.fSegmentMask;
204
205 // It's an oval only if it stays a rect. Technically if scale is uniform, then it would stay an
206 // arc. For now, don't bother handling that (we'd also need to fixup the angles for negative
207 // scale, etc.)
208 bool rectStaysRect = matrix.rectStaysRect();
209 const PathType newType =
210 (rectStaysRect && src.fType != PathType::kArc) ? src.fType : PathType::kGeneral;
211 (*dst)->fType = newType;
212 if (newType == PathType::kOval || newType == PathType::kOpenOval ||
213 newType == PathType::kRRect) {
214 unsigned start = src.fRRectOrOvalStartIdx;
215 bool isCCW = SkToBool(src.fRRectOrOvalIsCCW);
217 (*dst)->fRRectOrOvalIsCCW = isCCW;
218 (*dst)->fRRectOrOvalStartIdx = start;
219 }
220
221 if (dst->get() == &src) {
222 (*dst)->callGenIDChangeListeners();
223 (*dst)->fGenerationID = 0;
224 }
225
226 SkDEBUGCODE((*dst)->validate();)
227}
228
230 if ((*pathRef)->unique()) {
231 SkDEBUGCODE((*pathRef)->validate();)
232 (*pathRef)->callGenIDChangeListeners();
233 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite
234 (*pathRef)->fGenerationID = 0;
235 (*pathRef)->fPoints.clear();
236 (*pathRef)->fVerbs.clear();
237 (*pathRef)->fConicWeights.clear();
238 (*pathRef)->fSegmentMask = 0;
239 (*pathRef)->fType = PathType::kGeneral;
240 SkDEBUGCODE((*pathRef)->validate();)
241 } else {
242 int oldVCnt = (*pathRef)->countVerbs();
243 int oldPCnt = (*pathRef)->countPoints();
244 pathRef->reset(new SkPathRef);
245 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
246 }
247}
248
250 SkDEBUGCODE(this->validate();)
251 SkDEBUGCODE(ref.validate();)
252
253 // We explicitly check fSegmentMask as a quick-reject. We could skip it,
254 // since it is only a cache of info in the fVerbs, but its a fast way to
255 // notice a difference
256 if (fSegmentMask != ref.fSegmentMask) {
257 return false;
258 }
259
260 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
261#ifdef SK_RELEASE
262 if (genIDMatch) {
263 return true;
264 }
265#endif
266 if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) {
267 SkASSERT(!genIDMatch);
268 return false;
269 }
270 if (ref.fVerbs.empty()) {
271 SkASSERT(ref.fPoints.empty());
272 }
273 return true;
274}
275
276void SkPathRef::copy(const SkPathRef& ref,
277 int additionalReserveVerbs,
278 int additionalReservePoints,
279 int additionalReserveConics) {
280 SkDEBUGCODE(this->validate();)
281 this->resetToSize(ref.fVerbs.size(), ref.fPoints.size(), ref.fConicWeights.size(),
282 additionalReserveVerbs, additionalReservePoints, additionalReserveConics);
283 fVerbs = ref.fVerbs;
284 fPoints = ref.fPoints;
285 fConicWeights = ref.fConicWeights;
286 fBoundsIsDirty = ref.fBoundsIsDirty;
287 if (!fBoundsIsDirty) {
288 fBounds = ref.fBounds;
289 fIsFinite = ref.fIsFinite;
290 }
291 fSegmentMask = ref.fSegmentMask;
292 fType = ref.fType;
293 fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
294 fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
295 fArcOval = ref.fArcOval;
296 fArcStartAngle = ref.fArcStartAngle;
297 fArcSweepAngle = ref.fArcSweepAngle;
298 fArcType = ref.fArcType;
299 SkDEBUGCODE(this->validate();)
300}
301
302void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
303 const SkScalar* inValues = &ending.getPoints()->fX;
304 SkScalar* outValues = &out->getWritablePoints()->fX;
305 int count = out->countPoints() * 2;
306 for (int index = 0; index < count; ++index) {
307 outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
308 }
309 out->fBoundsIsDirty = true;
310 out->fType = PathType::kGeneral;
311}
312
313std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) {
314 SkDEBUGCODE(this->validate();)
315
316 fSegmentMask |= path.fSegmentMask;
317 fBoundsIsDirty = true; // this also invalidates fIsFinite
318 fType = PathType::kGeneral;
319
320 if (int numVerbs = path.countVerbs()) {
321 memcpy(fVerbs.push_back_n(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0]));
322 }
323
324 SkPoint* pts = nullptr;
325 if (int numPts = path.countPoints()) {
326 pts = fPoints.push_back_n(numPts);
327 }
328
329 SkScalar* weights = nullptr;
330 if (int numConics = path.countWeights()) {
331 weights = fConicWeights.push_back_n(numConics);
332 }
333
334 SkDEBUGCODE(this->validate();)
335 return {pts, weights};
336}
337
338SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
339 int numVbs,
340 SkScalar** weights) {
341 SkDEBUGCODE(this->validate();)
342 int pCnt;
343 switch (verb) {
345 pCnt = numVbs;
346 break;
348 fSegmentMask |= SkPath::kLine_SegmentMask;
349 pCnt = numVbs;
350 break;
352 fSegmentMask |= SkPath::kQuad_SegmentMask;
353 pCnt = 2 * numVbs;
354 break;
356 fSegmentMask |= SkPath::kConic_SegmentMask;
357 pCnt = 2 * numVbs;
358 break;
360 fSegmentMask |= SkPath::kCubic_SegmentMask;
361 pCnt = 3 * numVbs;
362 break;
364 SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb");
365 pCnt = 0;
366 break;
368 SkDEBUGFAIL("growForRepeatedVerb called for kDone");
369 pCnt = 0;
370 break;
371 default:
372 SkDEBUGFAIL("default should not be reached");
373 pCnt = 0;
374 break;
375 }
376
377 fBoundsIsDirty = true; // this also invalidates fIsFinite
378 fType = PathType::kGeneral;
379
380 memset(fVerbs.push_back_n(numVbs), verb, numVbs);
381 if (SkPath::kConic_Verb == verb) {
382 SkASSERT(weights);
383 *weights = fConicWeights.push_back_n(numVbs);
384 }
385 SkPoint* pts = fPoints.push_back_n(pCnt);
386
387 SkDEBUGCODE(this->validate();)
388 return pts;
389}
390
391SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
392 SkDEBUGCODE(this->validate();)
393 int pCnt;
394 unsigned mask = 0;
395 switch (verb) {
397 pCnt = 1;
398 break;
401 pCnt = 1;
402 break;
405 pCnt = 2;
406 break;
409 pCnt = 2;
410 break;
413 pCnt = 3;
414 break;
416 pCnt = 0;
417 break;
419 SkDEBUGFAIL("growForVerb called for kDone");
420 pCnt = 0;
421 break;
422 default:
423 SkDEBUGFAIL("default is not reached");
424 pCnt = 0;
425 break;
426 }
427
428 fSegmentMask |= mask;
429 fBoundsIsDirty = true; // this also invalidates fIsFinite
430 if (verb == SkPath::kClose_Verb && fType == PathType::kOpenOval) {
431 fType = PathType::kOval;
432 } else {
433 fType = PathType::kGeneral;
434 }
435
436 fVerbs.push_back(verb);
437 if (SkPath::kConic_Verb == verb) {
438 fConicWeights.push_back(weight);
439 }
440 SkPoint* pts = fPoints.push_back_n(pCnt);
441
442 SkDEBUGCODE(this->validate();)
443 return pts;
444}
445
446uint32_t SkPathRef::genID(uint8_t fillType) const {
447 SkASSERT(fEditorsAttached.load() == 0);
448 static const uint32_t kMask = (static_cast<int64_t>(1) << kPathRefGenIDBitCnt) - 1;
449
450 if (fGenerationID == 0) {
451 if (fPoints.empty() && fVerbs.empty()) {
452 fGenerationID = kEmptyGenID;
453 } else {
454 static std::atomic<uint32_t> nextID{kEmptyGenID + 1};
455 do {
456 fGenerationID = nextID.fetch_add(1, std::memory_order_relaxed) & kMask;
457 } while (fGenerationID == 0 || fGenerationID == kEmptyGenID);
458 }
459 }
460 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
461 SkASSERT((unsigned)fillType < (1 << (32 - kPathRefGenIDBitCnt)));
462 fGenerationID |= static_cast<uint32_t>(fillType) << kPathRefGenIDBitCnt;
463 #endif
464 return fGenerationID;
465}
466
468 if (this == gEmpty) {
469 return;
470 }
471 fGenIDChangeListeners.add(std::move(listener));
472}
473
474int SkPathRef::genIDChangeListenerCount() { return fGenIDChangeListeners.count(); }
475
476// we need to be called *before* the genID gets changed or zerod
477void SkPathRef::callGenIDChangeListeners() {
478 fGenIDChangeListeners.changed();
479}
480
482 const SkRect& bounds = this->getBounds();
483 SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
484 Iter iter(*this);
485 SkPoint pts[4];
486 uint8_t verb = iter.next(pts);
488 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
489 if (SkPath::kConic_Verb == verb) {
490 SkVector v1_0 = pts[1] - pts[0];
491 SkVector v2_1 = pts[2] - pts[1];
492 SkVector dxdy;
493 if (v1_0.fX) {
494 SkASSERT(!v2_1.fX && !v1_0.fY);
495 dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY));
496 } else if (!v1_0.fY) {
497 SkASSERT(!v2_1.fX || !v2_1.fY);
498 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY));
499 } else {
500 SkASSERT(!v2_1.fY);
501 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY));
502 }
503 SkRRect::Corner corner =
504 pts[1].fX == bounds.fLeft ?
505 pts[1].fY == bounds.fTop ?
507 pts[1].fY == bounds.fTop ?
509 SkASSERT(!radii[corner].fX && !radii[corner].fY);
510 radii[corner] = dxdy;
511 } else {
513 && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY)))
514 || verb == SkPath::kClose_Verb);
515 }
516 }
518 rrect.setRectRadii(bounds, radii);
519 return rrect;
520}
521
522bool SkPathRef::isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const {
523 if (fType == PathType::kRRect) {
524 if (rrect) {
525 *rrect = this->getRRect();
526 }
527 if (isCCW) {
528 *isCCW = SkToBool(fRRectOrOvalIsCCW);
529 }
530 if (start) {
531 *start = fRRectOrOvalStartIdx;
532 }
533 }
534 return fType == PathType::kRRect;
535}
536
537///////////////////////////////////////////////////////////////////////////////
538
540#ifdef SK_DEBUG
541 fPts = nullptr;
542 fConicWeights = nullptr;
543#endif
544 // need to init enough to make next() harmlessly return kDone_Verb
545 fVerbs = nullptr;
546 fVerbStop = nullptr;
547}
548
550 this->setPathRef(path);
551}
552
554 fPts = path.points();
555 fVerbs = path.verbsBegin();
556 fVerbStop = path.verbsEnd();
557 fConicWeights = path.conicWeights();
558 if (fConicWeights) {
559 fConicWeights -= 1; // begin one behind
560 }
561
562 // Don't allow iteration through non-finite points.
563 if (!path.isFinite()) {
564 fVerbStop = fVerbs;
565 }
566}
567
569 SkASSERT(pts);
570
571 SkDEBUGCODE(unsigned peekResult = this->peek();)
572
573 if (fVerbs == fVerbStop) {
574 SkASSERT(peekResult == SkPath::kDone_Verb);
575 return (uint8_t) SkPath::kDone_Verb;
576 }
577
578 // fVerbs points one beyond next verb so decrement first.
579 unsigned verb = *fVerbs++;
580 const SkPoint* srcPts = fPts;
581
582 switch (verb) {
584 pts[0] = srcPts[0];
585 srcPts += 1;
586 break;
588 pts[0] = srcPts[-1];
589 pts[1] = srcPts[0];
590 srcPts += 1;
591 break;
593 fConicWeights += 1;
594 [[fallthrough]];
596 pts[0] = srcPts[-1];
597 pts[1] = srcPts[0];
598 pts[2] = srcPts[1];
599 srcPts += 2;
600 break;
602 pts[0] = srcPts[-1];
603 pts[1] = srcPts[0];
604 pts[2] = srcPts[1];
605 pts[3] = srcPts[2];
606 srcPts += 3;
607 break;
609 break;
611 SkASSERT(fVerbs == fVerbStop);
612 break;
613 }
614 fPts = srcPts;
615 SkASSERT(peekResult == verb);
616 return (uint8_t) verb;
617}
618
619uint8_t SkPathRef::Iter::peek() const {
620 return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb;
621}
622
623
624bool SkPathRef::isValid() const {
625 switch (fType) {
627 break;
628 case PathType::kOval:
630 if (fRRectOrOvalStartIdx >= 4) {
631 return false;
632 }
633 break;
634 case PathType::kRRect:
635 if (fRRectOrOvalStartIdx >= 8) {
636 return false;
637 }
638 break;
639 case PathType::kArc:
640 if (!(fArcOval.isFinite() && SkIsFinite(fArcStartAngle, fArcSweepAngle))) {
641 return false;
642 }
643 break;
644 }
645
646 if (!fBoundsIsDirty && !fBounds.isEmpty()) {
647 bool isFinite = true;
648 auto leftTop = skvx::float2(fBounds.fLeft, fBounds.fTop);
649 auto rightBot = skvx::float2(fBounds.fRight, fBounds.fBottom);
650 for (int i = 0; i < fPoints.size(); ++i) {
651 auto point = skvx::float2(fPoints[i].fX, fPoints[i].fY);
652#ifdef SK_DEBUG
653 if (fPoints[i].isFinite() && (any(point < leftTop)|| any(point > rightBot))) {
654 SkDebugf("bad SkPathRef bounds: %g %g %g %g\n",
655 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
656 for (int j = 0; j < fPoints.size(); ++j) {
657 if (i == j) {
658 SkDebugf("*** bounds do not contain: ");
659 }
660 SkDebugf("%g %g\n", fPoints[j].fX, fPoints[j].fY);
661 }
662 return false;
663 }
664#endif
665
666 if (fPoints[i].isFinite() && any(point < leftTop) && !any(point > rightBot))
667 return false;
668 if (!fPoints[i].isFinite()) {
669 isFinite = false;
670 }
671 }
672 if (SkToBool(fIsFinite) != isFinite) {
673 return false;
674 }
675 }
676 return true;
677}
678
679void SkPathRef::reset() {
680 commonReset();
681 fPoints.clear();
682 fVerbs.clear();
683 fConicWeights.clear();
684 SkDEBUGCODE(validate();)
685}
686
688 const auto info = sk_path_analyze_verbs(fVerbs.begin(), fVerbs.size());
689 return info.valid &&
690 info.segmentMask == fSegmentMask &&
691 info.points == fPoints.size() &&
692 info.weights == fConicWeights.size();
693}
SkPoint fPts[2]
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
m reset()
int count
Definition: FontMgrTest.cpp:50
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
static constexpr int kPathRefGenIDBitCnt
Definition: SkPathRef.cpp:23
static void transform_dir_and_start(const SkMatrix &matrix, bool isRRect, bool *isCCW, unsigned *start)
Definition: SkPathRef.cpp:86
static SkPathRef * gEmpty
Definition: SkPathRef.cpp:75
SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count)
Definition: SkPath.cpp:3515
static T * SkRef(T *obj)
Definition: SkRefCnt.h:132
#define SkScalarAbs(x)
Definition: SkScalar.h:39
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
void changed() SK_EXCLUDES(fMutex)
int count() const SK_EXCLUDES(fMutex)
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
static constexpr int kMScaleX
horizontal scale factor
Definition: SkMatrix.h:353
static constexpr int kMSkewY
vertical skew factor
Definition: SkMatrix.h:356
static constexpr int kMScaleY
vertical scale factor
Definition: SkMatrix.h:357
static constexpr int kMSkewX
horizontal skew factor
Definition: SkMatrix.h:354
void ref() const
Definition: SkRefCnt.h:176
Definition: SkOnce.h:22
Editor(sk_sp< SkPathRef > *pathRef, int incReserveVerbs=0, int incReservePoints=0, int incReserveConics=0)
Definition: SkPathRef.cpp:27
SkPathRef * pathRef()
Definition: SkPathRef.h:159
void setPathRef(const SkPathRef &)
Definition: SkPathRef.cpp:553
uint8_t peek() const
Definition: SkPathRef.cpp:619
uint8_t next(SkPoint pts[4])
Definition: SkPathRef.cpp:568
SkDEBUGCODE(void validate() const { SkASSERT(this->isValid());}) void reset()
uint32_t genID(uint8_t fillType) const
Definition: SkPathRef.cpp:446
SkPathRef(SkSpan< const SkPoint > points, SkSpan< const uint8_t > verbs, SkSpan< const SkScalar > weights, unsigned segmentMask)
Definition: SkPathRef.h:71
SkRRect getRRect() const
Definition: SkPathRef.cpp:481
bool operator==(const SkPathRef &ref) const
Definition: SkPathRef.cpp:249
const SkRect & getBounds() const
Definition: SkPathRef.h:281
int genIDChangeListenerCount()
Definition: SkPathRef.cpp:474
static void Rewind(sk_sp< SkPathRef > *pathRef)
Definition: SkPathRef.cpp:229
bool isFinite() const
Definition: SkPathRef.h:217
static void CreateTransformedCopy(sk_sp< SkPathRef > *dst, const SkPathRef &src, const SkMatrix &matrix)
Definition: SkPathRef.cpp:141
static SkPathRef * CreateEmpty()
Definition: SkPathRef.cpp:77
size_t approximateBytesUsed() const
Definition: SkPathRef.cpp:61
bool isRRect(SkRRect *rrect, bool *isCCW, unsigned *start) const
Definition: SkPathRef.cpp:522
bool dataMatchesVerbs() const
Definition: SkPathRef.cpp:687
void addGenIDChangeListener(sk_sp< SkIDChangeListener >)
Definition: SkPathRef.cpp:467
bool isValid() const
Definition: SkPathRef.cpp:624
void interpolate(const SkPathRef &ending, SkScalar weight, SkPathRef *out) const
Definition: SkPathRef.cpp:302
@ kLine_SegmentMask
Definition: SkPath.h:1445
@ kQuad_SegmentMask
Definition: SkPath.h:1446
@ kCubic_SegmentMask
Definition: SkPath.h:1448
@ kConic_SegmentMask
Definition: SkPath.h:1447
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
@ kUpperLeft_Corner
index of top-left corner radii
Definition: SkRRect.h:252
@ kLowerRight_Corner
index of bottom-right corner radii
Definition: SkRRect.h:254
@ kUpperRight_Corner
index of top-right corner radii
Definition: SkRRect.h:253
@ kLowerLeft_Corner
index of bottom-left corner radii
Definition: SkRRect.h:255
void setRectRadii(const SkRect &rect, const SkVector radii[4])
Definition: SkRRect.cpp:189
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
T * push_back_n(int n)
Definition: SkTArray.h:267
int capacity() const
Definition: SkTArray.h:518
bool empty() const
Definition: SkTArray.h:199
int size() const
Definition: SkTArray.h:421
float SkScalar
Definition: extension.cpp:12
if(end==-1)
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkRRect rrect
Definition: SkRecords.h:232
Definition: copy.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
dst
Definition: cp.py:12
Vec< 2, float > float2
Definition: SkVx.h:1145
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
bool isFinite() const
Definition: SkRect.h:711
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
bool isEmpty() const
Definition: SkRect.h:693
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15