use SkRasterClip inside canvas (check-point for soft clipping)

git-svn-id: http://skia.googlecode.com/svn/trunk@2462 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2011-10-12 14:34:30 +00:00
parent 58af9a6470
commit 0017708a5b
8 changed files with 96 additions and 67 deletions

View File

@ -34,10 +34,10 @@ public:
SkRegion::Op op = SkRegion::kIntersect_Op) {
SkRect r;
r.set(ir);
this->clipDevRect(r, op);
this->clipDevRect(r, op, false);
}
void clipDevRect(const SkRect&, SkRegion::Op = SkRegion::kIntersect_Op);
void clipDevPath(const SkPath&, SkRegion::Op = SkRegion::kIntersect_Op);
void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
class B2FIter {
public:
@ -55,6 +55,7 @@ public:
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;
bool fDoAA;
};
/**

View File

@ -927,9 +927,9 @@ bool SkAAClip::op(const SkIRect& r, SkRegion::Op op) {
return this->op(*this, clip, op);
}
bool SkAAClip::op(const SkRect& r, SkRegion::Op op) {
bool SkAAClip::op(const SkRect& r, SkRegion::Op op, bool doAA) {
SkAAClip clip;
clip.setRect(r);
clip.setRect(r, doAA);
return this->op(*this, clip, op);
}

View File

@ -40,7 +40,7 @@ public:
// Helpers for op()
bool op(const SkIRect&, SkRegion::Op);
bool op(const SkRect&, SkRegion::Op);
bool op(const SkRect&, SkRegion::Op, bool doAA);
bool op(const SkAAClip&, SkRegion::Op);
bool offset(int dx, int dy);

View File

@ -14,6 +14,7 @@
#include "SkDrawFilter.h"
#include "SkDrawLooper.h"
#include "SkPicture.h"
#include "SkRasterClip.h"
#include "SkScalarCompare.h"
#include "SkTemplates.h"
#include "SkTextFormatParams.h"
@ -153,9 +154,9 @@ private:
class SkCanvas::MCRec {
public:
MCRec* fNext;
SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
SkDrawFilter* fFilter; // the current filter (or null)
SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
SkDrawFilter* fFilter; // the current filter (or null)
DeviceCM* fLayer;
/* If there are any layers in the stack, this points to the top-most
@ -176,10 +177,10 @@ public:
}
if (flags & SkCanvas::kClip_SaveFlag) {
fRegionStorage = *prev->fRegion;
fRegion = &fRegionStorage;
fRasterClipStorage = *prev->fRasterClip;
fRasterClip = &fRasterClipStorage;
} else {
fRegion = prev->fRegion;
fRasterClip = prev->fRasterClip;
}
fFilter = prev->fFilter;
@ -190,7 +191,7 @@ public:
fMatrixStorage.reset();
fMatrix = &fMatrixStorage;
fRegion = &fRegionStorage;
fRasterClip = &fRasterClipStorage;
fFilter = NULL;
fTopLayer = NULL;
}
@ -206,8 +207,8 @@ public:
}
private:
SkMatrix fMatrixStorage;
SkRegion fRegionStorage;
SkMatrix fMatrixStorage;
SkRasterClip fRasterClipStorage;
};
class SkDrawIter : public SkDraw {
@ -517,9 +518,9 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) {
*/
if (NULL == device) {
rec->fRegion->setEmpty();
rec->fRasterClip->setEmpty();
while ((rec = (MCRec*)iter.next()) != NULL) {
(void)rec->fRegion->setEmpty();
(void)rec->fRasterClip->setEmpty();
}
fClipStack.reset();
} else {
@ -529,9 +530,9 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) {
bounds.set(0, 0, device->width(), device->height());
// now jam our 1st clip to be bounds, and intersect the rest with that
rec->fRegion->setRect(bounds);
rec->fRasterClip->setRect(bounds);
while ((rec = (MCRec*)iter.next()) != NULL) {
(void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
(void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
}
}
return device;
@ -697,7 +698,7 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
// early exit if the layer's bounds are clipped out
if (!ir.intersect(clipBounds)) {
if (bounds_affects_clip(flags)) {
fMCRec->fRegion->setEmpty();
fMCRec->fRasterClip->setEmpty();
}
return count;
}
@ -708,7 +709,7 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
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)) {
!fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
return count;
}
@ -909,12 +910,10 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
// the region code to scan-convert the path, only to discover that it
// is really just a rect.
SkRect r;
SkIRect ir;
fMCRec->fMatrix->mapRect(&r, rect);
fClipStack.clipDevRect(r, op);
r.round(&ir);
return fMCRec->fRegion->op(ir, op);
fClipStack.clipDevRect(r, op, doAA);
return fMCRec->fRasterClip->op(r, op, doAA);
} else {
// since we're rotate or some such thing, we convert the rect to a path
// and clip against that, since it can handle any matrix. However, to
@ -927,7 +926,7 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
}
}
static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
const SkPath& devPath, SkRegion::Op op, bool doAA) {
// base is used to limit the size (and therefore memory allocation) of the
// region that results from scan converting devPath.
@ -937,24 +936,24 @@ static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
// since we are intersect, we can do better (tighter) with currRgn's
// bounds, than just using the device. However, if currRgn is complex,
// our region blitter may hork, so we do that case in two steps.
if (currRgn->isRect()) {
return currRgn->setPath(devPath, *currRgn);
if (currClip->isRect()) {
return currClip->setPath(devPath, *currClip, doAA);
} else {
base.setRect(currRgn->getBounds());
SkRegion rgn;
rgn.setPath(devPath, base);
return currRgn->op(rgn, op);
base.setRect(currClip->getBounds());
SkRasterClip clip;
clip.setPath(devPath, base, doAA);
return currClip->op(clip, op);
}
} else {
const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
base.setRect(0, 0, bm.width(), bm.height());
if (SkRegion::kReplace_Op == op) {
return currRgn->setPath(devPath, base);
return currClip->setPath(devPath, base, doAA);
} else {
SkRegion rgn;
rgn.setPath(devPath, base);
return currRgn->op(rgn, op);
SkRasterClip clip;
clip.setPath(devPath, base, doAA);
return currClip->op(clip, op);
}
}
}
@ -970,9 +969,9 @@ bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
path.transform(*fMCRec->fMatrix, &devPath);
// if we called path.swap() we could avoid a deep copy of this path
fClipStack.clipDevPath(devPath, op);
fClipStack.clipDevPath(devPath, op, doAA);
return clipPathHelper(this, fMCRec->fRegion, devPath, op, doAA);
return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
}
bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
@ -986,7 +985,7 @@ bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
// we have to ignore it, and use the region directly?
fClipStack.clipDevRect(rgn.getBounds());
return fMCRec->fRegion->op(rgn, op);
return fMCRec->fRasterClip->op(rgn, op);
}
#ifdef SK_DEBUG
@ -995,25 +994,25 @@ void SkCanvas::validateClip() const {
const SkDevice* device = this->getDevice();
SkIRect ir;
ir.set(0, 0, device->width(), device->height());
SkRegion clipRgn(ir);
SkRasterClip tmpClip(ir);
SkClipStack::B2FIter iter(fClipStack);
const SkClipStack::B2FIter::Clip* clip;
while ((clip = iter.next()) != NULL) {
if (clip->fPath) {
clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp, false);
clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
} else if (clip->fRect) {
clip->fRect->round(&ir);
clipRgn.op(ir, clip->fOp);
tmpClip.op(ir, clip->fOp);
} else {
clipRgn.setEmpty();
tmpClip.setEmpty();
}
}
#if 0 // enable this locally for testing
// now compare against the current rgn
const SkRegion& rgn = this->getTotalClip();
SkASSERT(rgn == clipRgn);
SkASSERT(rgn == tmpClip);
#endif
}
#endif
@ -1044,7 +1043,7 @@ bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
if (!rect.hasValidCoordinates())
return true;
if (fMCRec->fRegion->isEmpty()) {
if (fMCRec->fRasterClip->isEmpty()) {
return true;
}
@ -1053,7 +1052,7 @@ bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
fMCRec->fMatrix->mapRect(&dst, rect);
SkIRect idst;
dst.roundOut(&idst);
return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
} else {
const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
@ -1082,7 +1081,7 @@ bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
antialiasing (worst case)
*/
if (fMCRec->fRegion->isEmpty()) {
if (fMCRec->fRasterClip->isEmpty()) {
return true;
}
@ -1127,7 +1126,7 @@ bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
}
bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
const SkRegion& clip = *fMCRec->fRegion;
const SkRasterClip& clip = *fMCRec->fRasterClip;
if (clip.isEmpty()) {
if (bounds) {
bounds->setEmpty();
@ -1146,20 +1145,13 @@ const SkMatrix& SkCanvas::getTotalMatrix() const {
}
SkCanvas::ClipType SkCanvas::getClipType() const {
if (fMCRec->fRegion->isEmpty()) return kEmpty_ClipType;
if (fMCRec->fRegion->isRect()) return kRect_ClipType;
if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
return kComplex_ClipType;
}
bool SkCanvas::getTotalClipBounds(SkIRect* bounds) const {
if (bounds) {
*bounds = fMCRec->fRegion->getBounds();
}
return !fMCRec->fRegion->isEmpty();
}
const SkRegion& SkCanvas::getTotalClip() const {
return *fMCRec->fRegion;
return fMCRec->fRasterClip->forceGetBW();
}
const SkClipStack& SkCanvas::getTotalClipStack() const {

View File

@ -21,22 +21,26 @@ struct SkClipStack::Rec {
int fSaveCount;
SkRegion::Op fOp;
State fState;
bool fDoAA;
Rec(int saveCount, const SkRect& rect, SkRegion::Op op) : fRect(rect) {
Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
fSaveCount = saveCount;
fOp = op;
fState = kRect_State;
fDoAA = doAA;
}
Rec(int saveCount, const SkPath& path, SkRegion::Op op) : fPath(path) {
Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
fRect.setEmpty();
fSaveCount = saveCount;
fOp = op;
fState = kPath_State;
fDoAA = doAA;
}
bool operator==(const Rec& b) const {
if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState) {
if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
fDoAA != b.fDoAA) {
return false;
}
switch (fState) {
@ -138,7 +142,7 @@ void SkClipStack::restore() {
}
}
void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op) {
void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
Rec* rec = (Rec*)fDeque.back();
if (rec && rec->canBeIntersected(fSaveCount, op)) {
switch (rec->fState) {
@ -157,10 +161,10 @@ void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op) {
break;
}
}
new (fDeque.push_back()) Rec(fSaveCount, rect, op);
new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
}
void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) {
void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
Rec* rec = (Rec*)fDeque.back();
if (rec && rec->canBeIntersected(fSaveCount, op)) {
const SkRect& pathBounds = path.getBounds();
@ -181,7 +185,7 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) {
break;
}
}
new (fDeque.push_back()) Rec(fSaveCount, path, op);
new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
}
///////////////////////////////////////////////////////////////////////////////
@ -191,7 +195,7 @@ SkClipStack::B2FIter::B2FIter() {
bool operator==(const SkClipStack::B2FIter::Clip& a,
const SkClipStack::B2FIter::Clip& b) {
return a.fOp == b.fOp &&
return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
((a.fRect == NULL && b.fRect == NULL) ||
(a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
((a.fPath == NULL && b.fPath == NULL) ||
@ -228,6 +232,7 @@ const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
break;
}
fClip.fOp = rec->fOp;
fClip.fDoAA = rec->fDoAA;
return &fClip;
}

View File

@ -112,6 +112,35 @@ bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
}
}
// return true if x is nearly integral (within 1/256) since that is the highest
// precision our aa code can have.
static bool is_integral(SkScalar x) {
int ix = SkScalarRoundToInt(x);
SkScalar sx = SkIntToScalar(ix);
return SkScalarAbs(sx - x) < (SK_Scalar1 / 256);
}
bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) {
if (doAA) {
// check that the rect really needs aa
if (is_integral(r.fLeft) && is_integral(r.fTop) &&
is_integral(r.fRight) && is_integral(r.fBottom)) {
doAA = false;
}
}
if (fIsBW && !doAA) {
SkIRect ir;
r.round(&ir);
fBW.op(ir, op);
} else {
if (fIsBW) {
this->convertToAA();
}
fAA.op(r, op, doAA);
}
}
const SkRegion& SkRasterClip::forceGetBW() {
if (!fIsBW) {
fBW.setRect(fAA.getBounds());

View File

@ -36,6 +36,7 @@ public:
bool op(const SkIRect&, SkRegion::Op);
bool op(const SkRegion&, SkRegion::Op);
bool op(const SkRasterClip&, SkRegion::Op);
bool op(const SkRect&, SkRegion::Op, bool doAA);
const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; }
const SkAAClip& aaRgn() const { SkASSERT(!fIsBW); return fAA; }

View File

@ -1219,7 +1219,8 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
synthesizedClipStack = fExistingClipStack;
SkPath clipPath;
clipRegion.getBoundaryPath(&clipPath);
synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op);
synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op,
false);
clipStack = &synthesizedClipStack;
}
}