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
This commit is contained in:
parent
6034c506bd
commit
5c3d1471e4
@ -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
|
||||
|
65
include/core/SkClipStack.h
Normal file
65
include/core/SkClipStack.h
Normal file
@ -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
|
||||
|
@ -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 :
|
||||
|
144
src/core/SkClipStack.cpp
Normal file
144
src/core/SkClipStack.cpp
Normal file
@ -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;
|
||||
}
|
@ -22,6 +22,7 @@ SOURCE := \
|
||||
SkBuffer.cpp \
|
||||
SkCanvas.cpp \
|
||||
SkChunkAlloc.cpp \
|
||||
SkClipStack.cpp \
|
||||
SkColor.cpp \
|
||||
SkColorFilter.cpp \
|
||||
SkColorTable.cpp \
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user