SkClipStack::isRRect succeeds if stack is an intersection of rects.
This helps enable optimizations that rely on the entire clip reducing
to a single rectangle, such as converting a drawRect to a clear.
Bug: b/122296071
Change-Id: I1fea4565644280d57610f8585b76babb557301d8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/209382
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index cc2c187..54ef867 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -960,13 +960,18 @@
}
bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const {
- // We limit to 5 elements. This means the back element will be bounds checked at most 4 times if
- // it is an rrect.
- int cnt = fDeque.count();
- if (!cnt || cnt > 5) {
+ const Element* back = static_cast<const Element*>(fDeque.back());
+ if (!back) {
+ // TODO: return bounds?
return false;
}
- const Element* back = static_cast<const Element*>(fDeque.back());
+ // First check if the entire stack is known to be a rect by the top element.
+ if (back->fIsIntersectionOfRects && back->fFiniteBoundType == BoundsType::kNormal_BoundsType) {
+ rrect->setRect(back->fFiniteBound);
+ *aa = back->isAA();
+ return true;
+ }
+
if (back->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kRect &&
back->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kRRect) {
return false;
@@ -982,6 +987,12 @@
if (!backBounds.intersect(bounds, back->asDeviceSpaceRRect().rect())) {
return false;
}
+ // We limit to 5 elements. This means the back element will be bounds checked at most 4
+ // times if it is an rrect.
+ int cnt = fDeque.count();
+ if (cnt > 5) {
+ return false;
+ }
if (cnt > 1) {
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
SkAssertResult(static_cast<const Element*>(iter.prev()) == back);
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 0c3f3b2..c95bdc7 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -1425,6 +1425,41 @@
}
}
+static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
+ static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
+ // All antialiased or all not antialiased.
+ for (bool aa : {false, true}) {
+ SkClipStack stack;
+ for (int i = 0; i <= 100; ++i) {
+ stack.save();
+ stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
+ SkMatrix::I(), SkClipOp::kIntersect, aa);
+ }
+ SkRRect rrect;
+ bool isAA;
+ SkRRect expected = SkRRect::MakeRect(
+ SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
+ if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
+ REPORTER_ASSERT(reporter, rrect == expected);
+ REPORTER_ASSERT(reporter, aa == isAA);
+ } else {
+ ERRORF(reporter, "Expected to be an rrect.");
+ }
+ }
+ // Mixed AA and non-AA without simple containment.
+ SkClipStack stack;
+ for (int i = 0; i <= 100; ++i) {
+ bool aa = i & 0b1;
+ int j = 100 - i;
+ stack.save();
+ stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
+ SkMatrix::I(), SkClipOp::kIntersect, aa);
+ }
+ SkRRect rrect;
+ bool isAA;
+ REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
+}
+
DEF_TEST(ClipStack, reporter) {
SkClipStack stack;
@@ -1477,6 +1512,7 @@
test_reduced_clip_stack_no_aa_crash(reporter);
test_reduced_clip_stack_aa(reporter);
test_tiny_query_bounds_assertion_bug(reporter);
+ test_is_rrect_deep_rect_stack(reporter);
}
//////////////////////////////////////////////////////////////////////////////