Added isIntersectionOfRects to SkClipStack

http://codereview.appspot.com/6434050/



git-svn-id: http://skia.googlecode.com/svn/trunk@4745 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index fbdcb36..71b68ce 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -14,6 +14,12 @@
 struct SkRect;
 class SkPath;
 
+// Because a single save/restore state can have multiple clips, this class
+// stores the stack depth (fSaveCount) and clips (fDeque) separately.
+// Each clip in fDeque stores the stack state to which it belongs
+// (i.e., the fSaveCount in force when it was added). Restores are thus
+// implemented by removing clips from fDeque that have an fSaveCount larger
+// then the freshly decremented count.
 class SK_API SkClipStack {
 public:
     SkClipStack();
@@ -44,12 +50,15 @@
     /**
      * getBounds places the current finite bound in its first parameter. In its
      * second, it indicates which kind of bound is being returned. If 
-     * 'finiteBound' is a normal bounding box then it encloses are writeable
+     * 'finiteBound' is a normal bounding box then it encloses all writeable
      * pixels. If 'finiteBound' is an inside out bounding box then it 
      * encloses all the un-writeable pixels and the true/normal bound is the
-     * infinite plane.
+     * infinite plane. isIntersectionOfRects is an optional parameter
+     * that is true if 'finiteBound' resulted from an intersection of rects.
      */
-    void getBounds(SkRect* finiteBound, BoundsType* boundType) const;
+    void getBounds(SkRect* finiteBound, 
+                   BoundsType* boundType,
+                   bool* isIntersectionOfRects = NULL) const;
 
     void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
         SkRect r;
@@ -165,12 +174,15 @@
      * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
      * the translation (+offsetX, +offsetY) is applied before the clamp to the 
      * maximum rectangle: [0,maxWidth) x [0,maxHeight).
+     * isIntersectionOfRects is an optional parameter that is true when 
+     * 'bounds' is the result of an intersection of rects.
      */
     void getConservativeBounds(int offsetX,
                                int offsetY,
                                int maxWidth,
                                int maxHeight,
-                               SkRect* bounds) const;
+                               SkRect* bounds,
+                               bool* isIntersectionOfRects = NULL) const;
 
 private:
     friend class Iter;
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 2676f3b..7857bdf 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -38,13 +38,15 @@
     // of the extensions to infinity when two inverse filled clips are
     // Booleaned together.
     SkClipStack::BoundsType fFiniteBoundType;
-    SkRect                 fFiniteBound;
+    SkRect                  fFiniteBound;
+    bool                    fIsIntersectionOfRects;
 
     Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
         fSaveCount = saveCount;
         fOp = op;
         fState = kRect_State;
         fDoAA = doAA;
+        // bounding box members are updated in a following updateBound call
     }
 
     Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
@@ -53,6 +55,7 @@
         fOp = op;
         fState = kPath_State;
         fDoAA = doAA;
+        // bounding box members are updated in a following updateBound call
     }
 
     bool operator==(const Rec& b) const {
@@ -273,9 +276,17 @@
 
         // First, optimistically update the current Rec's bound information 
         // with the current clip's bound
+        fIsIntersectionOfRects = false;
         if (kRect_State == fState) {
             fFiniteBound = fRect;
             fFiniteBoundType = kNormal_BoundsType;
+
+            if (SkRegion::kReplace_Op == fOp ||
+                (SkRegion::kIntersect_Op == fOp && NULL == prior) || 
+                (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects)) {
+                fIsIntersectionOfRects = true;
+            }
+
         } else {
             fFiniteBound = fPath.getBounds();
 
@@ -432,7 +443,9 @@
     }
 }
 
-void SkClipStack::getBounds(SkRect* finiteBound, BoundsType* boundType) const {
+void SkClipStack::getBounds(SkRect* finiteBound, 
+                            BoundsType* boundType,
+                            bool* isIntersectionOfRects) const {
     SkASSERT(NULL != finiteBound && NULL != boundType);
 
     Rec* rec = (Rec*)fDeque.back();
@@ -441,36 +454,50 @@
         // the clip is wide open - the infinite plane w/ no pixels un-writeable
         finiteBound->setEmpty();
         *boundType = kInsideOut_BoundsType;
+        if (NULL != isIntersectionOfRects) {
+            *isIntersectionOfRects = false;
+        }
         return;
     }
 
     *finiteBound = rec->fFiniteBound;
     *boundType = rec->fFiniteBoundType;
+    if (NULL != isIntersectionOfRects) {
+        *isIntersectionOfRects = rec->fIsIntersectionOfRects;
+    }
 }
 
 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
-    Rec* rec = (Rec*)fDeque.back();
+
+    SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
+    Rec* rec = (Rec*) iter.prev();
+
     if (rec && rec->canBeIntersected(fSaveCount, op)) {
         switch (rec->fState) {
             case Rec::kEmpty_State:
                 SkASSERT(rec->fFiniteBound.isEmpty());
                 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
+                SkASSERT(!rec->fIsIntersectionOfRects);
                 return;
-            case Rec::kRect_State:
+            case Rec::kRect_State: {
                 if (!rec->fRect.intersect(rect)) {
                     rec->fState = Rec::kEmpty_State;
                     rec->fFiniteBound.setEmpty();
                     rec->fFiniteBoundType = kNormal_BoundsType;
+                    rec->fIsIntersectionOfRects = false;
                     return;
                 }
 
-                rec->updateBound(NULL);
+                Rec* prev = (Rec*) iter.prev();
+                rec->updateBound(prev);
                 return;
+            }
             case Rec::kPath_State:
                 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
                     rec->fState = Rec::kEmpty_State;
                     rec->fFiniteBound.setEmpty();
                     rec->fFiniteBoundType = kNormal_BoundsType;
+                    rec->fIsIntersectionOfRects = false;
                     return;
                 }
                 break;
@@ -492,12 +519,14 @@
             case Rec::kEmpty_State:
                 SkASSERT(rec->fFiniteBound.isEmpty());
                 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
+                SkASSERT(!rec->fIsIntersectionOfRects);
                 return;
             case Rec::kRect_State:
                 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
                     rec->fState = Rec::kEmpty_State;
                     rec->fFiniteBound.setEmpty();
                     rec->fFiniteBoundType = kNormal_BoundsType;
+                    rec->fIsIntersectionOfRects = false;
                     return;
                 }
                 break;
@@ -506,6 +535,7 @@
                     rec->fState = Rec::kEmpty_State;
                     rec->fFiniteBound.setEmpty();
                     rec->fFiniteBoundType = kNormal_BoundsType;
+                    rec->fIsIntersectionOfRects = false;
                     return;
                 }
                 break;
@@ -627,7 +657,8 @@
                                         int offsetY,
                                         int maxWidth,
                                         int maxHeight,
-                                        SkRect* bounds) const {
+                                        SkRect* bounds,
+                                        bool* isIntersectionOfRects) const {
     SkASSERT(NULL != bounds);
 
     bounds->setLTRB(0, 0, 
@@ -636,7 +667,7 @@
     SkRect temp;
     SkClipStack::BoundsType boundType;
     
-    this->getBounds(&temp, &boundType);
+    this->getBounds(&temp, &boundType, isIntersectionOfRects);
     if (SkClipStack::kInsideOut_BoundsType == boundType) {
         return;
     }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index c92af64..57d9fbd 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -409,14 +409,19 @@
 #endif
 
     SkRect bounds;
+    bool isIntersectionOfRects = false;
     clipStack.getConservativeBounds(-origin.fX,
                                     -origin.fY,
                                     renderTargetWidth,
                                     renderTargetHeight,
-                                    &bounds);
+                                    &bounds,
+                                    &isIntersectionOfRects);
 
     GrClip grc(&iter, GrIntToScalar(-origin.x()), GrIntToScalar(-origin.y()),
                bounds);
+
+    GrAssert(grc.isRect() == isIntersectionOfRects);
+
     context->setClip(grc);
 }
 
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index e5001f1..02fa8cf 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -183,7 +183,7 @@
     }
 }
 
-static void test_bounds(skiatest::Reporter* reporter) {
+static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
 
     static const int gNumCases = 20;
     static const SkRect gAnswerRectsBW[gNumCases] = {
@@ -236,9 +236,11 @@
 
     SkClipStack stack;
     SkRect bound;
+    bool isIntersectionOfRects = false;
 
     int testCase = 0;
-    for (int invBits = 0; invBits < 4; ++invBits) {
+    int numBitTests = useRects ? 1 : 4;
+    for (int invBits = 0; invBits < numBitTests; ++invBits) {
         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
 
             stack.save();
@@ -250,13 +252,26 @@
             clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
                                        SkPath::kEvenOdd_FillType);
 
-            stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
-            stack.clipDevPath(clipB, gOps[op], false);
+            if (useRects) {
+                stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
+                stack.clipDevRect(rectB, gOps[op], false);
+            } else {
+                stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
+                stack.clipDevPath(clipB, gOps[op], false);
+            }
 
-            stack.getConservativeBounds(0, 0, 100, 100, &bound);
+            stack.getConservativeBounds(0, 0, 100, 100, &bound,
+                                        &isIntersectionOfRects);
+
+            if (useRects) {
+                REPORTER_ASSERT(reporter, isIntersectionOfRects == 
+                        (gOps[op] == SkRegion::kIntersect_Op));
+            } else {
+                REPORTER_ASSERT(reporter, !isIntersectionOfRects);
+            }
 
             SkASSERT(testCase < gNumCases);
-            SkASSERT(bound == gAnswerRectsBW[testCase]);
+            REPORTER_ASSERT(reporter, bound == gAnswerRectsBW[testCase]);
             ++testCase;
 
             stack.restore();
@@ -300,7 +315,8 @@
 
     test_assign_and_comparison(reporter);
     test_iterators(reporter);
-    test_bounds(reporter);
+    test_bounds(reporter, true);
+    test_bounds(reporter, false);
 }
 
 #include "TestClassDef.h"