Adding quickContains API method to SkClipStack
BUG=http://code.google.com/p/chromium/issues/detail?id=164580
TEST=unit test ClipStack/quickContains
Review URL: https://codereview.appspot.com/6919044
git-svn-id: http://skia.googlecode.com/svn/trunk@6760 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index 52b1b22..b39c5af 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -292,6 +292,13 @@
*/
bool intersectRectWithClip(SkRect* devRect) const;
+ /**
+ * Returns true if the input rect in device space is entirely contained
+ * by the clip. A return value of false does not guarantee that the rect
+ * is not contained by the clip.
+ */
+ bool quickContains(const SkRect& devRect) const;
+
void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
SkRect r;
r.set(ir);
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index bb4ea08..9afef3f 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -497,6 +497,31 @@
}
}
+bool SkClipStack::quickContains(const SkRect& rect) const {
+
+ Iter iter(*this, Iter::kTop_IterStart);
+ const Element* element = iter.prev();
+ while (element != NULL) {
+ if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
+ return false;
+ if (element->isInverseFilled()) {
+ // Part of 'rect' could be trimmed off by the inverse-filled clip element
+ if (SkRect::Intersects(element->getBounds(), rect)) {
+ return false;
+ }
+ } else {
+ if (!element->contains(rect)) {
+ return false;
+ }
+ }
+ if (SkRegion::kReplace_Op == element->getOp()) {
+ break;
+ }
+ element = iter.prev();
+ }
+ return true;
+}
+
void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
// Use reverse iterator instead of back because Rect path may need previous
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 03da7f1..5e5c87f 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -497,6 +497,184 @@
}
}
+static void test_quickContains(skiatest::Reporter* reporter) {
+ SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
+ SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
+ SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
+ SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
+ SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
+
+ SkPath insideCircle;
+ insideCircle.addCircle(25, 25, 5);
+ SkPath intersectingCircle;
+ intersectingCircle.addCircle(25, 40, 10);
+ SkPath outsideCircle;
+ outsideCircle.addCircle(25, 25, 50);
+ SkPath nonIntersectingCircle;
+ nonIntersectingCircle.addCircle(100, 100, 5);
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
+ // return false because quickContains currently does not care for kDifference_Op
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ // Replace Op tests
+ {
+ SkClipStack stack;
+ stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+ stack.save(); // To prevent in-place substitution by replace OP
+ stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ stack.restore();
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+ stack.save(); // To prevent in-place substitution by replace OP
+ stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ stack.restore();
+ }
+
+ // Verify proper traversal of multi-element clip
+ {
+ SkClipStack stack;
+ stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+ // Use a path for second clip to prevent in-place intersection
+ stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ // Intersect Op tests with rectangles
+ {
+ SkClipStack stack;
+ stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ // Intersect Op tests with circle paths
+ {
+ SkClipStack stack;
+ stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ // Intersect Op tests with inverse filled rectangles
+ {
+ SkClipStack stack;
+ SkPath path;
+ path.addRect(outsideRect);
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path;
+ path.addRect(insideRect);
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path;
+ path.addRect(intersectingRect);
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path;
+ path.addRect(nonIntersectingRect);
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ }
+
+ // Intersect Op tests with inverse filled circles
+ {
+ SkClipStack stack;
+ SkPath path = outsideCircle;
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path = insideCircle;
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path = intersectingCircle;
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+ }
+
+ {
+ SkClipStack stack;
+ SkPath path = nonIntersectingCircle;
+ path.toggleInverseFillType();
+ stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+ REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU
@@ -773,6 +951,7 @@
test_isWideOpen(reporter);
test_rect_merging(reporter);
test_rect_inverse_fill(reporter);
+ test_quickContains(reporter);
#if SK_SUPPORT_GPU
test_reduced_clip_stack(reporter);
#endif