aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkCanvas.h19
-rw-r--r--include/core/SkClipStack.h65
-rw-r--r--src/core/SkCanvas.cpp49
-rw-r--r--src/core/SkClipStack.cpp144
-rw-r--r--src/core/core_files.mk1
-rw-r--r--xcode/core/core.xcodeproj/project.pbxproj4
6 files changed, 282 insertions, 0 deletions
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index a95a5993e9..c19fd2f463 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 @@ protected:
private:
class MCRec;
+ SkClipStack fClipStack;
SkDeque fMCStack;
// points to top of stack
MCRec* fMCRec;
@@ -835,6 +837,23 @@ private:
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 0000000000..bd64d66df8
--- /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 046c69298a..88b012defe 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -528,6 +528,7 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) {
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 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) {
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 @@ int SkCanvas::internalSave(SaveFlags flags) {
newTop->fNext = fMCRec;
fMCRec = newTop;
+ fClipStack.save();
+ SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
+
return saveCount;
}
@@ -729,6 +734,7 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
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 @@ void SkCanvas::internalRestore() {
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 @@ void SkCanvas::internalRestore() {
}
SkDELETE(layer);
}
+
+ SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
}
int SkCanvas::getSaveCount() const {
@@ -910,7 +919,33 @@ void SkCanvas::resetMatrix() {
//////////////////////////////////////////////////////////////////////////////
+#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 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
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::clipRect(const SkRect& rect, SkRegion::Op op) {
}
bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+ AutoValidateClip avc(this);
+
fDeviceCMDirty = true;
fLocalBoundsCompareTypeDirty = true;
fLocalBoundsCompareTypeDirtyBW = true;
@@ -946,6 +984,9 @@ bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
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::clipPath(const SkPath& path, SkRegion::Op op) {
}
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 0000000000..2b63aea9a0
--- /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 399b1a5a65..63b88336d1 100644
--- a/src/core/core_files.mk
+++ b/src/core/core_files.mk
@@ -22,6 +22,7 @@ SOURCE := \
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 3c468fa19f..08eac41ba7 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;
};