diff options
-rw-r--r-- | include/core/SkCanvas.h | 19 | ||||
-rw-r--r-- | include/core/SkClipStack.h | 65 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 49 | ||||
-rw-r--r-- | src/core/SkClipStack.cpp | 144 | ||||
-rw-r--r-- | src/core/core_files.mk | 1 | ||||
-rw-r--r-- | xcode/core/core.xcodeproj/project.pbxproj | 4 |
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; }; |