Combine multiple intersecting rects in SkClipStack::Iter.

R=robertphillips@google.com
Review URL: https://codereview.appspot.com/6816104

git-svn-id: http://skia.googlecode.com/svn/trunk@6339 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 8a74dc8..0516d9e 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -473,6 +473,173 @@
     }
 }
 
+
+// This is similar to the above test but tests the iterator's ability to merge rects in the
+// middle of a clip stack's sequence using nextCombined(). There is a save after every clip
+// element to prevent the clip stack from merging the rectangles as they are added.
+static void test_iter_rect_merging(skiatest::Reporter* reporter) {
+
+    SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
+    SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
+
+    SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
+    SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
+
+    SkRect farAway      = SkRect::MakeLTRB(1000, 1000, 1010, 1010);
+
+    SkRect overlapIntersect;
+    overlapIntersect.intersect(overlapLeft, overlapRight);
+
+    SkPath path1, path2;
+    path1.addCircle(SkIntToScalar(30), SkIntToScalar(30), SkIntToScalar(1000));
+    path2.addOval(SkRect::MakeWH(500, 600));
+
+    const SkClipStack::Iter::Clip* clip;
+
+    // call nextCombined with an empty clip stack
+    {
+        SkClipStack stack;
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+        REPORTER_ASSERT(reporter, NULL == iter.nextCombined());
+    }
+
+    // two bw overlapping - should merge, bracketed by paths
+    {
+        SkClipStack stack;
+        stack.clipDevPath(path1, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevPath(path2, SkRegion::kIntersect_Op, false); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, *clip->fPath == path1 && !clip->fDoAA);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == overlapIntersect);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, *clip->fPath == path2 && !clip->fDoAA);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // same as above but rects are aa and no final path.
+    {
+        SkClipStack stack;
+        stack.clipDevPath(path1, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
+
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, *clip->fPath == path1 && !clip->fDoAA);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapIntersect);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // mixed overlapping - no paths - should _not_ merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapLeft);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == overlapRight);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // three rects in a row where the third rect uses a non-intersect op.
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); stack.save();
+        stack.clipDevRect(nestedParent, SkRegion::kXOR_Op, true); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapIntersect);
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == nestedParent);
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // mixed nested (bw inside aa) - should merge
+    {
+        SkClipStack stack;
+        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+        
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == nestedChild);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // mixed nested (aa inside bw) - should merge
+    {
+        SkClipStack stack;
+        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); stack.save();
+
+        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+        
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == nestedChild);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+
+    // three rect intersects in a row where result is empty after the second.
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, false); stack.save();
+        stack.clipDevRect(farAway, SkRegion::kIntersect_Op, false); stack.save();
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, clip->fRect->isEmpty());
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, *clip->fRect == overlapRight);
+
+        clip = iter.nextCombined();
+        REPORTER_ASSERT(reporter, NULL == clip);
+    }
+}
+
 static void TestClipStack(skiatest::Reporter* reporter) {
     SkClipStack stack;
 
@@ -513,6 +680,7 @@
     test_bounds(reporter, false);       // once with paths
     test_isWideOpen(reporter);
     test_rect_merging(reporter);
+    test_iter_rect_merging(reporter);
 }
 
 #include "TestClassDef.h"