Compact the clipstack for kReplace_Op'd geometry

When adding a clip rect or clip path to the stack with the
kReplace_Op operation, remove all previous elements within the
same save frame (elements with fSaveCount equal to the current
fSaveCount of the stack). This prevents unbounded growth of the
clipstack for long-lived instances that gets reused.

Addresses https://code.google.com/p/skia/issues/detail?id=748

R=robertphillips@google.com

Author: fs@opera.com

Review URL: https://chromiumcodereview.appspot.com/16160020

git-svn-id: http://skia.googlecode.com/svn/trunk@9502 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 9afef3f..2c0961a 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -443,9 +443,13 @@
 
 void SkClipStack::restore() {
     fSaveCount -= 1;
+    restoreTo(fSaveCount);
+}
+
+void SkClipStack::restoreTo(int saveCount) {
     while (!fDeque.empty()) {
         Element* element = (Element*)fDeque.back();
-        if (element->fSaveCount <= fSaveCount) {
+        if (element->fSaveCount <= saveCount) {
             break;
         }
         this->purgeClip(element);
@@ -528,32 +532,37 @@
     SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
     Element* element = (Element*) iter.prev();
 
-    if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
-        switch (element->fType) {
-            case Element::kEmpty_Type:
-                element->checkEmpty();
-                return;
-            case Element::kRect_Type:
-                if (element->rectRectIntersectAllowed(rect, doAA)) {
-                    this->purgeClip(element);
-                    if (!element->fRect.intersect(rect)) {
+    if (NULL != element) {
+        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
+            switch (element->fType) {
+                case Element::kEmpty_Type:
+                    element->checkEmpty();
+                    return;
+                case Element::kRect_Type:
+                    if (element->rectRectIntersectAllowed(rect, doAA)) {
+                        this->purgeClip(element);
+                        if (!element->fRect.intersect(rect)) {
+                            element->setEmpty();
+                            return;
+                        }
+
+                        element->fDoAA = doAA;
+                        Element* prev = (Element*) iter.prev();
+                        element->updateBoundAndGenID(prev);
+                        return;
+                    }
+                    break;
+                case Element::kPath_Type:
+                    if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
+                        this->purgeClip(element);
                         element->setEmpty();
                         return;
                     }
-
-                    element->fDoAA = doAA;
-                    Element* prev = (Element*) iter.prev();
-                    element->updateBoundAndGenID(prev);
-                    return;
-                }
-                break;
-            case Element::kPath_Type:
-                if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
-                    this->purgeClip(element);
-                    element->setEmpty();
-                    return;
-                }
-                break;
+                    break;
+            }
+        } else if (SkRegion::kReplace_Op == op) {
+            this->restoreTo(fSaveCount - 1);
+            element = (Element*) fDeque.back();
         }
     }
     new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
@@ -571,26 +580,31 @@
     }
 
     Element* element = (Element*)fDeque.back();
-    if (element && 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)) {
-                    this->purgeClip(element);
-                    element->setEmpty();
+    if (NULL != element) {
+        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
+            const SkRect& pathBounds = path.getBounds();
+            switch (element->fType) {
+                case Element::kEmpty_Type:
+                    element->checkEmpty();
                     return;
-                }
-                break;
-            case Element::kPath_Type:
-                if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
-                    this->purgeClip(element);
-                    element->setEmpty();
-                    return;
-                }
-                break;
+                case Element::kRect_Type:
+                    if (!SkRect::Intersects(element->fRect, pathBounds)) {
+                        this->purgeClip(element);
+                        element->setEmpty();
+                        return;
+                    }
+                    break;
+                case Element::kPath_Type:
+                    if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
+                        this->purgeClip(element);
+                        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);