add clipstack to canvas. not used yet, but will be for devices (e.g. gpu) that
want to see how the clip was built



git-svn-id: http://skia.googlecode.com/svn/trunk@824 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index a95a599..c19fd2f 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -20,6 +20,7 @@
 #include "SkTypes.h"
 #include "SkBitmap.h"
 #include "SkDeque.h"
+#include "SkClipStack.h"
 #include "SkPaint.h"
 #include "SkRefCnt.h"
 #include "SkPath.h"
@@ -776,6 +777,7 @@
 private:
     class MCRec;
 
+    SkClipStack fClipStack;
     SkDeque     fMCStack;
     // points to top of stack
     MCRec*      fMCRec;
@@ -835,6 +837,23 @@
 
     SkMatrix    fExternalMatrix, fExternalInverse;
     bool        fUseExternalMatrix;
+
+    class AutoValidateClip : ::SkNoncopyable {
+    public:
+        explicit AutoValidateClip(SkCanvas* canvas) : fCanvas(canvas) {
+            fCanvas->validateClip();
+        }
+        ~AutoValidateClip() { fCanvas->validateClip(); }
+
+    private:
+        const SkCanvas* fCanvas;
+    };
+
+#ifdef SK_DEBUG
+    void validateClip() const;
+#else
+    void validateClip() const {}
+#endif
 };
 
 /** Stack helper class to automatically call restoreToCount() on the canvas
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
new file mode 100644
index 0000000..bd64d66
--- /dev/null
+++ b/include/core/SkClipStack.h
@@ -0,0 +1,65 @@
+#ifndef SkClipStack_DEFINED
+#define SkClipStack_DEFINED
+
+#include "SkDeque.h"
+#include "SkRegion.h"
+
+class SkRect;
+class SkPath;
+
+class SkClipStack {
+public:
+    SkClipStack();
+    ~SkClipStack() {}
+
+    void reset();
+
+    int getSaveCount() const { return fSaveCount; }
+    void save();
+    void restore();
+
+    void clipDevRect(const SkIRect& ir,
+                     SkRegion::Op op = SkRegion::kIntersect_Op) {
+        SkRect r;
+        r.set(ir);
+        this->clipDevRect(r, op);
+    }
+    void clipDevRect(const SkRect&, SkRegion::Op = SkRegion::kIntersect_Op);
+    void clipDevPath(const SkPath&, SkRegion::Op = SkRegion::kIntersect_Op);
+
+    class B2FIter {
+    public:
+        B2FIter(const SkClipStack& stack);
+
+        struct Clip {
+            const SkRect*   fRect;  // if non-null, this is a rect clip
+            const SkPath*   fPath;  // if non-null, this is a path clip
+            SkRegion::Op    fOp;
+        };
+
+        /**
+         *  Return the clip for this element in the iterator. If next() returns
+         *  NULL, then the iterator is done. The type of clip is determined by
+         *  the pointers fRect and fPath:
+         *
+         *  fRect==NULL  fPath!=NULL    path clip
+         *  fRect!=NULL  fPath==NULL    rect clip
+         *  fRect==NULL  fPath==NULL    empty clip
+         */
+        const Clip* next();
+
+    private:
+        Clip             fClip;
+        SkDeque::F2BIter fIter;
+    };
+
+private:
+    friend class B2FIter;
+    struct Rec;
+
+    SkDeque fDeque;
+    int     fSaveCount;
+};
+
+#endif
+
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 046c692..88b012d 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -528,6 +528,7 @@
         while ((rec = (MCRec*)iter.next()) != NULL) {
             (void)rec->fRegion->setEmpty();
         }
+        fClipStack.reset();
     } else {
         // compute our total bounds for all devices
         SkIRect bounds;
@@ -539,6 +540,7 @@
         while ((rec = (MCRec*)iter.next()) != NULL) {
             (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
         }
+        fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
     }
     return device;
 }
@@ -648,6 +650,9 @@
     newTop->fNext = fMCRec;
     fMCRec = newTop;
 
+    fClipStack.save();
+    SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
+
     return saveCount;
 }
 
@@ -729,6 +734,7 @@
         ir = clipBounds;
     }
 
+    fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
     // early exit if the clip is now empty
     if (bounds_affects_clip(flags) &&
         !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
@@ -775,6 +781,7 @@
     fLocalBoundsCompareTypeDirty = true;
     fLocalBoundsCompareTypeDirtyBW = true;
 
+    fClipStack.restore();
 	// reserve our layer (if any)
     DeviceCM* layer = fMCRec->fLayer;   // may be null
     // now detach it from fMCRec so we can pop(). Gets freed after its drawn
@@ -798,6 +805,8 @@
         }
         SkDELETE(layer);
 	}
+
+    SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
 }
 
 int SkCanvas::getSaveCount() const {
@@ -910,7 +919,33 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
+#ifdef SK_DEBUG
+void SkCanvas::validateClip() const {
+    const SkRegion& rgn = this->getTotalClip();
+    const SkDevice* device = this->getDevice();
+    SkIRect ir;
+    ir.set(0, 0, device->width(), device->height());
+    SkRegion clipRgn(ir);
+
+    SkClipStack::B2FIter                iter(fClipStack);
+    const SkClipStack::B2FIter::Clip*   clip;
+    while ((clip = iter.next()) != NULL) {
+        if (clip->fPath) {
+            clipRgn.setPath(*clip->fPath, clipRgn);
+        } else if (clip->fRect) {
+            clip->fRect->round(&ir);
+            clipRgn.op(ir, clip->fOp);
+        } else {
+            break;
+        }
+    }
+    SkASSERT(rgn == clipRgn);
+}
+#endif
+
 bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+    AutoValidateClip avc(this);
+
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
     fLocalBoundsCompareTypeDirtyBW = true;
@@ -924,6 +959,7 @@
         SkIRect     ir;
 
         fMCRec->fMatrix->mapRect(&r, rect);
+        fClipStack.clipDevRect(r, op);
         r.round(&ir);
         return fMCRec->fRegion->op(ir, op);
     } else {
@@ -939,6 +975,8 @@
 }
 
 bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+    AutoValidateClip avc(this);
+
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
     fLocalBoundsCompareTypeDirtyBW = true;
@@ -946,6 +984,9 @@
     SkPath devPath;
     path.transform(*fMCRec->fMatrix, &devPath);
 
+    // if we called path.swap() we could avoid a deep copy of this path
+    fClipStack.clipDevPath(devPath, op);
+
     if (SkRegion::kIntersect_Op == op) {
         return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
     } else {
@@ -964,13 +1005,21 @@
 }
 
 bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+    AutoValidateClip avc(this);
+
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
     fLocalBoundsCompareTypeDirtyBW = true;
 
+    // todo: signal fClipStack that we have a region, and therefore (I guess)
+    // we have to ignore it, and use the region directly?
+    fClipStack.clipDevRect(rgn.getBounds());
+
     return fMCRec->fRegion->op(rgn, op);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
     SkRect r;
     SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
new file mode 100644
index 0000000..2b63aea
--- /dev/null
+++ b/src/core/SkClipStack.cpp
@@ -0,0 +1,144 @@
+#include "SkClipStack.h"
+#include "SkPath.h"
+#include <new>
+
+struct SkClipStack::Rec {
+    enum State {
+        kEmpty_State,
+        kRect_State,
+        kPath_State
+    };
+
+    SkPath          fPath;
+    SkRect          fRect;
+    int             fSaveCount;
+    SkRegion::Op    fOp;
+    State           fState;
+
+    Rec(int saveCount, const SkRect& rect, SkRegion::Op op) : fRect(rect) {
+        fSaveCount = saveCount;
+        fOp = op;
+        fState = kRect_State;
+    }
+
+    Rec(int saveCount, const SkPath& path, SkRegion::Op op) : fPath(path) {
+        fSaveCount = saveCount;
+        fOp = op;
+        fState = kPath_State;
+    }
+
+    /**
+     *  Returns true if this Rec can be intersected in place with a new clip
+     */
+    bool canBeIntersected(int saveCount, SkRegion::Op op) const {
+        if (kEmpty_State == fState) {
+            return true;
+        }
+        return  fSaveCount == saveCount &&
+                SkRegion::kIntersect_Op == fOp &&
+                SkRegion::kIntersect_Op == op;
+    }
+};
+
+SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
+    fSaveCount = 0;
+}
+
+void SkClipStack::reset() {
+    // don't have a reset() on SkDeque, so fake it here
+    fDeque.~SkDeque();
+    new (&fDeque) SkDeque(sizeof(Rec));
+
+    fSaveCount = 0;
+}
+
+void SkClipStack::save() {
+    fSaveCount += 1;
+}
+
+void SkClipStack::restore() {
+    fSaveCount -= 1;
+    while (!fDeque.empty()) {
+        Rec* rec = (Rec*)fDeque.back();
+        if (rec->fSaveCount <= fSaveCount) {
+            break;
+        }
+        rec->~Rec();
+        fDeque.pop_back();
+    }
+}
+
+void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op) {
+    Rec* rec = (Rec*)fDeque.back();
+    if (rec && rec->canBeIntersected(fSaveCount, op)) {
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                return;
+            case Rec::kRect_State:
+                if (!rec->fRect.intersect(rect)) {
+                    rec->fState = Rec::kEmpty_State;
+                }
+                return;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, rect, op);
+}
+
+void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) {
+    Rec* rec = (Rec*)fDeque.back();
+    if (rec && rec->canBeIntersected(fSaveCount, op)) {
+        const SkRect& pathBounds = path.getBounds();
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                return;
+            case Rec::kRect_State:
+                if (!SkRect::Intersects(rec->fRect, pathBounds)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, path, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) : fIter(stack.fDeque) {
+}
+
+const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
+    const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
+    if (NULL == rec) {
+        return NULL;
+    }
+
+    switch (rec->fState) {
+        case SkClipStack::Rec::kEmpty_State:
+            fClip.fRect = NULL;
+            fClip.fPath = NULL;
+            break;
+        case SkClipStack::Rec::kRect_State:
+            fClip.fRect = &rec->fRect;
+            fClip.fPath = NULL;
+            break;
+        case SkClipStack::Rec::kPath_State:
+            fClip.fRect = NULL;
+            fClip.fPath = &rec->fPath;
+            break;
+    }
+    fClip.fOp = rec->fOp;
+    return &fClip;
+}
diff --git a/src/core/core_files.mk b/src/core/core_files.mk
index 399b1a5..63b8833 100644
--- a/src/core/core_files.mk
+++ b/src/core/core_files.mk
@@ -22,6 +22,7 @@
     SkBuffer.cpp \
     SkCanvas.cpp \
     SkChunkAlloc.cpp \
+    SkClipStack.cpp \
     SkColor.cpp \
     SkColorFilter.cpp \
     SkColorTable.cpp \
diff --git a/xcode/core/core.xcodeproj/project.pbxproj b/xcode/core/core.xcodeproj/project.pbxproj
index 3c468fa..08eac41 100644
--- a/xcode/core/core.xcodeproj/project.pbxproj
+++ b/xcode/core/core.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		0009B176131441CD00C52F70 /* SkClipStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009B175131441CD00C52F70 /* SkClipStack.cpp */; };
 		00244E10106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00244E0F106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp */; };
 		002884C80EFAB8B90083E387 /* SkMMapStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884C70EFAB8B90083E387 /* SkMMapStream.cpp */; };
 		002884D50EFAB8F80083E387 /* SkStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884D40EFAB8F80083E387 /* SkStream.cpp */; };
@@ -141,6 +142,7 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		0009B175131441CD00C52F70 /* SkClipStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkClipStack.cpp; path = ../../src/core/SkClipStack.cpp; sourceTree = SOURCE_ROOT; };
 		00244E0F106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitRow_D32.cpp; path = ../../src/core/SkBlitRow_D32.cpp; sourceTree = SOURCE_ROOT; };
 		002884C70EFAB8B90083E387 /* SkMMapStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMMapStream.cpp; path = ../../src/core/SkMMapStream.cpp; sourceTree = SOURCE_ROOT; };
 		002884D40EFAB8F80083E387 /* SkStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkStream.cpp; path = ../../src/core/SkStream.cpp; sourceTree = SOURCE_ROOT; };
@@ -342,6 +344,7 @@
 		08FB7795FE84155DC02AAC07 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				0009B175131441CD00C52F70 /* SkClipStack.cpp */,
 				006EB61312EF97E100686979 /* SkRefDict.cpp */,
 				00B5785E12BFDC2A00393BE9 /* SkFlate.cpp */,
 				277670F312B840CA006811C2 /* SkRegion_rects.cpp */,
@@ -677,6 +680,7 @@
 				008AE3D812E4A3D6002516FE /* SkBlitRow_opts_SSE2.cpp in Sources */,
 				008AE3DA12E4A3D6002516FE /* SkUtils_opts_SSE2.cpp in Sources */,
 				006EB61412EF97E100686979 /* SkRefDict.cpp in Sources */,
+				0009B176131441CD00C52F70 /* SkClipStack.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};