aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkCanvas.h17
-rw-r--r--include/core/SkDevice.h2
-rw-r--r--include/gpu/SkGpuDevice.h1
-rw-r--r--include/utils/SkNoSaveLayerCanvas.h18
-rw-r--r--src/core/SkCanvas.cpp183
-rw-r--r--src/core/SkDeviceLooper.cpp7
-rw-r--r--src/core/SkPictureRecord.cpp6
-rw-r--r--src/core/SkRasterClip.cpp138
-rw-r--r--src/core/SkRasterClip.h10
-rw-r--r--src/core/SkRecorder.cpp6
-rw-r--r--tests/AAClipTest.cpp7
-rw-r--r--tests/PictureTest.cpp4
12 files changed, 233 insertions, 166 deletions
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index f514d18b5e..5088d7ded5 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1265,11 +1265,6 @@ protected:
SkIRect* intersection,
const SkImageFilter* imageFilter = NULL);
- // Called by child classes that override clipPath and clipRRect to only
- // track fast conservative clip bounds, rather than exact clips.
- void updateClipConservativelyUsingBounds(const SkRect&, SkRegion::Op,
- bool inverseFilled);
-
// notify our surface (if we have one) that we are about to draw, so it
// can perform copy-on-write or invalidate any cached images
void predrawNotify();
@@ -1309,13 +1304,22 @@ private:
friend class SkDebugCanvas; // needs experimental fAllowSimplifyClip
friend class SkDeferredDevice; // needs getTopDevice()
friend class SkSurface_Raster; // needs getDevice()
+ friend class SkRecorder; // InitFlags
+ friend class SkNoSaveLayerCanvas; // InitFlags
+ enum InitFlags {
+ kDefault_InitFlags = 0,
+ kConservativeRasterClip_InitFlag = 1 << 0,
+ };
+ SkCanvas(int width, int height, InitFlags flags);
+ SkCanvas(SkBaseDevice*, InitFlags flags);
+
// needs gettotalclip()
friend SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas*);
SkBaseDevice* createLayerDevice(const SkImageInfo&);
- SkBaseDevice* init(SkBaseDevice*);
+ SkBaseDevice* init(SkBaseDevice*, InitFlags);
/**
* DEPRECATED
@@ -1365,6 +1369,7 @@ private:
mutable bool fCachedLocalClipBoundsDirty;
bool fAllowSoftClip;
bool fAllowSimplifyClip;
+ bool fConservativeRasterClip;
const SkRect& getLocalClipBounds() const {
if (fCachedLocalClipBoundsDirty) {
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index aad5bf4737..618431a2de 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -372,6 +372,8 @@ private:
// TODO: move to SkBitmapDevice
virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) {}
+ virtual bool forceConservativeRasterClip() const { return false; }
+
// just called by SkCanvas when built as a layer
void setOrigin(int x, int y) { fOrigin.set(x, y); }
// just called by SkCanvas for saveLayer
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 91fc2f0bb6..5545227129 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -152,6 +152,7 @@ private:
virtual SkSurface* newSurface(const SkImageInfo&) SK_OVERRIDE;
virtual SkImageFilter::Cache* getImageFilterCache() SK_OVERRIDE;
+ virtual bool forceConservativeRasterClip() const SK_OVERRIDE { return true; }
// sets the render target, clip, and matrix on GrContext. Use forceIdenity to override
// SkDraw's matrix and draw in device coords.
diff --git a/include/utils/SkNoSaveLayerCanvas.h b/include/utils/SkNoSaveLayerCanvas.h
index 686f179901..56a09622a0 100644
--- a/include/utils/SkNoSaveLayerCanvas.h
+++ b/include/utils/SkNoSaveLayerCanvas.h
@@ -16,7 +16,8 @@
// It also simplifies the clipping calls to only use rectangles.
class SK_API SkNoSaveLayerCanvas : public SkCanvas {
public:
- SkNoSaveLayerCanvas(SkBaseDevice* device) : INHERITED(device) {}
+ SkNoSaveLayerCanvas(SkBaseDevice* device) : INHERITED(device, kConservativeRasterClip_InitFlag)
+ {}
protected:
virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
@@ -25,21 +26,6 @@ protected:
return kNoLayer_SaveLayerStrategy;
}
- // disable aa for speed
- virtual void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
- this->INHERITED::onClipRect(rect, op, kHard_ClipEdgeStyle);
- }
-
- // for speed, just respect the bounds, and disable AA. May give us a few
- // false positives and negatives.
- virtual void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
- this->updateClipConservativelyUsingBounds(path.getBounds(), op,
- path.isInverseFillType());
- }
- virtual void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
- this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
- }
-
private:
typedef SkCanvas INHERITED;
};
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 99786151ab..62d60da178 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -81,9 +81,12 @@ struct DeviceCM {
const SkMatrix* fMatrix;
SkPaint* fPaint; // may be null (in the future)
- DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
- : fNext(NULL) {
- if (device) {
+ DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
+ bool conservativeRasterClip)
+ : fNext(NULL)
+ , fClip(conservativeRasterClip)
+ {
+ if (NULL != device) {
device->ref();
device->onAttachToCanvas(canvas);
}
@@ -151,11 +154,10 @@ private:
*/
class SkCanvas::MCRec {
public:
- SkMatrix fMatrix;
SkRasterClip fRasterClip;
+ SkMatrix fMatrix;
SkDrawFilter* fFilter; // the current filter (or null)
-
- DeviceCM* fLayer;
+ DeviceCM* fLayer;
/* If there are any layers in the stack, this points to the top-most
one that is at or below this level in the stack (so we know what
bitmap/device to draw into from this level. This value is NOT
@@ -164,22 +166,21 @@ public:
*/
DeviceCM* fTopLayer;
- MCRec(const MCRec* prev) {
- if (prev) {
- fMatrix = prev->fMatrix;
- fRasterClip = prev->fRasterClip;
-
- fFilter = prev->fFilter;
- SkSafeRef(fFilter);
-
- fTopLayer = prev->fTopLayer;
- } else { // no prev
- fMatrix.reset();
- fFilter = NULL;
- fTopLayer = NULL;
- }
+ MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
+ fMatrix.reset();
+ fFilter = NULL;
+ fLayer = NULL;
+ fTopLayer = NULL;
+
+ // don't bother initializing fNext
+ inc_rec();
+ }
+ MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
+ fMatrix = prev.fMatrix;
+ fFilter = SkSafeRef(prev.fFilter);
fLayer = NULL;
-
+ fTopLayer = prev.fTopLayer;
+
// don't bother initializing fNext
inc_rec();
}
@@ -381,7 +382,8 @@ bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
////////////////////////////////////////////////////////////////////////////
-SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
+SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
+ fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
fCachedLocalClipBounds.setEmpty();
fCachedLocalClipBoundsDirty = true;
fAllowSoftClip = true;
@@ -391,10 +393,14 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
fCullCount = 0;
fMetaData = NULL;
+ if (device && device->forceConservativeRasterClip()) {
+ fConservativeRasterClip = true;
+ }
+
fMCRec = (MCRec*)fMCStack.push_back();
- new (fMCRec) MCRec(NULL);
+ new (fMCRec) MCRec(fConservativeRasterClip);
- fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
+ fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
fMCRec->fTopLayer = fMCRec->fLayer;
fSurfaceBase = NULL;
@@ -412,25 +418,54 @@ SkCanvas::SkCanvas()
{
inc_canvas();
- this->init(NULL);
+ this->init(NULL, kDefault_InitFlags);
}
+static SkBitmap make_nopixels(int width, int height) {
+ SkBitmap bitmap;
+ bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
+ return bitmap;
+}
+
+class SkNoPixelsBitmapDevice : public SkBitmapDevice {
+public:
+ SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
+
+private:
+
+ typedef SkBitmapDevice INHERITED;
+};
+
SkCanvas::SkCanvas(int width, int height)
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
{
inc_canvas();
+
+ this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
+}
- SkBitmap bitmap;
- bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
- this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
+SkCanvas::SkCanvas(int width, int height, InitFlags flags)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
+{
+ inc_canvas();
+
+ this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
}
-SkCanvas::SkCanvas(SkBaseDevice* device)
+SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
{
inc_canvas();
+
+ this->init(device, flags);
+}
- this->init(device);
+SkCanvas::SkCanvas(SkBaseDevice* device)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
+{
+ inc_canvas();
+
+ this->init(device, kDefault_InitFlags);
}
SkCanvas::SkCanvas(const SkBitmap& bitmap)
@@ -438,7 +473,7 @@ SkCanvas::SkCanvas(const SkBitmap& bitmap)
{
inc_canvas();
- this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
+ this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)), kDefault_InitFlags)->unref();
}
SkCanvas::~SkCanvas() {
@@ -734,7 +769,7 @@ int SkCanvas::internalSave() {
int saveCount = this->getSaveCount(); // record this before the actual save
MCRec* newTop = (MCRec*)fMCStack.push_back();
- new (newTop) MCRec(fMCRec); // balanced in restore()
+ new (newTop) MCRec(*fMCRec); // balanced in restore()
fMCRec = newTop;
fClipStack.save();
@@ -866,7 +901,8 @@ int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Save
}
device->setOrigin(ir.fLeft, ir.fTop);
- DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
+ DeviceCM* layer = SkNEW_ARGS(DeviceCM,
+ (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
device->unref();
layer->fNext = fMCRec->fTopLayer;
@@ -1286,7 +1322,7 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg
fMCRec->fMatrix.mapRect(&r, rect);
fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
- fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
+ fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
} else {
// since we're rotated or some such thing, we convert the rect to a path
// and clip against that, since it can handle any matrix. However, to
@@ -1421,82 +1457,6 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg
rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
}
-void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
- bool inverseFilled) {
- // This is for updating the clip conservatively using only bounds
- // information.
- // Contract:
- // The current clip must contain the true clip. The true
- // clip is the clip that would have normally been computed
- // by calls to clipPath and clipRRect
- // Objective:
- // Keep the current clip as small as possible without
- // breaking the contract, using only clip bounding rectangles
- // (for performance).
-
- // N.B.: This *never* calls back through a virtual on canvas, so subclasses
- // don't have to worry about getting caught in a loop. Thus anywhere
- // we call a virtual method, we explicitly prefix it with
- // SkCanvas:: to be sure to call the base-class.
-
- if (inverseFilled) {
- switch (op) {
- case SkRegion::kIntersect_Op:
- case SkRegion::kDifference_Op:
- // These ops can only shrink the current clip. So leaving
- // the clip unchanged conservatively respects the contract.
- break;
- case SkRegion::kUnion_Op:
- case SkRegion::kReplace_Op:
- case SkRegion::kReverseDifference_Op:
- case SkRegion::kXOR_Op: {
- // These ops can grow the current clip up to the extents of
- // the input clip, which is inverse filled, so we just set
- // the current clip to the device bounds.
- SkRect deviceBounds;
- SkIRect deviceIBounds;
- this->getDevice()->getGlobalBounds(&deviceIBounds);
- deviceBounds = SkRect::Make(deviceIBounds);
-
- // set the clip in device space
- SkMatrix savedMatrix = this->getTotalMatrix();
- this->SkCanvas::setMatrix(SkMatrix::I());
- this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
- kHard_ClipEdgeStyle);
- this->setMatrix(savedMatrix);
- break;
- }
- default:
- SkASSERT(0); // unhandled op?
- }
- } else {
- // Not inverse filled
- switch (op) {
- case SkRegion::kIntersect_Op:
- case SkRegion::kUnion_Op:
- case SkRegion::kReplace_Op:
- this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
- break;
- case SkRegion::kDifference_Op:
- // Difference can only shrink the current clip.
- // Leaving clip unchanged conservatively fullfills the contract.
- break;
- case SkRegion::kReverseDifference_Op:
- // To reverse, we swap in the bounds with a replace op.
- // As with difference, leave it unchanged.
- this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
- break;
- case SkRegion::kXOR_Op:
- // Be conservative, based on (A XOR B) always included in (A union B),
- // which is always included in (bounds(A) union bounds(B))
- this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
- break;
- default:
- SkASSERT(0); // unhandled op?
- }
- }
-}
-
void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
this->onClipRegion(rgn, op);
}
@@ -1525,7 +1485,7 @@ void SkCanvas::validateClip() const {
SkIRect ir;
ir.set(0, 0, device->width(), device->height());
- SkRasterClip tmpClip(ir);
+ SkRasterClip tmpClip(ir, fConservativeRasterClip);
SkClipStack::B2TIter iter(fClipStack);
const SkClipStack::Element* element;
@@ -1569,7 +1529,6 @@ bool SkCanvas::isClipRect() const {
}
bool SkCanvas::quickReject(const SkRect& rect) const {
-
if (!rect.isFinite())
return true;
diff --git a/src/core/SkDeviceLooper.cpp b/src/core/SkDeviceLooper.cpp
index 9346465dfc..a8350cc027 100644
--- a/src/core/SkDeviceLooper.cpp
+++ b/src/core/SkDeviceLooper.cpp
@@ -10,9 +10,10 @@
SkDeviceLooper::SkDeviceLooper(const SkBitmap& base,
const SkRasterClip& rc,
const SkIRect& bounds, bool aa)
-: fBaseBitmap(base)
-, fBaseRC(rc)
-, fDelta(aa ? kAA_Delta : kBW_Delta)
+ : fBaseBitmap(base)
+ , fBaseRC(rc)
+ , fSubsetRC(rc.isForceConservativeRects())
+ , fDelta(aa ? kAA_Delta : kBW_Delta)
{
// sentinels that next() has not yet been called, and so our mapper functions
// should not be called either.
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index b17169183a..cae8420e95 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -788,7 +788,7 @@ size_t SkPictureRecord::recordClipRect(const SkRect& rect, SkRegion::Op op, bool
void SkPictureRecord::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
this->recordClipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle);
- this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
+ this->INHERITED::onClipRRect(rrect, op, edgeStyle);
}
size_t SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
@@ -810,9 +810,7 @@ size_t SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkRegion::Op op, b
void SkPictureRecord::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
int pathID = this->addPathToHeap(path);
this->recordClipPath(pathID, op, kSoft_ClipEdgeStyle == edgeStyle);
-
- this->updateClipConservativelyUsingBounds(path.getBounds(), op,
- path.isInverseFillType());
+ this->INHERITED::onClipPath(path, op, edgeStyle);
}
size_t SkPictureRecord::recordClipPath(int pathID, SkRegion::Op op, bool doAA) {
diff --git a/src/core/SkRasterClip.cpp b/src/core/SkRasterClip.cpp
index dab91d8f4c..f820c5a0a4 100644
--- a/src/core/SkRasterClip.cpp
+++ b/src/core/SkRasterClip.cpp
@@ -6,18 +6,12 @@
*/
#include "SkRasterClip.h"
-
-
-SkRasterClip::SkRasterClip() {
- fIsBW = true;
- fIsEmpty = true;
- fIsRect = false;
- SkDEBUGCODE(this->validate();)
-}
+#include "SkPath.h"
SkRasterClip::SkRasterClip(const SkRasterClip& src) {
AUTO_RASTERCLIP_VALIDATE(src);
+ fForceConservativeRects = src.fForceConservativeRects;
fIsBW = src.fIsBW;
if (fIsBW) {
fBW = src.fBW;
@@ -30,13 +24,22 @@ SkRasterClip::SkRasterClip(const SkRasterClip& src) {
SkDEBUGCODE(this->validate();)
}
-SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
+SkRasterClip::SkRasterClip(const SkIRect& bounds, bool forceConservativeRects) : fBW(bounds) {
+ fForceConservativeRects = forceConservativeRects;
fIsBW = true;
fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
fIsRect = !fIsEmpty;
SkDEBUGCODE(this->validate();)
}
+SkRasterClip::SkRasterClip(bool forceConservativeRects) {
+ fForceConservativeRects = forceConservativeRects;
+ fIsBW = true;
+ fIsEmpty = true;
+ fIsRect = false;
+ SkDEBUGCODE(this->validate();)
+}
+
SkRasterClip::~SkRasterClip() {
SkDEBUGCODE(this->validate();)
}
@@ -70,9 +73,84 @@ bool SkRasterClip::setRect(const SkIRect& rect) {
return fIsRect;
}
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
+ SkIRect ir;
+ r.roundOut(&ir);
+
+ SkRegion::Op op;
+ if (isInverse) {
+ op = SkRegion::kDifference_Op;
+ } else {
+ op = SkRegion::kIntersect_Op;
+ }
+ fBW.setRect(clipR);
+ fBW.op(ir, op);
+ return this->updateCacheAndReturnNonEmpty();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+enum MutateResult {
+ kDoNothing_MutateResult,
+ kReplaceClippedAgainstGlobalBounds_MutateResult,
+ kContinue_MutateResult,
+};
+
+static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
+ if (inverseFilled) {
+ switch (*op) {
+ case SkRegion::kIntersect_Op:
+ case SkRegion::kDifference_Op:
+ // These ops can only shrink the current clip. So leaving
+ // the clip unchanged conservatively respects the contract.
+ return kDoNothing_MutateResult;
+ case SkRegion::kUnion_Op:
+ case SkRegion::kReplace_Op:
+ case SkRegion::kReverseDifference_Op:
+ case SkRegion::kXOR_Op: {
+ // These ops can grow the current clip up to the extents of
+ // the input clip, which is inverse filled, so we just set
+ // the current clip to the device bounds.
+ *op = SkRegion::kReplace_Op;
+ return kReplaceClippedAgainstGlobalBounds_MutateResult;
+ }
+ }
+ } else {
+ // Not inverse filled
+ switch (*op) {
+ case SkRegion::kIntersect_Op:
+ case SkRegion::kUnion_Op:
+ case SkRegion::kReplace_Op:
+ return kContinue_MutateResult;
+ case SkRegion::kDifference_Op:
+ // Difference can only shrink the current clip.
+ // Leaving clip unchanged conservatively fullfills the contract.
+ return kDoNothing_MutateResult;
+ case SkRegion::kReverseDifference_Op:
+ // To reverse, we swap in the bounds with a replace op.
+ // As with difference, leave it unchanged.
+ *op = SkRegion::kReplace_Op;
+ return kContinue_MutateResult;
+ case SkRegion::kXOR_Op:
+ // Be conservative, based on (A XOR B) always included in (A union B),
+ // which is always included in (bounds(A) union bounds(B))
+ *op = SkRegion::kUnion_Op;
+ return kContinue_MutateResult;
+ }
+ }
+ SkFAIL("should not get here");
+ return kDoNothing_MutateResult;
+}
+
bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
+ if (fForceConservativeRects) {
+ return this->setConservativeRect(path.getBounds(), clip.getBounds(), path.isInverseFillType());
+ }
+
if (this->isBW() && !doAA) {
(void)fBW.setPath(path, clip);
} else {
@@ -90,7 +168,22 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op,
// base is used to limit the size (and therefore memory allocation) of the
// region that results from scan converting devPath.
SkRegion base;
-
+
+ if (fForceConservativeRects) {
+ SkIRect ir;
+ switch (mutate_conservative_op(&op, path.isInverseFillType())) {
+ case kDoNothing_MutateResult:
+ return !this->isEmpty();
+ case kReplaceClippedAgainstGlobalBounds_MutateResult:
+ ir = SkIRect::MakeSize(size);
+ break;
+ case kContinue_MutateResult:
+ path.getBounds().roundOut(&ir);
+ break;
+ }
+ return this->op(ir, op);
+ }
+
if (SkRegion::kIntersect_Op == op) {
// since we are intersect, we can do better (tighter) with currRgn's
// bounds, than just using the device. However, if currRgn is complex,
@@ -102,7 +195,7 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op,
return this->setPath(path, this->bwRgn(), doAA);
} else {
base.setRect(this->getBounds());
- SkRasterClip clip;
+ SkRasterClip clip(fForceConservativeRects);
clip.setPath(path, base, doAA);
return this->op(clip, op);
}
@@ -112,7 +205,7 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op,
if (SkRegion::kReplace_Op == op) {
return this->setPath(path, base, doAA);
} else {
- SkRasterClip clip;
+ SkRasterClip clip(fForceConservativeRects);
clip.setPath(path, base, doAA);
return this->op(clip, op);
}
@@ -182,9 +275,24 @@ static bool nearly_integral(SkScalar x) {
return x - SkScalarFloorToScalar(x) < domain;
}
-bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) {
+bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
+ if (fForceConservativeRects) {
+ SkIRect ir;
+ switch (mutate_conservative_op(&op, false)) {
+ case kDoNothing_MutateResult:
+ return !this->isEmpty();
+ case kReplaceClippedAgainstGlobalBounds_MutateResult:
+ ir = SkIRect::MakeSize(size);
+ break;
+ case kContinue_MutateResult:
+ r.roundOut(&ir);
+ break;
+ }
+ return this->op(ir, op);
+ }
+
if (fIsBW && doAA) {
// check that the rect really needs aa, or is it close enought to
// integer boundaries that we can just treat it as a BW rect?
@@ -251,7 +359,9 @@ const SkRegion& SkRasterClip::forceGetBW() {
void SkRasterClip::convertToAA() {
AUTO_RASTERCLIP_VALIDATE(*this);
-
+
+ SkASSERT(!fForceConservativeRects);
+
SkASSERT(fIsBW);
fAA.setRegion(fBW);
fIsBW = false;
diff --git a/src/core/SkRasterClip.h b/src/core/SkRasterClip.h
index ec38990a0c..8a0681805c 100644
--- a/src/core/SkRasterClip.h
+++ b/src/core/SkRasterClip.h
@@ -13,11 +13,13 @@
class SkRasterClip {
public:
- SkRasterClip();
- SkRasterClip(const SkIRect&);
+ SkRasterClip(bool forceConservativeRects = false);
+ SkRasterClip(const SkIRect&, bool forceConservativeRects = false);
SkRasterClip(const SkRasterClip&);
~SkRasterClip();
+ bool isForceConservativeRects() const { return fForceConservativeRects; }
+
bool isBW() const { return fIsBW; }
bool isAA() const { return !fIsBW; }
const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; }
@@ -41,7 +43,7 @@ public:
bool op(const SkIRect&, SkRegion::Op);
bool op(const SkRegion&, SkRegion::Op);
- bool op(const SkRect&, SkRegion::Op, bool doAA);
+ bool op(const SkRect&, const SkISize&, SkRegion::Op, bool doAA);
bool op(const SkPath&, const SkISize&, SkRegion::Op, bool doAA);
void translate(int dx, int dy, SkRasterClip* dst) const;
@@ -75,6 +77,7 @@ public:
private:
SkRegion fBW;
SkAAClip fAA;
+ bool fForceConservativeRects;
bool fIsBW;
// these 2 are caches based on querying the right obj based on fIsBW
bool fIsEmpty;
@@ -107,6 +110,7 @@ private:
bool setPath(const SkPath& path, const SkRegion& clip, bool doAA);
bool setPath(const SkPath& path, const SkIRect& clip, bool doAA);
bool op(const SkRasterClip&, SkRegion::Op);
+ bool setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse);
};
class SkAutoRasterClipValidate : SkNoncopyable {
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 86578a53b8..f6c16d1c30 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -11,7 +11,7 @@
// SkCanvas will fail in mysterious ways if it doesn't know the real width and height.
SkRecorder::SkRecorder(SkRecord* record, int width, int height)
- : SkCanvas(width, height)
+ : SkCanvas(width, height, SkCanvas::kConservativeRasterClip_InitFlag)
, fRecord(record)
, fSaveLayerCount(0) {}
@@ -280,12 +280,12 @@ void SkRecorder::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle e
}
void SkRecorder::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
- INHERITED(updateClipConservativelyUsingBounds, rrect.getBounds(), op, false);
+ INHERITED(onClipRRect, rrect, op, edgeStyle);
APPEND(ClipRRect, this->devBounds(), rrect, op, edgeStyle == kSoft_ClipEdgeStyle);
}
void SkRecorder::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
- INHERITED(updateClipConservativelyUsingBounds, path.getBounds(), op, path.isInverseFillType());
+ INHERITED(onClipPath, path, op, edgeStyle);
APPEND(ClipPath, this->devBounds(), delay_copy(path), op, edgeStyle == kSoft_ClipEdgeStyle);
}
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp
index 776cd5267a..64e378455a 100644
--- a/tests/AAClipTest.cpp
+++ b/tests/AAClipTest.cpp
@@ -371,6 +371,7 @@ static bool operator==(const SkRasterClip& a, const SkRasterClip& b) {
static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
size_t count, bool changed) {
+ const SkISize baseSize = SkISize::Make(10, 10);
SkIRect ir = { 0, 0, 10, 10 };
for (size_t i = 0; i < count; ++i) {
@@ -381,11 +382,11 @@ static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
SkRasterClip rc1(ir);
SkRasterClip rc2(ir);
- rc0.op(r, SkRegion::kIntersect_Op, false);
+ rc0.op(r, baseSize, SkRegion::kIntersect_Op, false);
r.offset(dx[i], 0);
- rc1.op(r, SkRegion::kIntersect_Op, true);
+ rc1.op(r, baseSize, SkRegion::kIntersect_Op, true);
r.offset(-2*dx[i], 0);
- rc2.op(r, SkRegion::kIntersect_Op, true);
+ rc2.op(r, baseSize, SkRegion::kIntersect_Op, true);
REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 05aec092c3..3bc91d78a7 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -1570,8 +1570,8 @@ static void test_clip_bound_opt(skiatest::Reporter* reporter) {
path2.addOval(rect3);
SkIRect clipBounds;
SkPictureRecorder recorder;
- // Minimalist test set for 100% code coverage of
- // SkPictureRecord::updateClipConservativelyUsingBounds
+
+ // Testing conservative-raster-clip that is enabled by PictureRecord
{
SkCanvas* canvas = recorder.beginRecording(10, 10);
canvas->clipPath(invPath, SkRegion::kIntersect_Op);