Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
19#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
20 static constexpr int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
21#else
22 static constexpr int kPathRefGenIDBitCnt = 32;
23#endif
24
25//////////////////////////////////////////////////////////////////////////////
27 int incReserveVerbs,
28 int incReservePoints,
29 int incReserveConics)
30{
31 SkASSERT(incReserveVerbs >= 0);
32 SkASSERT(incReservePoints >= 0);
33
34 if ((*pathRef)->unique()) {
35 (*pathRef)->incReserve(incReserveVerbs, incReservePoints, incReserveConics);
36 } else {
38 // No need to copy if the existing ref is the empty ref (because it doesn't contain
39 // anything).
40 if (!(*pathRef)->isInitialEmptyPathRef()) {
41 copy = new SkPathRef;
42 copy->copy(**pathRef, incReserveVerbs, incReservePoints, incReserveConics);
43 } else {
44 // Size previously empty paths to exactly fit the supplied hints. The assumpion is
45 // the caller knows the exact size they want (as happens in chrome when deserializing
46 // paths).
47 copy = new SkPathRef(incReserveVerbs, incReservePoints, incReserveConics);
48 }
49 pathRef->reset(copy);
50 }
51 fPathRef = pathRef->get();
52 fPathRef->callGenIDChangeListeners();
53 fPathRef->fGenerationID = 0;
54 fPathRef->fBoundsIsDirty = true;
55 SkDEBUGCODE(fPathRef->fEditorsAttached++;)
56}
57
58//////////////////////////////////////////////////////////////////////////////
59
61 return sizeof(SkPathRef)
62 + fPoints .capacity() * sizeof(fPoints [0])
63 + fVerbs .capacity() * sizeof(fVerbs [0])
64 + fConicWeights.capacity() * sizeof(fConicWeights[0]);
65}
66
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
74static SkPathRef* gEmpty = nullptr;
75
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
85static 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
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.resize(src.fPoints.size());
171 }
172 matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.size());
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. Technically if scale is uniform, then it would stay an
205 // arc. For now, don't bother handling that (we'd also need to fixup the angles for negative
206 // scale, etc.)
207 bool rectStaysRect = matrix.rectStaysRect();
208 const PathType newType =
209 (rectStaysRect && src.fType != PathType::kArc) ? src.fType : PathType::kGeneral;
210 (*dst)->fType = newType;
211 if (newType == PathType::kOval || newType == PathType::kRRect) {
212 unsigned start = src.fRRectOrOvalStartIdx;
213 bool isCCW = SkToBool(src.fRRectOrOvalIsCCW);
214 transform_dir_and_start(matrix, newType == PathType::kRRect, &isCCW, &start);
215 (*dst)->fRRectOrOvalIsCCW = isCCW;
216 (*dst)->fRRectOrOvalStartIdx = start;
217 }
218
219 if (dst->get() == &src) {
220 (*dst)->callGenIDChangeListeners();
221 (*dst)->fGenerationID = 0;
222 }
223
224 SkDEBUGCODE((*dst)->validate();)
225}
226
228 if ((*pathRef)->unique()) {
229 SkDEBUGCODE((*pathRef)->validate();)
230 (*pathRef)->callGenIDChangeListeners();
231 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite
232 (*pathRef)->fGenerationID = 0;
233 (*pathRef)->fPoints.clear();
234 (*pathRef)->fVerbs.clear();
235 (*pathRef)->fConicWeights.clear();
236 (*pathRef)->fSegmentMask = 0;
237 (*pathRef)->fType = PathType::kGeneral;
238 SkDEBUGCODE((*pathRef)->validate();)
239 } else {
240 int oldVCnt = (*pathRef)->countVerbs();
241 int oldPCnt = (*pathRef)->countPoints();
242 pathRef->reset(new SkPathRef);
243 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
244 }
245}
246
248 SkDEBUGCODE(this->validate();)
249 SkDEBUGCODE(ref.validate();)
250
251 // We explicitly check fSegmentMask as a quick-reject. We could skip it,
252 // since it is only a cache of info in the fVerbs, but its a fast way to
253 // notice a difference
254 if (fSegmentMask != ref.fSegmentMask) {
255 return false;
256 }
257
258 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
259#ifdef SK_RELEASE
260 if (genIDMatch) {
261 return true;
262 }
263#endif
264 if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) {
265 SkASSERT(!genIDMatch);
266 return false;
267 }
268 if (ref.fVerbs.empty()) {
269 SkASSERT(ref.fPoints.empty());
270 }
271 return true;
272}
273
274void SkPathRef::copy(const SkPathRef& ref,
275 int additionalReserveVerbs,
276 int additionalReservePoints,
277 int additionalReserveConics) {
278 SkDEBUGCODE(this->validate();)
279 this->resetToSize(ref.fVerbs.size(), ref.fPoints.size(), ref.fConicWeights.size(),
280 additionalReserveVerbs, additionalReservePoints, additionalReserveConics);
281 fVerbs = ref.fVerbs;
282 fPoints = ref.fPoints;
283 fConicWeights = ref.fConicWeights;
284 fBoundsIsDirty = ref.fBoundsIsDirty;
285 if (!fBoundsIsDirty) {
286 fBounds = ref.fBounds;
287 fIsFinite = ref.fIsFinite;
288 }
289 fSegmentMask = ref.fSegmentMask;
290 fType = ref.fType;
291 fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
292 fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
293 fArcOval = ref.fArcOval;
294 fArcStartAngle = ref.fArcStartAngle;
295 fArcSweepAngle = ref.fArcSweepAngle;
296 fArcUseCenter = ref.fArcUseCenter;
297 SkDEBUGCODE(this->validate();)
298}
299
300void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
301 const SkScalar* inValues = &ending.getPoints()->fX;
302 SkScalar* outValues = &out->getWritablePoints()->fX;
303 int count = out->countPoints() * 2;
304 for (int index = 0; index < count; ++index) {
305 outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
306 }
307 out->fBoundsIsDirty = true;
308 out->fType = PathType::kGeneral;
309}
310
311std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) {
312 SkDEBUGCODE(this->validate();)
313
314 fSegmentMask |= path.fSegmentMask;
315 fBoundsIsDirty = true; // this also invalidates fIsFinite
316 fType = PathType::kGeneral;
317
318 if (int numVerbs = path.countVerbs()) {
319 memcpy(fVerbs.push_back_n(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0]));
320 }
321
322 SkPoint* pts = nullptr;
323 if (int numPts = path.countPoints()) {
324 pts = fPoints.push_back_n(numPts);
325 }
326
327 SkScalar* weights = nullptr;
328 if (int numConics = path.countWeights()) {
329 weights = fConicWeights.push_back_n(numConics);
330 }
331
332 SkDEBUGCODE(this->validate();)
333 return {pts, weights};
334}
335
336SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
337 int numVbs,
338 SkScalar** weights) {
339 SkDEBUGCODE(this->validate();)
340 int pCnt;
341 switch (verb) {
343 pCnt = numVbs;
344 break;
346 fSegmentMask |= SkPath::kLine_SegmentMask;
347 pCnt = numVbs;
348 break;
350 fSegmentMask |= SkPath::kQuad_SegmentMask;
351 pCnt = 2 * numVbs;
352 break;
354 fSegmentMask |= SkPath::kConic_SegmentMask;
355 pCnt = 2 * numVbs;
356 break;
358 fSegmentMask |= SkPath::kCubic_SegmentMask;
359 pCnt = 3 * numVbs;
360 break;
362 SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb");
363 pCnt = 0;
364 break;
366 SkDEBUGFAIL("growForRepeatedVerb called for kDone");
367 pCnt = 0;
368 break;
369 default:
370 SkDEBUGFAIL("default should not be reached");
371 pCnt = 0;
372 break;
373 }
374
375 fBoundsIsDirty = true; // this also invalidates fIsFinite
376 fType = PathType::kGeneral;
377
378 memset(fVerbs.push_back_n(numVbs), verb, numVbs);
379 if (SkPath::kConic_Verb == verb) {
380 SkASSERT(weights);
381 *weights = fConicWeights.push_back_n(numVbs);
382 }
383 SkPoint* pts = fPoints.push_back_n(pCnt);
384
385 SkDEBUGCODE(this->validate();)
386 return pts;
387}
388
389SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
390 SkDEBUGCODE(this->validate();)
391 int pCnt;
392 unsigned mask = 0;
393 switch (verb) {
395 pCnt = 1;
396 break;
399 pCnt = 1;
400 break;
403 pCnt = 2;
404 break;
407 pCnt = 2;
408 break;
411 pCnt = 3;
412 break;
414 pCnt = 0;
415 break;
417 SkDEBUGFAIL("growForVerb called for kDone");
418 pCnt = 0;
419 break;
420 default:
421 SkDEBUGFAIL("default is not reached");
422 pCnt = 0;
423 break;
424 }
425
426 fSegmentMask |= mask;
427 fBoundsIsDirty = true; // this also invalidates fIsFinite
428 fType = PathType::kGeneral;
429
430 fVerbs.push_back(verb);
431 if (SkPath::kConic_Verb == verb) {
432 fConicWeights.push_back(weight);
433 }
434 SkPoint* pts = fPoints.push_back_n(pCnt);
435
436 SkDEBUGCODE(this->validate();)
437 return pts;
438}
439
440uint32_t SkPathRef::genID(uint8_t fillType) const {
441 SkASSERT(fEditorsAttached.load() == 0);
442 static const uint32_t kMask = (static_cast<int64_t>(1) << kPathRefGenIDBitCnt) - 1;
443
444 if (fGenerationID == 0) {
445 if (fPoints.empty() && fVerbs.empty()) {
446 fGenerationID = kEmptyGenID;
447 } else {
448 static std::atomic<uint32_t> nextID{kEmptyGenID + 1};
449 do {
450 fGenerationID = nextID.fetch_add(1, std::memory_order_relaxed) & kMask;
451 } while (fGenerationID == 0 || fGenerationID == kEmptyGenID);
452 }
453 }
454 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
455 SkASSERT((unsigned)fillType < (1 << (32 - kPathRefGenIDBitCnt)));
456 fGenerationID |= static_cast<uint32_t>(fillType) << kPathRefGenIDBitCnt;
457 #endif
458 return fGenerationID;
459}
460
462 if (this == gEmpty) {
463 return;
464 }
465 fGenIDChangeListeners.add(std::move(listener));
466}
467
468int SkPathRef::genIDChangeListenerCount() { return fGenIDChangeListeners.count(); }
469
470// we need to be called *before* the genID gets changed or zerod
471void SkPathRef::callGenIDChangeListeners() {
472 fGenIDChangeListeners.changed();
473}
474
476 const SkRect& bounds = this->getBounds();
477 SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
478 Iter iter(*this);
479 SkPoint pts[4];
480 uint8_t verb = iter.next(pts);
482 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
483 if (SkPath::kConic_Verb == verb) {
484 SkVector v1_0 = pts[1] - pts[0];
485 SkVector v2_1 = pts[2] - pts[1];
486 SkVector dxdy;
487 if (v1_0.fX) {
488 SkASSERT(!v2_1.fX && !v1_0.fY);
489 dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY));
490 } else if (!v1_0.fY) {
491 SkASSERT(!v2_1.fX || !v2_1.fY);
492 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY));
493 } else {
494 SkASSERT(!v2_1.fY);
495 dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY));
496 }
497 SkRRect::Corner corner =
498 pts[1].fX == bounds.fLeft ?
499 pts[1].fY == bounds.fTop ?
501 pts[1].fY == bounds.fTop ?
503 SkASSERT(!radii[corner].fX && !radii[corner].fY);
504 radii[corner] = dxdy;
505 } else {
507 && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY)))
508 || verb == SkPath::kClose_Verb);
509 }
510 }
511 SkRRect rrect;
512 rrect.setRectRadii(bounds, radii);
513 return rrect;
514}
515
516bool SkPathRef::isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const {
517 if (fType == PathType::kRRect) {
518 if (rrect) {
519 *rrect = this->getRRect();
520 }
521 if (isCCW) {
522 *isCCW = SkToBool(fRRectOrOvalIsCCW);
523 }
524 if (start) {
525 *start = fRRectOrOvalStartIdx;
526 }
527 }
528 return fType == PathType::kRRect;
529}
530
531///////////////////////////////////////////////////////////////////////////////
532
534#ifdef SK_DEBUG
535 fPts = nullptr;
536 fConicWeights = nullptr;
537#endif
538 // need to init enough to make next() harmlessly return kDone_Verb
539 fVerbs = nullptr;
540 fVerbStop = nullptr;
541}
542
544 this->setPathRef(path);
545}
546
548 fPts = path.points();
549 fVerbs = path.verbsBegin();
550 fVerbStop = path.verbsEnd();
551 fConicWeights = path.conicWeights();
552 if (fConicWeights) {
553 fConicWeights -= 1; // begin one behind
554 }
555
556 // Don't allow iteration through non-finite points.
557 if (!path.isFinite()) {
558 fVerbStop = fVerbs;
559 }
560}
561
563 SkASSERT(pts);
564
565 SkDEBUGCODE(unsigned peekResult = this->peek();)
566
567 if (fVerbs == fVerbStop) {
568 SkASSERT(peekResult == SkPath::kDone_Verb);
569 return (uint8_t) SkPath::kDone_Verb;
570 }
571
572 // fVerbs points one beyond next verb so decrement first.
573 unsigned verb = *fVerbs++;
574 const SkPoint* srcPts = fPts;
575
576 switch (verb) {
578 pts[0] = srcPts[0];
579 srcPts += 1;
580 break;
582 pts[0] = srcPts[-1];
583 pts[1] = srcPts[0];
584 srcPts += 1;
585 break;
587 fConicWeights += 1;
588 [[fallthrough]];
590 pts[0] = srcPts[-1];
591 pts[1] = srcPts[0];
592 pts[2] = srcPts[1];
593 srcPts += 2;
594 break;
596 pts[0] = srcPts[-1];
597 pts[1] = srcPts[0];
598 pts[2] = srcPts[1];
599 pts[3] = srcPts[2];
600 srcPts += 3;
601 break;
603 break;
605 SkASSERT(fVerbs == fVerbStop);
606 break;
607 }
608 fPts = srcPts;
609 SkASSERT(peekResult == verb);
610 return (uint8_t) verb;
611}
612
613uint8_t SkPathRef::Iter::peek() const {
614 return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb;
615}
616
617
618bool SkPathRef::isValid() const {
619 switch (fType) {
621 break;
622 case PathType::kOval:
623 if (fRRectOrOvalStartIdx >= 4) {
624 return false;
625 }
626 break;
627 case PathType::kRRect:
628 if (fRRectOrOvalStartIdx >= 8) {
629 return false;
630 }
631 break;
632 case PathType::kArc:
633 if (!(fArcOval.isFinite() && SkIsFinite(fArcStartAngle, fArcSweepAngle))) {
634 return false;
635 }
636 break;
637 }
638
639 if (!fBoundsIsDirty && !fBounds.isEmpty()) {
640 bool isFinite = true;
641 auto leftTop = skvx::float2(fBounds.fLeft, fBounds.fTop);
642 auto rightBot = skvx::float2(fBounds.fRight, fBounds.fBottom);
643 for (int i = 0; i < fPoints.size(); ++i) {
644 auto point = skvx::float2(fPoints[i].fX, fPoints[i].fY);
645#ifdef SK_DEBUG
646 if (fPoints[i].isFinite() && (any(point < leftTop)|| any(point > rightBot))) {
647 SkDebugf("bad SkPathRef bounds: %g %g %g %g\n",
648 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
649 for (int j = 0; j < fPoints.size(); ++j) {
650 if (i == j) {
651 SkDebugf("*** bounds do not contain: ");
652 }
653 SkDebugf("%g %g\n", fPoints[j].fX, fPoints[j].fY);
654 }
655 return false;
656 }
657#endif
658
659 if (fPoints[i].isFinite() && any(point < leftTop) && !any(point > rightBot))
660 return false;
661 if (!fPoints[i].isFinite()) {
662 isFinite = false;
663 }
664 }
665 if (SkToBool(fIsFinite) != isFinite) {
666 return false;
667 }
668 }
669 return true;
670}
671
672void SkPathRef::reset() {
673 commonReset();
674 fPoints.clear();
675 fVerbs.clear();
676 fConicWeights.clear();
677 SkDEBUGCODE(validate();)
678}
679
681 const auto info = sk_path_analyze_verbs(fVerbs.begin(), fVerbs.size());
682 return info.valid &&
683 info.segmentMask == fSegmentMask &&
684 info.points == fPoints.size() &&
685 info.weights == fConicWeights.size();
686}
SkPoint fPts[2]
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static constexpr uint64_t kMask
Definition DrawPass.cpp:53
int count
#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
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static bool SkIsFinite(T x, Pack... values)
static constexpr int kPathRefGenIDBitCnt
Definition SkPathRef.cpp:22
static void transform_dir_and_start(const SkMatrix &matrix, bool isRRect, bool *isCCW, unsigned *start)
Definition SkPathRef.cpp:85
static SkPathRef * gEmpty
Definition SkPathRef.cpp:74
SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count)
Definition SkPath.cpp:3447
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
Editor(sk_sp< SkPathRef > *pathRef, int incReserveVerbs=0, int incReservePoints=0, int incReserveConics=0)
Definition SkPathRef.cpp:26
SkPathRef * pathRef()
Definition SkPathRef.h:158
void setPathRef(const SkPathRef &)
uint8_t peek() const
uint8_t next(SkPoint pts[4])
uint32_t genID(uint8_t fillType) const
SkRRect getRRect() const
bool operator==(const SkPathRef &ref) const
const SkRect & getBounds() const
Definition SkPathRef.h:283
int genIDChangeListenerCount()
static void Rewind(sk_sp< SkPathRef > *pathRef)
bool isFinite() const
Definition SkPathRef.h:216
static void CreateTransformedCopy(sk_sp< SkPathRef > *dst, const SkPathRef &src, const SkMatrix &matrix)
static SkPathRef * CreateEmpty()
Definition SkPathRef.cpp:76
size_t approximateBytesUsed() const
Definition SkPathRef.cpp:60
bool isRRect(SkRRect *rrect, bool *isCCW, unsigned *start) const
bool dataMatchesVerbs() const
void addGenIDChangeListener(sk_sp< SkIDChangeListener >)
bool isValid() const
void interpolate(const SkPathRef &ending, SkScalar weight, SkPathRef *out) const
@ kLine_SegmentMask
Definition SkPath.h:1437
@ kQuad_SegmentMask
Definition SkPath.h:1438
@ kCubic_SegmentMask
Definition SkPath.h:1440
@ kConic_SegmentMask
Definition SkPath.h:1439
@ kClose_Verb
Definition SkPath.h:1463
@ kMove_Verb
Definition SkPath.h:1458
@ kConic_Verb
Definition SkPath.h:1461
@ kDone_Verb
Definition SkPath.h:1464
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
@ 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:262
int capacity() const
Definition SkTArray.h:513
bool empty() const
Definition SkTArray.h:194
int size() const
Definition SkTArray.h:416
float SkScalar
Definition extension.cpp:12
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
Vec< 2, float > float2
Definition SkVx.h:1145
float fX
x-axis value
void set(float x, float y)
float fY
y-axis value
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