Flutter Engine
The Flutter Engine
SkClipStack.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 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/core/SkRectPriv.h"
17
18#include <array>
19#include <atomic>
20#include <new>
21
23 switch (that.getDeviceSpaceType()) {
25 fDeviceSpaceRRect.setEmpty();
26 fDeviceSpacePath.reset();
27 fShader.reset();
28 break;
29 case DeviceSpaceType::kRect: // Rect uses rrect
31 fDeviceSpacePath.reset();
32 fShader.reset();
33 fDeviceSpaceRRect = that.fDeviceSpaceRRect;
34 break;
36 fShader.reset();
37 fDeviceSpacePath.set(that.getDeviceSpacePath());
38 break;
40 fDeviceSpacePath.reset();
41 fShader = that.fShader;
42 break;
43 }
44
45 fSaveCount = that.fSaveCount;
46 fOp = that.fOp;
47 fDeviceSpaceType = that.fDeviceSpaceType;
48 fDoAA = that.fDoAA;
49 fIsReplace = that.fIsReplace;
50 fFiniteBoundType = that.fFiniteBoundType;
51 fFiniteBound = that.fFiniteBound;
52 fIsIntersectionOfRects = that.fIsIntersectionOfRects;
53 fGenID = that.fGenID;
54}
55
57
58bool SkClipStack::Element::operator== (const Element& element) const {
59 if (this == &element) {
60 return true;
61 }
62 if (fOp != element.fOp || fDeviceSpaceType != element.fDeviceSpaceType ||
63 fDoAA != element.fDoAA || fIsReplace != element.fIsReplace ||
64 fSaveCount != element.fSaveCount) {
65 return false;
66 }
67 switch (fDeviceSpaceType) {
68 case DeviceSpaceType::kShader:
69 return this->getShader() == element.getShader();
71 return this->getDeviceSpacePath() == element.getDeviceSpacePath();
73 return fDeviceSpaceRRect == element.fDeviceSpaceRRect;
75 return this->getDeviceSpaceRect() == element.getDeviceSpaceRect();
77 return true;
78 default:
79 SkDEBUGFAIL("Unexpected type.");
80 return false;
81 }
82}
83
85 static const SkRect kEmpty = {0, 0, 0, 0};
87 switch (fDeviceSpaceType) {
88 case DeviceSpaceType::kRect: // fallthrough
90 return fDeviceSpaceRRect.getBounds();
92 return fDeviceSpacePath->getBounds();
93 case DeviceSpaceType::kShader:
94 // Shaders have infinite bounds since any pixel could have clipped or full coverage
95 // (which is different from wide-open, where every pixel has 1.0 coverage, or empty
96 // where every pixel has 0.0 coverage).
97 return kInfinite;
99 return kEmpty;
100 default:
101 SkDEBUGFAIL("Unexpected type.");
102 return kEmpty;
103 }
104}
105
107 switch (fDeviceSpaceType) {
109 return this->getDeviceSpaceRect().contains(rect);
111 return fDeviceSpaceRRect.contains(rect);
113 return fDeviceSpacePath->conservativelyContainsRect(rect);
115 case DeviceSpaceType::kShader:
116 return false;
117 default:
118 SkDEBUGFAIL("Unexpected type.");
119 return false;
120 }
121}
122
124 switch (fDeviceSpaceType) {
126 return this->getDeviceSpaceRect().contains(rrect.getBounds());
128 // We don't currently have a generalized rrect-rrect containment.
129 return fDeviceSpaceRRect.contains(rrect.getBounds()) || rrect == fDeviceSpaceRRect;
131 return fDeviceSpacePath->conservativelyContainsRect(rrect.getBounds());
133 case DeviceSpaceType::kShader:
134 return false;
135 default:
136 SkDEBUGFAIL("Unexpected type.");
137 return false;
138 }
139}
140
142 switch (fDeviceSpaceType) {
144 fDeviceSpacePath.init();
145 fDeviceSpacePath->addRect(this->getDeviceSpaceRect());
146 fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
147 fDeviceSpaceType = DeviceSpaceType::kPath;
148 break;
150 fDeviceSpacePath.init();
151 fDeviceSpacePath->addRRect(fDeviceSpaceRRect);
152 fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
153 fDeviceSpaceType = DeviceSpaceType::kPath;
154 break;
156 fDeviceSpacePath->toggleInverseFillType();
157 break;
158 case DeviceSpaceType::kShader:
159 fShader = as_SB(fShader)->makeInvertAlpha();
160 break;
162 // Should this set to an empty, inverse filled path?
163 break;
164 }
165}
166
167void SkClipStack::Element::initCommon(int saveCount, SkClipOp op, bool doAA) {
168 fSaveCount = saveCount;
169 fOp = op;
170 fDoAA = doAA;
171 fIsReplace = false;
172 // A default of inside-out and empty bounds means the bounds are effectively void as it
173 // indicates that nothing is known to be outside the clip.
174 fFiniteBoundType = kInsideOut_BoundsType;
175 fFiniteBound.setEmpty();
176 fIsIntersectionOfRects = false;
177 fGenID = kInvalidGenID;
178}
179
180void SkClipStack::Element::initRect(int saveCount, const SkRect& rect, const SkMatrix& m,
181 SkClipOp op, bool doAA) {
182 if (m.rectStaysRect()) {
183 SkRect devRect;
184 m.mapRect(&devRect, rect);
185 fDeviceSpaceRRect.setRect(devRect);
186 fDeviceSpaceType = DeviceSpaceType::kRect;
187 this->initCommon(saveCount, op, doAA);
188 return;
189 }
190 SkPath path;
191 path.addRect(rect);
192 path.setIsVolatile(true);
193 this->initAsPath(saveCount, path, m, op, doAA);
194}
195
196void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, const SkMatrix& m,
197 SkClipOp op, bool doAA) {
198 if (rrect.transform(m, &fDeviceSpaceRRect)) {
199 SkRRect::Type type = fDeviceSpaceRRect.getType();
201 fDeviceSpaceType = DeviceSpaceType::kRect;
202 } else {
203 fDeviceSpaceType = DeviceSpaceType::kRRect;
204 }
205 this->initCommon(saveCount, op, doAA);
206 return;
207 }
208 SkPath path;
209 path.addRRect(rrect);
210 path.setIsVolatile(true);
211 this->initAsPath(saveCount, path, m, op, doAA);
212}
213
214void SkClipStack::Element::initPath(int saveCount, const SkPath& path, const SkMatrix& m,
215 SkClipOp op, bool doAA) {
216 if (!path.isInverseFillType()) {
217 SkRect r;
218 if (path.isRect(&r)) {
219 this->initRect(saveCount, r, m, op, doAA);
220 return;
221 }
222 SkRect ovalRect;
223 if (path.isOval(&ovalRect)) {
225 rrect.setOval(ovalRect);
226 this->initRRect(saveCount, rrect, m, op, doAA);
227 return;
228 }
229 }
230 this->initAsPath(saveCount, path, m, op, doAA);
231}
232
233void SkClipStack::Element::initAsPath(int saveCount, const SkPath& path, const SkMatrix& m,
234 SkClipOp op, bool doAA) {
235 path.transform(m, fDeviceSpacePath.init());
236 fDeviceSpacePath->setIsVolatile(true);
237 fDeviceSpaceType = DeviceSpaceType::kPath;
238 this->initCommon(saveCount, op, doAA);
239}
240
241void SkClipStack::Element::initShader(int saveCount, sk_sp<SkShader> shader) {
242 SkASSERT(shader);
243 fDeviceSpaceType = DeviceSpaceType::kShader;
244 fShader = std::move(shader);
245 this->initCommon(saveCount, SkClipOp::kIntersect, false);
246}
247
248void SkClipStack::Element::initReplaceRect(int saveCount, const SkRect& rect, bool doAA) {
249 fDeviceSpaceRRect.setRect(rect);
250 fDeviceSpaceType = DeviceSpaceType::kRect;
251 this->initCommon(saveCount, SkClipOp::kIntersect, doAA);
252 fIsReplace = true;
253}
254
256 switch (fDeviceSpaceType) {
258 path->reset();
259 break;
261 path->reset();
262 path->addRect(this->getDeviceSpaceRect());
263 break;
265 path->reset();
266 path->addRRect(fDeviceSpaceRRect);
267 break;
269 *path = *fDeviceSpacePath;
270 break;
271 case DeviceSpaceType::kShader:
272 path->reset();
273 path->addRect(SkRectPriv::MakeLargeS32());
274 break;
275 }
276 path->setIsVolatile(true);
277}
278
279void SkClipStack::Element::setEmpty() {
280 fDeviceSpaceType = DeviceSpaceType::kEmpty;
281 fFiniteBound.setEmpty();
282 fFiniteBoundType = kNormal_BoundsType;
283 fIsIntersectionOfRects = false;
284 fDeviceSpaceRRect.setEmpty();
285 fDeviceSpacePath.reset();
286 fShader.reset();
287 fGenID = kEmptyGenID;
288 SkDEBUGCODE(this->checkEmpty();)
289}
290
291void SkClipStack::Element::checkEmpty() const {
292 SkASSERT(fFiniteBound.isEmpty());
293 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
294 SkASSERT(!fIsIntersectionOfRects);
295 SkASSERT(kEmptyGenID == fGenID);
296 SkASSERT(fDeviceSpaceRRect.isEmpty());
297 SkASSERT(!fDeviceSpacePath.isValid());
298 SkASSERT(!fShader);
299}
300
301bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkClipOp op) const {
302 if (DeviceSpaceType::kEmpty == fDeviceSpaceType &&
304 return true;
305 }
306 // Only clips within the same save/restore frame (as captured by
307 // the save count) can be merged
308 return fSaveCount == saveCount &&
309 SkClipOp::kIntersect == op &&
310 (SkClipOp::kIntersect == fOp || this->isReplaceOp());
311}
312
313bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
314 SkASSERT(DeviceSpaceType::kRect == fDeviceSpaceType);
315
316 if (fDoAA == newAA) {
317 // if the AA setting is the same there is no issue
318 return true;
319 }
320
321 if (!SkRect::Intersects(this->getDeviceSpaceRect(), newR)) {
322 // The calling code will correctly set the result to the empty clip
323 return true;
324 }
325
326 if (this->getDeviceSpaceRect().contains(newR)) {
327 // if the new rect carves out a portion of the old one there is no
328 // issue
329 return true;
330 }
331
332 // So either the two overlap in some complex manner or newR contains oldR.
333 // In the first, case the edges will require different AA. In the second,
334 // the AA setting that would be carried forward is incorrect (e.g., oldR
335 // is AA while newR is BW but since newR contains oldR, oldR will be
336 // drawn BW) since the new AA setting will predominate.
337 return false;
338}
339
340// a mirror of combineBoundsRevDiff
341void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
342 switch (combination) {
343 case kInvPrev_InvCur_FillCombo:
344 // In this case the only pixels that can remain set
345 // are inside the current clip rect since the extensions
346 // to infinity of both clips cancel out and whatever
347 // is outside of the current clip is removed
348 fFiniteBoundType = kNormal_BoundsType;
349 break;
350 case kInvPrev_Cur_FillCombo:
351 // In this case the current op is finite so the only pixels
352 // that aren't set are whatever isn't set in the previous
353 // clip and whatever this clip carves out
354 fFiniteBound.join(prevFinite);
355 fFiniteBoundType = kInsideOut_BoundsType;
356 break;
357 case kPrev_InvCur_FillCombo:
358 // In this case everything outside of this clip's bound
359 // is erased, so the only pixels that can remain set
360 // occur w/in the intersection of the two finite bounds
361 if (!fFiniteBound.intersect(prevFinite)) {
362 fFiniteBound.setEmpty();
363 fGenID = kEmptyGenID;
364 }
365 fFiniteBoundType = kNormal_BoundsType;
366 break;
367 case kPrev_Cur_FillCombo:
368 // The most conservative result bound is that of the
369 // prior clip. This could be wildly incorrect if the
370 // second clip either exactly matches the first clip
371 // (which should yield the empty set) or reduces the
372 // size of the prior bound (e.g., if the second clip
373 // exactly matched the bottom half of the prior clip).
374 // We ignore these two possibilities.
375 fFiniteBound = prevFinite;
376 break;
377 default:
378 SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
379 break;
380 }
381}
382
383// a mirror of combineBoundsUnion
384void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
385
386 switch (combination) {
387 case kInvPrev_InvCur_FillCombo:
388 // The only pixels that aren't writable in this case
389 // occur in the union of the two finite bounds
390 fFiniteBound.join(prevFinite);
391 fFiniteBoundType = kInsideOut_BoundsType;
392 break;
393 case kInvPrev_Cur_FillCombo:
394 // In this case the only pixels that will remain writeable
395 // are within the current clip
396 break;
397 case kPrev_InvCur_FillCombo:
398 // In this case the only pixels that will remain writeable
399 // are with the previous clip
400 fFiniteBound = prevFinite;
401 fFiniteBoundType = kNormal_BoundsType;
402 break;
403 case kPrev_Cur_FillCombo:
404 if (!fFiniteBound.intersect(prevFinite)) {
405 this->setEmpty();
406 }
407 break;
408 default:
409 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
410 break;
411 }
412}
413
414void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
415 // We set this first here but we may overwrite it later if we determine that the clip is
416 // either wide-open or empty.
417 fGenID = GetNextGenID();
419 // First, optimistically update the current Element's bound information
420 // with the current clip's bound
421 fIsIntersectionOfRects = false;
422 switch (fDeviceSpaceType) {
424 fFiniteBound = this->getDeviceSpaceRect();
425 fFiniteBoundType = kNormal_BoundsType;
426
427 if (this->isReplaceOp() ||
428 (SkClipOp::kIntersect == fOp && nullptr == prior) ||
429 (SkClipOp::kIntersect == fOp && prior->fIsIntersectionOfRects &&
430 prior->rectRectIntersectAllowed(this->getDeviceSpaceRect(), fDoAA))) {
431 fIsIntersectionOfRects = true;
432 }
433 break;
435 fFiniteBound = fDeviceSpaceRRect.getBounds();
436 fFiniteBoundType = kNormal_BoundsType;
437 break;
439 fFiniteBound = fDeviceSpacePath->getBounds();
440
441 if (fDeviceSpacePath->isInverseFillType()) {
442 fFiniteBoundType = kInsideOut_BoundsType;
443 } else {
444 fFiniteBoundType = kNormal_BoundsType;
445 }
446 break;
447 case DeviceSpaceType::kShader:
448 // A shader is infinite. We don't act as wide-open here (which is an empty bounds with
449 // the inside out type). This is because when the bounds is empty and inside-out, we
450 // know there's full coverage everywhere. With a shader, there's *unknown* coverage
451 // everywhere.
452 fFiniteBound = SkRectPriv::MakeLargeS32();
453 fFiniteBoundType = kNormal_BoundsType;
454 break;
456 SkDEBUGFAIL("We shouldn't get here with an empty element.");
457 break;
458 }
459
460 // Now determine the previous Element's bound information taking into
461 // account that there may be no previous clip
462 SkRect prevFinite;
464
465 if (nullptr == prior) {
466 // no prior clip means the entire plane is writable
467 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
468 prevType = kInsideOut_BoundsType;
469 } else {
470 prevFinite = prior->fFiniteBound;
471 prevType = prior->fFiniteBoundType;
472 }
473
474 FillCombo combination = kPrev_Cur_FillCombo;
475 if (kInsideOut_BoundsType == fFiniteBoundType) {
476 combination = (FillCombo) (combination | 0x01);
477 }
478 if (kInsideOut_BoundsType == prevType) {
479 combination = (FillCombo) (combination | 0x02);
480 }
481
482 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
483 kInvPrev_Cur_FillCombo == combination ||
484 kPrev_InvCur_FillCombo == combination ||
485 kPrev_Cur_FillCombo == combination);
486
487 // Now integrate with clip with the prior clips
488 if (!this->isReplaceOp()) {
489 switch (fOp) {
491 this->combineBoundsDiff(combination, prevFinite);
492 break;
494 this->combineBoundsIntersection(combination, prevFinite);
495 break;
496 default:
497 SkDebugf("SkClipOp error\n");
498 SkASSERT(0);
499 break;
500 }
501 } // else Replace just ignores everything prior and should already have filled in bounds.
502}
503
504// This constant determines how many Element's are allocated together as a block in
505// the deque. As such it needs to balance allocating too much memory vs.
506// incurring allocation/deallocation thrashing. It should roughly correspond to
507// the deepest save/restore stack we expect to see.
508static const int kDefaultElementAllocCnt = 8;
509
511 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
512 , fSaveCount(0) {
513}
514
515SkClipStack::SkClipStack(void* storage, size_t size)
516 : fDeque(sizeof(Element), storage, size, kDefaultElementAllocCnt)
517 , fSaveCount(0) {
518}
519
521 : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
522 *this = b;
523}
524
526 reset();
527}
528
530 if (this == &b) {
531 return *this;
532 }
533 reset();
534
535 fSaveCount = b.fSaveCount;
536 SkDeque::F2BIter recIter(b.fDeque);
537 for (const Element* element = (const Element*)recIter.next();
538 element != nullptr;
539 element = (const Element*)recIter.next()) {
540 new (fDeque.push_back()) Element(*element);
541 }
542
543 return *this;
544}
545
547 if (this->getTopmostGenID() == b.getTopmostGenID()) {
548 return true;
549 }
550 if (fSaveCount != b.fSaveCount ||
551 fDeque.count() != b.fDeque.count()) {
552 return false;
553 }
554 SkDeque::F2BIter myIter(fDeque);
555 SkDeque::F2BIter bIter(b.fDeque);
556 const Element* myElement = (const Element*)myIter.next();
557 const Element* bElement = (const Element*)bIter.next();
558
559 while (myElement != nullptr && bElement != nullptr) {
560 if (*myElement != *bElement) {
561 return false;
562 }
563 myElement = (const Element*)myIter.next();
564 bElement = (const Element*)bIter.next();
565 }
566 return myElement == nullptr && bElement == nullptr;
567}
568
570 // We used a placement new for each object in fDeque, so we're responsible
571 // for calling the destructor on each of them as well.
572 while (!fDeque.empty()) {
573 Element* element = (Element*)fDeque.back();
574 element->~Element();
575 fDeque.pop_back();
576 }
577
578 fSaveCount = 0;
579}
580
582 fSaveCount += 1;
583}
584
586 fSaveCount -= 1;
587 restoreTo(fSaveCount);
588}
589
590void SkClipStack::restoreTo(int saveCount) {
591 while (!fDeque.empty()) {
592 Element* element = (Element*)fDeque.back();
593 if (element->fSaveCount <= saveCount) {
594 break;
595 }
596 element->~Element();
597 fDeque.pop_back();
598 }
599}
600
601SkRect SkClipStack::bounds(const SkIRect& deviceBounds) const {
602 // TODO: optimize this.
603 SkRect r;
605 this->getBounds(&r, &bounds);
607 return SkRect::Make(deviceBounds);
608 }
609 return r.intersect(SkRect::Make(deviceBounds)) ? r : SkRect::MakeEmpty();
610}
611
612// TODO: optimize this.
613bool SkClipStack::isEmpty(const SkIRect& r) const { return this->bounds(r).isEmpty(); }
614
615void SkClipStack::getBounds(SkRect* canvFiniteBound,
616 BoundsType* boundType,
617 bool* isIntersectionOfRects) const {
618 SkASSERT(canvFiniteBound && boundType);
619
620 const Element* element = (const Element*)fDeque.back();
621
622 if (nullptr == element) {
623 // the clip is wide open - the infinite plane w/ no pixels un-writeable
624 canvFiniteBound->setEmpty();
625 *boundType = kInsideOut_BoundsType;
626 if (isIntersectionOfRects) {
627 *isIntersectionOfRects = false;
628 }
629 return;
630 }
631
632 *canvFiniteBound = element->fFiniteBound;
633 *boundType = element->fFiniteBoundType;
634 if (isIntersectionOfRects) {
635 *isIntersectionOfRects = element->fIsIntersectionOfRects;
636 }
637}
638
639bool SkClipStack::internalQuickContains(const SkRect& rect) const {
640 Iter iter(*this, Iter::kTop_IterStart);
641 const Element* element = iter.prev();
642 while (element != nullptr) {
643 // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
644 if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
645 return false;
646 }
647 if (element->isInverseFilled()) {
648 // Part of 'rect' could be trimmed off by the inverse-filled clip element
649 if (SkRect::Intersects(element->getBounds(), rect)) {
650 return false;
651 }
652 } else {
653 if (!element->contains(rect)) {
654 return false;
655 }
656 }
657 if (element->isReplaceOp()) {
658 break;
659 }
660 element = iter.prev();
661 }
662 return true;
663}
664
665bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
666 Iter iter(*this, Iter::kTop_IterStart);
667 const Element* element = iter.prev();
668 while (element != nullptr) {
669 // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
670 if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
671 return false;
672 }
673 if (element->isInverseFilled()) {
674 // Part of 'rrect' could be trimmed off by the inverse-filled clip element
675 if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) {
676 return false;
677 }
678 } else {
679 if (!element->contains(rrect)) {
680 return false;
681 }
682 }
683 if (element->isReplaceOp()) {
684 break;
685 }
686 element = iter.prev();
687 }
688 return true;
689}
690
691void SkClipStack::pushElement(const Element& element) {
692 // Use reverse iterator instead of back because Rect path may need previous
694 Element* prior = (Element*) iter.prev();
695
696 if (prior) {
697 if (element.isReplaceOp()) {
698 this->restoreTo(fSaveCount - 1);
699 prior = (Element*) fDeque.back();
700 } else if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
701 switch (prior->fDeviceSpaceType) {
703 SkDEBUGCODE(prior->checkEmpty();)
704 return;
705 case Element::DeviceSpaceType::kShader:
706 if (Element::DeviceSpaceType::kShader == element.getDeviceSpaceType()) {
707 prior->fShader = SkShaders::Blend(SkBlendMode::kSrcIn,
708 element.fShader, prior->fShader);
709 Element* priorPrior = (Element*) iter.prev();
710 prior->updateBoundAndGenID(priorPrior);
711 return;
712 }
713 break;
715 if (Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) {
716 if (prior->rectRectIntersectAllowed(element.getDeviceSpaceRect(),
717 element.isAA())) {
718 SkRect isectRect;
719 if (!isectRect.intersect(prior->getDeviceSpaceRect(),
720 element.getDeviceSpaceRect())) {
721 prior->setEmpty();
722 return;
723 }
724
725 prior->fDeviceSpaceRRect.setRect(isectRect);
726 prior->fDoAA = element.isAA();
727 Element* priorPrior = (Element*) iter.prev();
728 prior->updateBoundAndGenID(priorPrior);
729 return;
730 }
731 break;
732 }
733 [[fallthrough]];
734 default:
735 if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
736 prior->setEmpty();
737 return;
738 }
739 break;
740 }
741 }
742 }
743 Element* newElement = new (fDeque.push_back()) Element(element);
744 newElement->updateBoundAndGenID(prior);
745}
746
747void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
748 Element element(fSaveCount, rrect, matrix, op, doAA);
749 this->pushElement(element);
750}
751
752void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
753 Element element(fSaveCount, rect, matrix, op, doAA);
754 this->pushElement(element);
755}
756
758 bool doAA) {
759 Element element(fSaveCount, path, matrix, op, doAA);
760 this->pushElement(element);
761}
762
764 Element element(fSaveCount, std::move(shader));
765 this->pushElement(element);
766}
767
768void SkClipStack::replaceClip(const SkRect& rect, bool doAA) {
769 Element element(fSaveCount, rect, doAA);
770 this->pushElement(element);
771}
772
774 Element* element = (Element*) fDeque.back();
775
776 if (element && element->canBeIntersectedInPlace(fSaveCount, SkClipOp::kIntersect)) {
777 element->setEmpty();
778 }
779 new (fDeque.push_back()) Element(fSaveCount);
780
781 ((Element*)fDeque.back())->fGenID = kEmptyGenID;
782}
783
784///////////////////////////////////////////////////////////////////////////////
785
786SkClipStack::Iter::Iter() : fStack(nullptr) {
787}
788
790 : fStack(&stack) {
791 this->reset(stack, startLoc);
792}
793
795 return (const SkClipStack::Element*)fIter.next();
796}
797
799 return (const SkClipStack::Element*)fIter.prev();
800}
801
803 if (nullptr == fStack) {
804 return nullptr;
805 }
806
807 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
808
809 const SkClipStack::Element* element = nullptr;
810
811 for (element = (const SkClipStack::Element*) fIter.prev();
812 element;
813 element = (const SkClipStack::Element*) fIter.prev()) {
814
815 if (op == element->fOp) {
816 // The Deque's iterator is actually one pace ahead of the
817 // returned value. So while "element" is the element we want to
818 // return, the iterator is actually pointing at (and will
819 // return on the next "next" or "prev" call) the element
820 // in front of it in the deque. Bump the iterator forward a
821 // step so we get the expected result.
822 if (nullptr == fIter.next()) {
823 // The reverse iterator has run off the front of the deque
824 // (i.e., the "op" clip is the first clip) and can't
825 // recover. Reset the iterator to start at the front.
826 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
827 }
828 break;
829 }
830 }
831
832 if (nullptr == element) {
833 // There were no "op" clips
834 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
835 }
836
837 return this->next();
838}
839
840void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
841 fStack = &stack;
842 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
843}
844
845// helper method
847 int offsetY,
848 int maxWidth,
849 int maxHeight,
850 SkRect* devBounds,
851 bool* isIntersectionOfRects) const {
852 SkASSERT(devBounds);
853
854 devBounds->setLTRB(0, 0,
855 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
856
857 SkRect temp;
858 SkClipStack::BoundsType boundType;
859
860 // temp starts off in canvas space here
861 this->getBounds(&temp, &boundType, isIntersectionOfRects);
862 if (SkClipStack::kInsideOut_BoundsType == boundType) {
863 return;
864 }
865
866 // but is converted to device space here
868
869 if (!devBounds->intersect(temp)) {
870 devBounds->setEmpty();
871 }
872}
873
874bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const {
875 const Element* back = static_cast<const Element*>(fDeque.back());
876 if (!back) {
877 // TODO: return bounds?
878 return false;
879 }
880 // First check if the entire stack is known to be a rect by the top element.
881 if (back->fIsIntersectionOfRects && back->fFiniteBoundType == BoundsType::kNormal_BoundsType) {
882 rrect->setRect(back->fFiniteBound);
883 *aa = back->isAA();
884 return true;
885 }
886
889 return false;
890 }
891 if (back->isReplaceOp()) {
892 *rrect = back->asDeviceSpaceRRect();
893 *aa = back->isAA();
894 return true;
895 }
896
897 if (back->getOp() == SkClipOp::kIntersect) {
898 SkRect backBounds;
899 if (!backBounds.intersect(bounds, back->asDeviceSpaceRRect().rect())) {
900 return false;
901 }
902 // We limit to 17 elements. This means the back element will be bounds checked at most 16
903 // times if it is an rrect.
904 int cnt = fDeque.count();
905 if (cnt > 17) {
906 return false;
907 }
908 if (cnt > 1) {
910 SkAssertResult(static_cast<const Element*>(iter.prev()) == back);
911 while (const Element* prior = (const Element*)iter.prev()) {
912 // TODO: Once expanding clip ops are removed, this is equiv. to op == kDifference
913 if ((prior->getOp() != SkClipOp::kIntersect && !prior->isReplaceOp()) ||
914 !prior->contains(backBounds)) {
915 return false;
916 }
917 if (prior->isReplaceOp()) {
918 break;
919 }
920 }
921 }
922 *rrect = back->asDeviceSpaceRRect();
923 *aa = back->isAA();
924 return true;
925 }
926 return false;
927}
928
929uint32_t SkClipStack::GetNextGenID() {
930 // 0-2 are reserved for invalid, empty & wide-open
931 static const uint32_t kFirstUnreservedGenID = 3;
932 static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
933
934 uint32_t id;
935 do {
936 id = nextID.fetch_add(1, std::memory_order_relaxed);
937 } while (id < kFirstUnreservedGenID);
938 return id;
939}
940
942 if (fDeque.empty()) {
943 return kWideOpenGenID;
944 }
945
946 const Element* back = static_cast<const Element*>(fDeque.back());
947 if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty() &&
948 Element::DeviceSpaceType::kShader != back->fDeviceSpaceType) {
949 return kWideOpenGenID;
950 }
951
952 return back->getGenID();
953}
954
955#ifdef SK_DEBUG
956void SkClipStack::Element::dump() const {
957 static const char* kTypeStrings[] = {
958 "empty",
959 "rect",
960 "rrect",
961 "path",
962 "shader"
963 };
964 static_assert(0 == static_cast<int>(DeviceSpaceType::kEmpty), "enum mismatch");
965 static_assert(1 == static_cast<int>(DeviceSpaceType::kRect), "enum mismatch");
966 static_assert(2 == static_cast<int>(DeviceSpaceType::kRRect), "enum mismatch");
967 static_assert(3 == static_cast<int>(DeviceSpaceType::kPath), "enum mismatch");
968 static_assert(4 == static_cast<int>(DeviceSpaceType::kShader), "enum mismatch");
969 static_assert(std::size(kTypeStrings) == kTypeCnt, "enum mismatch");
970
971 const char* opName = this->isReplaceOp() ? "replace" :
972 (fOp == SkClipOp::kDifference ? "difference" : "intersect");
973 SkDebugf("Type: %s, Op: %s, AA: %s, Save Count: %d\n", kTypeStrings[(int)fDeviceSpaceType],
974 opName, (fDoAA ? "yes" : "no"), fSaveCount);
975 switch (fDeviceSpaceType) {
977 SkDebugf("\n");
978 break;
980 this->getDeviceSpaceRect().dump();
981 SkDebugf("\n");
982 break;
984 this->getDeviceSpaceRRect().dump();
985 SkDebugf("\n");
986 break;
988 this->getDeviceSpacePath().dump(nullptr, false);
989 break;
990 case DeviceSpaceType::kShader:
991 // SkShaders don't provide much introspection that's worth while.
992 break;
993 }
994}
995
996void SkClipStack::dump() const {
997 B2TIter iter(*this);
998 const Element* e;
999 while ((e = iter.next())) {
1000 e->dump();
1001 SkDebugf("\n");
1002 }
1003}
1004#endif
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
std::unique_ptr< SkLatticeIter > fIter
Definition: LatticeOp.cpp:380
static float next(float f)
@ kInfinite
Definition: ScalarTest.cpp:124
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kSrcIn
r = s * da
SkClipOp
Definition: SkClipOp.h:13
static const int kDefaultElementAllocCnt
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
@ kRRect
#define SkIntToScalar(x)
Definition: SkScalar.h:57
SkShaderBase * as_SB(SkShader *shader)
Definition: SkShaderBase.h:412
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Definition: SkYUVMath.cpp:629
GLenum type
const SkShader * getShader() const
Definition: SkClipStack.h:130
@ kPath
This element does not have geometry, but applies a shader to the clip.
@ kEmpty
This element makes the clip empty (regardless of previous elements).
@ kRect
This element combines a device space round-rect with the current clip.
@ kRRect
This element combines a device space path with the current clip.
SkClipOp getOp() const
Definition: SkClipStack.h:136
uint32_t getGenID() const
Definition: SkClipStack.h:161
const SkRect & getDeviceSpaceRect() const
Call if getDeviceSpaceType() is kShader to get a reference to the clip shader.
Definition: SkClipStack.h:120
bool isAA() const
Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
Definition: SkClipStack.h:151
DeviceSpaceType getDeviceSpaceType() const
Call to get the save count associated with this clip element.
Definition: SkClipStack.h:102
const SkPath & getDeviceSpacePath() const
Call if getDeviceSpaceType() is kRRect to get the round-rect.
Definition: SkClipStack.h:108
bool operator==(const Element &element) const
Definition: SkClipStack.cpp:58
const SkRRect & asDeviceSpaceRRect() const
Definition: SkClipStack.h:144
const SkRect & getBounds() const
Definition: SkClipStack.cpp:84
bool contains(const SkRect &rect) const
bool isReplaceOp() const
Call to get the element as a path, regardless of its type.
Definition: SkClipStack.h:138
void asDeviceSpacePath(SkPath *path) const
Call if getType() is not kPath to get the element as a round rect.
void reset(const SkClipStack &stack, IterStart startLoc)
const Element * skipToTopmost(SkClipOp op)
const Element * next()
const Element * prev()
@ kNormal_BoundsType
Definition: SkClipStack.h:37
@ kInsideOut_BoundsType
Definition: SkClipStack.h:43
void clipRect(const SkRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
void clipShader(sk_sp< SkShader >)
bool isRRect(const SkRect &bounds, SkRRect *rrect, bool *aa) const
static const uint32_t kEmptyGenID
Definition: SkClipStack.h:386
friend class Iter
Definition: SkClipStack.h:489
void clipRRect(const SkRRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
void getBounds(SkRect *canvFiniteBound, BoundsType *boundType, bool *isIntersectionOfRects=nullptr) const
bool isEmpty(const SkIRect &deviceBounds) const
void getConservativeBounds(int offsetX, int offsetY, int maxWidth, int maxHeight, SkRect *devBounds, bool *isIntersectionOfRects=nullptr) const
void clipEmpty()
bool operator==(const SkClipStack &b) const
SkClipStack & operator=(const SkClipStack &b)
uint32_t getTopmostGenID() const
static const uint32_t kInvalidGenID
Definition: SkClipStack.h:383
static const uint32_t kWideOpenGenID
Definition: SkClipStack.h:387
void restore()
void clipPath(const SkPath &, const SkMatrix &matrix, SkClipOp, bool doAA)
SkRect bounds(const SkIRect &deviceBounds) const
void replaceClip(const SkRect &devRect, bool doAA)
void * next()
Definition: SkDeque.cpp:251
void * prev()
Definition: SkDeque.cpp:270
@ kBack_IterStart
Definition: SkDeque.h:66
@ kFront_IterStart
Definition: SkDeque.h:65
void * push_back()
Definition: SkDeque.cpp:112
bool empty() const
Definition: SkDeque.h:38
void pop_back()
Definition: SkDeque.cpp:187
const void * back() const
Definition: SkDeque.h:43
int count() const
Definition: SkDeque.h:39
Definition: SkPath.h:59
const SkRect & rect() const
Definition: SkRRect.h:264
@ kEmpty_Type
zero width or height
Definition: SkRRect.h:67
@ kRect_Type
non-zero width and height, and zeroed radii
Definition: SkRRect.h:68
void setEmpty()
Definition: SkRRect.h:118
bool transform(const SkMatrix &matrix, SkRRect *dst) const
Definition: SkRRect.cpp:436
void setOval(const SkRect &oval)
Definition: SkRRect.cpp:30
const SkRect & getBounds() const
Definition: SkRRect.h:279
void setRect(const SkRect &rect)
Definition: SkRRect.h:126
static SkRect MakeLargeS32()
Definition: SkRectPriv.h:33
sk_sp< SkShader > makeInvertAlpha() const
void reset()
Definition: SkTLazy.h:69
T * set(const T &src)
Definition: SkTLazy.h:56
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
static bool b
if(end==-1)
Definition: dart.idl:42
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkShader > Blend(SkBlendMode mode, sk_sp< SkShader > dst, sk_sp< SkShader > src)
constexpr std::array< std::array< float, 2 >, 2 > kRect
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
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
@ kPath
Definition: SkGlyph.h:317
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
SkScalar offsetX
SkScalar offsetY
Definition: SkRect.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
void offset(float dx, float dy)
Definition: SkRect.h:1016
void setLTRB(float left, float top, float right, float bottom)
Definition: SkRect.h:865
bool isEmpty() const
Definition: SkRect.h:693
void setEmpty()
Definition: SkRect.h:842
const uintptr_t id