diff options
author | reed <reed@chromium.org> | 2014-09-09 18:46:22 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-09 18:46:22 -0700 |
commit | d954498c01ccf0417feacf89e45d0c62a06a813b (patch) | |
tree | bc6676ab7fb5b44b297daf141f1bbf0dafe31f6c | |
parent | 87a79236f53cb1e1e4be494a14142cea03b93a77 (diff) |
Revert of Revert of allow canvas to force conservative clips (for speed) (patchset #1 id:1 of https://codereview.chromium.org/554033003/)
Reason for revert:
May just rebaseline, plus want to see the results of the chrome tests, so re-trying this CL.
Original issue's description:
> Revert of allow canvas to force conservative clips (for speed) (patchset #7 id:120001 of https://codereview.chromium.org/541593005/)
>
> Reason for revert:
> multipicturedraw failed on nvprmsaa -- don't know why yet
>
> Original issue's description:
> > Allow SkCanvas to be initialized to force conservative rasterclips. This has the following effects:
> >
> > 1. Queries to the current clip will be conservatively large. This can mean the quickReject may return false more often.
> >
> > 2. The conservative clips mean less work is done.
> >
> > 3. Enabled by default for Gpu, Record, and NoSaveLayer canvases.
> >
> > 4. API is private for now.
> >
> > Committed: https://skia.googlesource.com/skia/+/27a5e656c3d6ef22f9cb34de18e1b960da3aa241
>
> TBR=robertphillips@google.com,bsalomon@google.com,mtklein@google.com,junov@google.com
> NOTREECHECKS=true
> NOTRY=true
>
> Committed: https://skia.googlesource.com/skia/+/6f09709519b79a1159f3826645f1c5fbc101ee11
R=robertphillips@google.com, bsalomon@google.com, mtklein@google.com, junov@google.com, reed@google.com
TBR=bsalomon@google.com, junov@google.com, mtklein@google.com, reed@google.com, robertphillips@google.com
NOTREECHECKS=true
NOTRY=true
Author: reed@chromium.org
Review URL: https://codereview.chromium.org/560713002
-rw-r--r-- | include/core/SkCanvas.h | 17 | ||||
-rw-r--r-- | include/core/SkDevice.h | 2 | ||||
-rw-r--r-- | include/gpu/SkGpuDevice.h | 1 | ||||
-rw-r--r-- | include/utils/SkNoSaveLayerCanvas.h | 18 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 183 | ||||
-rw-r--r-- | src/core/SkDeviceLooper.cpp | 7 | ||||
-rw-r--r-- | src/core/SkPictureRecord.cpp | 6 | ||||
-rw-r--r-- | src/core/SkRasterClip.cpp | 138 | ||||
-rw-r--r-- | src/core/SkRasterClip.h | 10 | ||||
-rw-r--r-- | src/core/SkRecorder.cpp | 6 | ||||
-rw-r--r-- | tests/AAClipTest.cpp | 7 | ||||
-rw-r--r-- | tests/PictureTest.cpp | 4 |
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); |