Store SkRRects in SkClipStack

BUG=skia:2181
R=robertphillips@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/163683002

git-svn-id: http://skia.googlecode.com/svn/trunk@13465 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index c66a219..17eb6f9 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -16,17 +16,89 @@
 static const int32_t kFirstUnreservedGenID = 3;
 int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
 
+bool SkClipStack::Element::operator== (const Element& element) const {
+    if (this == &element) {
+        return true;
+    }
+    if (fOp != element.fOp ||
+        fType != element.fType ||
+        fDoAA != element.fDoAA ||
+        fSaveCount != element.fSaveCount) {
+        return false;
+    }
+    switch (fType) {
+        case kPath_Type:
+            return fPath == element.fPath;
+        case kRRect_Type:
+            return fRRect == element.fRRect;
+        case kRect_Type:
+            return fRect == element.fRect;
+        case kEmpty_Type:
+            return true;
+        default:
+            SkDEBUGFAIL("Unexpected type.");
+            return false;
+    }
+}
+
 void SkClipStack::Element::invertShapeFillType() {
     switch (fType) {
         case kRect_Type:
             fPath.reset();
             fPath.addRect(fRect);
-            fPath.setFillType(SkPath::kInverseWinding_FillType);
+            fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
+            fType = kPath_Type;
+            break;
+        case kRRect_Type:
+            fPath.reset();
+            fPath.addRRect(fRRect);
+            fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
             fType = kPath_Type;
             break;
         case kPath_Type:
             fPath.toggleInverseFillType();
+            break;
         case kEmpty_Type:
+            // Should this set to an empty, inverse filled path?
+            break;
+    }
+}
+
+void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion::Op op,
+                                    bool doAA) {
+    if (!path.isInverseFillType()) {
+        if (SkPath::kNone_PathAsRect != path.asRect()) {
+            this->initRect(saveCount, path.getBounds(), op, doAA);
+            return;
+        }
+        SkRect ovalRect;
+        if (path.isOval(&ovalRect)) {
+            SkRRect rrect;
+            rrect.setOval(ovalRect);
+            this->initRRect(saveCount, rrect, op, doAA);
+            return;
+        }
+    }
+    fPath = path;
+    fType = kPath_Type;
+    this->initCommon(saveCount, op, doAA);
+}
+
+void SkClipStack::Element::asPath(SkPath* path) const {
+    switch (fType) {
+        case kEmpty_Type:
+            path->reset();
+            break;
+        case kRect_Type:
+            path->reset();
+            path->addRect(fRect);
+            break;
+        case kRRect_Type:
+            path->reset();
+            path->addRRect(fRRect);
+            break;
+        case kPath_Type:
+            *path = fPath;
             break;
     }
 }
@@ -100,10 +172,10 @@
             // is erased, so the only pixels that can remain set
             // occur w/in the intersection of the two finite bounds
             if (!fFiniteBound.intersect(prevFinite)) {
-                fFiniteBound.setEmpty();
-                fGenID = kEmptyGenID;
+                this->setEmpty();
+            } else {
+                fFiniteBoundType = kNormal_BoundsType;
             }
-            fFiniteBoundType = kNormal_BoundsType;
             break;
         case kPrev_Cur_FillCombo:
             // The most conservative result bound is that of the
@@ -205,8 +277,7 @@
             break;
         case kPrev_Cur_FillCombo:
             if (!fFiniteBound.intersect(prevFinite)) {
-                fFiniteBound.setEmpty();
-                fGenID = kEmptyGenID;
+                this->setEmpty();
             }
             break;
         default:
@@ -228,10 +299,10 @@
             break;
         case kInvPrev_Cur_FillCombo:
             if (!fFiniteBound.intersect(prevFinite)) {
-                fFiniteBound.setEmpty();
-                fGenID = kEmptyGenID;
+                this->setEmpty();
+            } else {
+                fFiniteBoundType = kNormal_BoundsType;
             }
-            fFiniteBoundType = kNormal_BoundsType;
             break;
         case kPrev_InvCur_FillCombo:
             fFiniteBound.join(prevFinite);
@@ -258,27 +329,34 @@
     // First, optimistically update the current Element's bound information
     // with the current clip's bound
     fIsIntersectionOfRects = false;
-    if (kRect_Type == fType) {
-        fFiniteBound = fRect;
-        fFiniteBoundType = kNormal_BoundsType;
-
-        if (SkRegion::kReplace_Op == fOp ||
-            (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
-            (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
-                prior->rectRectIntersectAllowed(fRect, fDoAA))) {
-            fIsIntersectionOfRects = true;
-        }
-
-    } else {
-        SkASSERT(kPath_Type == fType);
-
-        fFiniteBound = fPath.getBounds();
-
-        if (fPath.isInverseFillType()) {
-            fFiniteBoundType = kInsideOut_BoundsType;
-        } else {
+    switch (fType) {
+        case kRect_Type:
+            fFiniteBound = fRect;
             fFiniteBoundType = kNormal_BoundsType;
-        }
+
+            if (SkRegion::kReplace_Op == fOp ||
+                (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
+                (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
+                    prior->rectRectIntersectAllowed(fRect, fDoAA))) {
+                fIsIntersectionOfRects = true;
+            }
+            break;
+        case kRRect_Type:
+            fFiniteBound = fRRect.getBounds();
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kPath_Type:
+            fFiniteBound = fPath.getBounds();
+
+            if (fPath.isInverseFillType()) {
+                fFiniteBoundType = kInsideOut_BoundsType;
+            } else {
+                fFiniteBoundType = kNormal_BoundsType;
+            }
+            break;
+        case kEmpty_Type:
+            SkDEBUGFAIL("We shouldn't get here with an empty element.");
+            break;
     }
 
     if (!fDoAA) {
@@ -344,7 +422,7 @@
             // so nothing to do
             break;
         default:
-            SkDebugf("SkRegion::Op error/n");
+            SkDebugf("SkRegion::Op error\n");
             SkASSERT(0);
             break;
     }
@@ -528,97 +606,69 @@
     return true;
 }
 
-void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
-
+void SkClipStack::pushElement(const Element& element) {
     // Use reverse iterator instead of back because Rect path may need previous
     SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
-    Element* element = (Element*) iter.prev();
+    Element* prior = (Element*) iter.prev();
 
-    if (NULL != element) {
-        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
-            switch (element->fType) {
+    if (NULL != prior) {
+        if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
+            switch (prior->fType) {
                 case Element::kEmpty_Type:
-                    element->checkEmpty();
+                    SkDEBUGCODE(prior->checkEmpty();)
                     return;
                 case Element::kRect_Type:
-                    if (element->rectRectIntersectAllowed(rect, doAA)) {
-                        if (!element->fRect.intersect(rect)) {
-                            element->setEmpty();
+                    if (Element::kRect_Type == element.getType()) {
+                        if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) {
+                            if (!prior->fRect.intersect(element.getRect())) {
+                                prior->setEmpty();
+                                return;
+                            }
+
+                            prior->fDoAA = element.isAA();
+                            Element* priorPrior = (Element*) iter.prev();
+                            prior->updateBoundAndGenID(priorPrior);
                             return;
                         }
-
-                        element->fDoAA = doAA;
-                        Element* prev = (Element*) iter.prev();
-                        element->updateBoundAndGenID(prev);
-                        return;
+                        break;
                     }
-                    break;
-                case Element::kPath_Type:
-                    if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
-                        element->setEmpty();
+                    // fallthrough
+                default:
+                    if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
+                        prior->setEmpty();
                         return;
                     }
                     break;
             }
-        } else if (SkRegion::kReplace_Op == op) {
+        } else if (SkRegion::kReplace_Op == element.getOp()) {
             this->restoreTo(fSaveCount - 1);
-            element = (Element*) fDeque.back();
+            prior = (Element*) fDeque.back();
         }
     }
-    new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
-    ((Element*) fDeque.back())->updateBoundAndGenID(element);
+    Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (element));
+    newElement->updateBoundAndGenID(prior);
+}
+
+void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    Element element(fSaveCount, rrect, op, doAA);
+    this->pushElement(element);
+}
+
+void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    Element element(fSaveCount, rect, op, doAA);
+    this->pushElement(element);
 }
 
 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
-    SkRect alt;
-    if (path.isRect(&alt) && !path.isInverseFillType()) {
-        return this->clipDevRect(alt, op, doAA);
-    }
-
-    Element* element = (Element*)fDeque.back();
-    if (NULL != element) {
-        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
-            const SkRect& pathBounds = path.getBounds();
-            switch (element->fType) {
-                case Element::kEmpty_Type:
-                    element->checkEmpty();
-                    return;
-                case Element::kRect_Type:
-                    if (!SkRect::Intersects(element->fRect, pathBounds)) {
-                        element->setEmpty();
-                        return;
-                    }
-                    break;
-                case Element::kPath_Type:
-                    if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
-                        element->setEmpty();
-                        return;
-                    }
-                    break;
-            }
-        } else if (SkRegion::kReplace_Op == op) {
-            this->restoreTo(fSaveCount - 1);
-            element = (Element*) fDeque.back();
-        }
-    }
-    new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
-    ((Element*) fDeque.back())->updateBoundAndGenID(element);
+    Element element(fSaveCount, path, op, doAA);
+    this->pushElement(element);
 }
 
 void SkClipStack::clipEmpty() {
-
     Element* element = (Element*) fDeque.back();
 
     if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
-        switch (element->fType) {
-            case Element::kEmpty_Type:
-                element->checkEmpty();
-                return;
-            case Element::kRect_Type:
-            case Element::kPath_Type:
-                element->setEmpty();
-                return;
-        }
+        element->setEmpty();
     }
     new (fDeque.push_back()) Element(fSaveCount);