With the flag (SkDevice.h) enabled, I get correct drawing w/ the rasterbackend.
After this lands, hopefully we can work in parallel on gpu/pdf/svg/xps/etc.
BUG=skia:6214
Change-Id: Ie35fee818470aab57aebacca8a2a5b812a552ee2
Reviewed-on: https://skia-review.googlesource.com/8192
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Robert Phillips <robertphillips@google.com>
static SaveLayerFlags LegacySaveFlagsToSaveLayerFlags(uint32_t legacySaveFlags);
static void DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
- SkBaseDevice* dst, const SkMatrix& ctm,
- const SkClipStack* clipStack);
+ SkBaseDevice* dst, const SkIPoint& dstOrigin,
+ const SkMatrix& ctm, const SkClipStack* clipStack);
enum ShaderOverrideOpacity {
kNone_ShaderOverrideOpacity, //!< there is no overriding shader (bitmap or image)
SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
: INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))
, fBitmap(bitmap)
+ , fRCStack(bitmap.width(), bitmap.height())
{
SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
fBitmap.lockPixels();
-
- fRCStack.push_back().setRect(SkIRect::MakeWH(bitmap.width(), bitmap.height()));
}
SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) {
: INHERITED(bitmap.info(), surfaceProps)
, fBitmap(bitmap)
, fRasterHandle(hndl)
+ , fRCStack(bitmap.width(), bitmap.height())
{
SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
fBitmap.lockPixels();
-
- fRCStack.push_back().setRect(SkIRect::MakeWH(bitmap.width(), bitmap.height()));
}
SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
SkASSERT(!fBitmap.pixelRef());
fBitmap.setInfo(fBitmap.info().makeWH(size.fWidth, size.fHeight));
this->privateResize(fBitmap.info().width(), fBitmap.info().height());
+
+ fRCStack.setNewSize(size.fWidth, size.fHeight);
}
void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_USE_DEVICE_CLIPPING
+class ModifiedDraw : public SkDraw {
+public:
+ ModifiedDraw(const SkMatrix& devCTM, const SkRasterClip& rc, SkISize rcs,
+ const SkDraw& draw) : SkDraw(draw) {
+#ifdef SK_DEBUG
+ SkISize dvs = { draw.fDevice->width(), draw.fDevice->height() };
+ SkASSERT(dvs == rcs);
+ SkASSERT(devCTM == *draw.fMatrix);
+#endif
+ fRC = &rc;
+ }
+};
+#define PREPARE_DRAW(draw) ModifiedDraw(this->ctm(), fRCStack.rc(), fRCStack.getRootSize(), draw)
+#else
+#define PREPARE_DRAW(draw) draw
+#endif
+
void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
- draw.drawPaint(paint);
+ PREPARE_DRAW(draw).drawPaint(paint);
}
void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint) {
- draw.drawPoints(mode, count, pts, paint);
+ PREPARE_DRAW(draw).drawPoints(mode, count, pts, paint);
}
void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
- draw.drawRect(r, paint);
+ PREPARE_DRAW(draw).drawRect(r, paint);
}
void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
// required to override drawRRect.
this->drawPath(draw, path, paint, nullptr, true);
#else
- draw.drawRRect(rrect, paint);
+ PREPARE_DRAW(draw).drawRRect(rrect, paint);
#endif
}
void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
const SkPaint& paint, const SkMatrix* prePathMatrix,
bool pathIsMutable) {
- draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
+ PREPARE_DRAW(draw).drawPath(path, paint, prePathMatrix, pathIsMutable);
}
void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
const SkMatrix& matrix, const SkPaint& paint) {
LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality());
- draw.drawBitmap(bitmap, matrix, nullptr, paint);
+ PREPARE_DRAW(draw).drawBitmap(bitmap, matrix, nullptr, paint);
}
static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
// matrix with the CTM, and try to call drawSprite if it can. If not,
// it will make a shader and call drawRect, as we do below.
if (CanApplyDstMatrixAsCTM(matrix, paint)) {
- draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
+ PREPARE_DRAW(draw).drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
return;
}
}
void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint) {
- draw.drawSprite(bitmap, x, y, paint);
+ PREPARE_DRAW(draw).drawSprite(bitmap, x, y, paint);
}
void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint) {
- draw.drawText((const char*)text, len, x, y, paint);
+ PREPARE_DRAW(draw).drawText((const char*)text, len, x, y, paint);
}
void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
const SkScalar xpos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint& paint) {
- draw.drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint);
+ PREPARE_DRAW(draw).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint);
}
void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
const SkColor colors[], SkBlendMode bmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
- draw.drawVertices(vmode, vertexCount, verts, textures, colors, bmode,
+ PREPARE_DRAW(draw).drawVertices(vmode, vertexCount, verts, textures, colors, bmode,
indices, indexCount, paint);
}
void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
int x, int y, const SkPaint& paint) {
SkASSERT(!paint.getImageFilter());
- draw.drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint);
+ PREPARE_DRAW(draw).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void SkBitmapDevice::onSave() {
- fRCStack.push_back(fRCStack.back());
+ fRCStack.save();
}
void SkBitmapDevice::onRestore() {
- fRCStack.pop_back();
+ fRCStack.restore();
}
void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
- fRCStack.back().op(rect, this->ctm(), SkIRect::MakeWH(this->width(), this->height()),
- (SkRegion::Op)op, aa);
+ fRCStack.clipRect(this->ctm(), rect, op, aa);
}
void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
- fRCStack.back().op(rrect, this->ctm(), SkIRect::MakeWH(this->width(), this->height()),
- (SkRegion::Op)op, aa);
+ fRCStack.clipRRect(this->ctm(), rrect, op, aa);
}
void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
- fRCStack.back().op(path, this->ctm(), SkIRect::MakeWH(this->width(), this->height()),
- (SkRegion::Op)op, aa);
+ fRCStack.clipPath(this->ctm(), path, op, aa);
}
void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
- fRCStack.back().op(rgn, (SkRegion::Op)op);
+ SkIPoint origin = this->getOrigin();
+ SkRegion tmp;
+ const SkRegion* ptr = &rgn;
+ if (origin.fX | origin.fY) {
+ // translate from "global/canvas" coordinates to relative to this device
+ rgn.translate(-origin.fX, -origin.fY, &tmp);
+ ptr = &tmp;
+ }
+ fRCStack.clipRegion(*ptr, op);
+}
+
+void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) {
+ fRCStack.setDeviceClipRestriction(mutableClipRestriction);
+}
+
+void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) {
+#ifdef SK_DEBUG
+ const SkIRect& stackBounds = fRCStack.rc().getBounds();
+ SkASSERT(drawClipBounds == stackBounds);
+#endif
}
#include "SkImageInfo.h"
#include "SkPixelRef.h"
#include "SkRasterClip.h"
+#include "SkRasterClipStack.h"
#include "SkRect.h"
#include "SkScalar.h"
#include "SkSize.h"
void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override;
void onClipPath(const SkPath& path, SkClipOp, bool aa) override;
void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override;
+ void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) override;
+ void validateDevBounds(const SkIRect& r) override;
private:
friend class SkCanvas;
SkBitmap fBitmap;
void* fRasterHandle = nullptr;
- SkTArray<SkRasterClip> fRCStack;
+ SkRasterClipStack fRCStack;
void setNewSize(const SkISize&); // Used by SkCanvas for resetForNextPicture().
fMatrixStorage.postTranslate(SkIntToScalar(-x),
SkIntToScalar(-y));
fMatrix = &fMatrixStorage;
-
totalClip.translate(-x, -y, &fClip);
}
fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
+#ifdef SK_USE_DEVICE_CLIPPING
+ SkASSERT(*fMatrix == fDevice->ctm());
+ // TODO: debug tiles-rt-8888 so we can enable this all the time
+// fDevice->validateDevBounds(fClip.getBounds());
+#endif
+
// intersect clip, but don't translate it (yet)
if (updateClip) {
DeviceCM* layer = fMCRec->fTopLayer; \
while (layer) { \
SkBaseDevice* device = layer->fDevice; \
- code; \
+ if (device) { \
+ code; \
+ } \
layer = layer->fNext; \
} \
} while (0)
fMCRec->fLayer->fDevice = SkRef(device);
fMCRec->fRasterClip.setRect(device->getGlobalBounds());
fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
+
+#ifdef SK_USE_DEVICE_CLIPPING
+ device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
+#endif
}
return device;
SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
: INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
{
- this->setOrigin(bounds.x(), bounds.y());
+ this->setOrigin(SkMatrix::I(), bounds.x(), bounds.y());
}
private:
SkASSERT(fMCRec->fDeferredSaveCount > 0);
fMCRec->fDeferredSaveCount -= 1;
this->internalSave();
-#ifdef SK_USE_DEVICE_CLIPPING
- FOR_EACH_TOP_DEVICE(device->save());
-#endif
}
void SkCanvas::restore() {
fSaveCount -= 1;
this->internalRestore();
this->didRestore();
-#ifdef SK_USE_DEVICE_CLIPPING
- FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
-#endif
}
}
}
fMCRec = newTop;
fClipStack->save();
+#ifdef SK_USE_DEVICE_CLIPPING
+ FOR_EACH_TOP_DEVICE(device->save());
+#endif
}
bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
}
void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
- SkBaseDevice* dst, const SkMatrix& ctm,
- const SkClipStack* clipStack) {
+ SkBaseDevice* dst, const SkIPoint& dstOrigin,
+ const SkMatrix& ctm, const SkClipStack* clipStack) {
SkDraw draw;
SkRasterClip rc;
rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
SkPaint p;
p.setImageFilter(filter->makeWithLocalMatrix(ctm));
- int x = src->getOrigin().x() - dst->getOrigin().x();
- int y = src->getOrigin().y() - dst->getOrigin().y();
+ int x = src->getOrigin().x() - dstOrigin.x();
+ int y = src->getOrigin().y() - dstOrigin.y();
auto special = src->snapSpecial();
if (special) {
dst->drawSpecial(draw, special.get(), x, y, p);
}
SkBaseDevice* priorDevice = this->getTopDevice();
- if (nullptr == priorDevice) {
+ if (nullptr == priorDevice) { // Do we still need this check???
SkDebugf("Unable to find device for layer.");
return;
}
return;
}
}
- newDevice->setOrigin(ir.fLeft, ir.fTop);
+#ifndef SK_USE_DEVICE_CLIPPING
+ newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
+#endif
DeviceCM* layer =
new DeviceCM(newDevice.get(), paint, this, fConservativeRasterClip, stashedMatrix);
fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
if (rec.fBackdrop) {
- DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(),
+ DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
fMCRec->fMatrix, this->getClipStack());
}
+
+#ifdef SK_USE_DEVICE_CLIPPING
+ newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
+
+ newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
+ if (layer->fNext) {
+ // need to punch a hole in the previous device, so we don't draw there, given that
+ // the new top-layer will allow drawing to happen "below" it.
+ SkRegion hole(ir);
+ do {
+ layer = layer->fNext;
+ layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
+ } while (layer->fNext);
+ }
+#endif
}
int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
fMCStack.pop_back();
fMCRec = (MCRec*)fMCStack.back();
+#ifdef SK_USE_DEVICE_CLIPPING
+ if (fMCRec) {
+ FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
+ }
+#endif
+
/* Time to draw the layer's offscreen. We can't call the public drawSprite,
since if we're being recorded, we don't want to record this (the
recorder will have already recorded the restore).
// Translate shouldn't affect the is-scale-translateness of the matrix.
SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
+#ifdef SK_USE_DEVICE_CLIPPING
+ FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
+#endif
+
this->didTranslate(dx,dy);
}
}
fDeviceCMDirty = true;
fMCRec->fMatrix = matrix;
fIsScaleTranslate = matrix.isScaleTranslate();
+
+#ifdef SK_USE_DEVICE_CLIPPING
+ FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
+#endif
}
void SkCanvas::setMatrix(const SkMatrix& matrix) {
fClipStack->setDeviceClipRestriction(fClipRestrictionRect);
if (!fClipRestrictionRect.isEmpty()) {
this->checkForDeferredSave();
+#ifdef SK_USE_DEVICE_CLIPPING
+ SkRegion restrictRgn(fClipRestrictionRect);
+ FOR_EACH_TOP_DEVICE(device->clipRegion(restrictRgn, SkClipOp::kIntersect));
+#endif
AutoValidateClip avc(this);
fClipStack->clipDevRect(fClipRestrictionRect, kIntersect_SkClipOp);
fMCRec->fRasterClip.op(fClipRestrictionRect, SkRegion::kIntersect_Op);
, fSurfaceProps(surfaceProps)
{
fOrigin.setZero();
+ fCTM.reset();
}
SkBaseDevice::~SkBaseDevice() {}
+void SkBaseDevice::setOrigin(const SkMatrix& globalCTM, int x, int y) {
+ fOrigin.set(x, y);
+ fCTM = globalCTM;
+ fCTM.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+}
+
+void SkBaseDevice::setGlobalCTM(const SkMatrix& ctm) {
+ fCTM = ctm;
+ if (fOrigin.fX | fOrigin.fY) {
+ fCTM.postTranslate(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
+ }
+}
+
SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
TileUsage tileUsage,
SkPixelGeometry geo,
localM.setRSXform(*xform++);
currM.setConcat(*draw.fMatrix, localM);
localD.fMatrix = &currM;
+#ifdef SK_USE_DEVICE_CLIPPING
+ SkAutoDeviceCTMRestore adc(this, currM);
+#endif
+
int subLen = proc((const char*)text);
this->drawText(localD, text, subLen, 0, 0, paint);
text = (const char*)text + subLen;
void clipRegion(const SkRegion& region, SkClipOp op) {
this->onClipRegion(region, op);
}
+ void androidFramework_setDeviceClipRestriction(SkIRect* mutableClipRestriction) {
+ this->onSetDeviceClipRestriction(mutableClipRestriction);
+ }
const SkMatrix& ctm() const { return fCTM; }
void setCTM(const SkMatrix& ctm) {
fCTM = ctm;
}
- void setGlobalCTM(const SkMatrix& ctm) {
- fCTM = ctm;
- if (fOrigin.fX | fOrigin.fY) {
- fCTM.postTranslate(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
- }
- }
-
+ void setGlobalCTM(const SkMatrix& ctm);
+ virtual void validateDevBounds(const SkIRect&) {}
+
protected:
enum TileUsage {
kPossible_TileUsage, //!< the created device may be drawn tiled
virtual void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) {}
virtual void onClipPath(const SkPath& path, SkClipOp, bool aa) {}
virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp) {}
+ virtual void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) {}
/** These are called inside the per-device-layer loop for each draw call.
When these are called, we have already applied any saveLayer operations,
virtual GrRenderTargetContext* accessRenderTargetContext() { return nullptr; }
// just called by SkCanvas when built as a layer
- void setOrigin(int x, int y) { fOrigin.set(x, y); }
+ void setOrigin(const SkMatrix& ctm, int x, int y);
/** Causes any deferred drawing to the device to be completed.
*/
typedef SkRefCnt INHERITED;
};
+class SkAutoDeviceCTMRestore : SkNoncopyable {
+public:
+ SkAutoDeviceCTMRestore(SkBaseDevice* device, const SkMatrix& ctm)
+ : fDevice(device)
+ , fPrevCTM(device->ctm())
+ {
+ fDevice->setCTM(ctm);
+ }
+ ~SkAutoDeviceCTMRestore() {
+ fDevice->setCTM(fPrevCTM);
+ }
+
+private:
+ SkBaseDevice* fDevice;
+ const SkMatrix fPrevCTM;
+};
+
#endif
--- /dev/null
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRasterClipStack_DEFINED
+#define SkRasterClipStack_DEFINED
+
+#include "SkDeque.h"
+#include "SkRasterClip.h"
+
+template <typename T> class SkTStack {
+public:
+ SkTStack(void* storage, size_t size) : fDeque(sizeof(T), storage, size), fTop(nullptr) {}
+ ~SkTStack() {
+ while (!fDeque.empty()) {
+ ((T*)fDeque.back())->~T();
+ fDeque.pop_back();
+ }
+ }
+
+ bool empty() const { return fDeque.empty(); }
+
+ int count() const { return fDeque.count(); }
+
+ const T& top() const {
+ SkASSERT(fTop);
+ return *fTop;
+ }
+
+ T& top() {
+ SkASSERT(fTop);
+ return *fTop;
+ }
+
+ T* push_raw() { return (T*)fDeque.push_back(); }
+ T& push() {
+ fTop = this->push_raw();
+ new (fTop) T();
+ return *fTop;
+ }
+ T& push(const T& src) {
+ fTop = this->push_raw();
+ new (fTop) T(src);
+ return *fTop;
+ }
+
+ void pop() {
+ fTop->~T();
+ fDeque.pop_back();
+ fTop = fDeque.empty() ? nullptr : (T*)fDeque.back();
+ }
+
+private:
+ SkDeque fDeque;
+ T* fTop;
+};
+
+class SkRasterClipStack : SkNoncopyable {
+ int fCounter = 0;
+public:
+ SkRasterClipStack(int width, int height)
+ : fStack(fStorage, sizeof(fStorage))
+ , fRootBounds(SkIRect::MakeWH(width, height))
+ {
+ Rec& rec = fStack.push();
+ rec.fRC.setRect(fRootBounds);
+ rec.fDeferredCount = 0;
+ SkASSERT(fStack.count() == 1);
+ }
+
+ void setNewSize(int w, int h) {
+ SkASSERT(fStack.count() == 1);
+ fRootBounds.setXYWH(0, 0, w, h);
+ }
+
+ SkISize getRootSize() const { return fRootBounds.size(); }
+
+ const SkRasterClip& rc() const { return fStack.top().fRC; }
+
+ void save() {
+ fCounter += 1;
+ SkASSERT(fStack.top().fDeferredCount >= 0);
+ fStack.top().fDeferredCount += 1;
+ }
+
+ void restore() {
+ fCounter -= 1; SkASSERT(fCounter >= 0);
+ if (--fStack.top().fDeferredCount < 0) {
+ SkASSERT(fStack.top().fDeferredCount == -1);
+ SkASSERT(fStack.count() > 1);
+ fStack.pop();
+ }
+ }
+
+ void clipRect(const SkMatrix& ctm, const SkRect& rect, SkClipOp op, bool aa) {
+ this->writable_rc().op(rect, ctm, fRootBounds, (SkRegion::Op)op, aa);
+ this->trimIfExpanding(op);
+ this->validate();
+ }
+
+ void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, SkClipOp op, bool aa) {
+ this->writable_rc().op(rrect, ctm, fRootBounds, (SkRegion::Op)op, aa);
+ this->trimIfExpanding(op);
+ this->validate();
+ }
+
+ void clipPath(const SkMatrix& ctm, const SkPath& path, SkClipOp op, bool aa) {
+ this->writable_rc().op(path, ctm, fRootBounds, (SkRegion::Op)op, aa);
+ this->trimIfExpanding(op);
+ this->validate();
+ }
+
+ void clipRegion(const SkRegion& rgn, SkClipOp op) {
+ this->writable_rc().op(rgn, (SkRegion::Op)op);
+ this->trimIfExpanding(op);
+ this->validate();
+ }
+
+ void setDeviceClipRestriction(SkIRect* mutableClipRestriction) {
+ this->writable_rc().setDeviceClipRestriction(mutableClipRestriction);
+ }
+
+ void validate() const {
+#ifdef SK_DEBUG
+ const SkRasterClip& clip = this->rc();
+ if (fRootBounds.isEmpty()) {
+ SkASSERT(clip.isEmpty());
+ } else if (!clip.isEmpty()) {
+ SkASSERT(fRootBounds.contains(clip.getBounds()));
+ }
+#endif
+ }
+
+private:
+ struct Rec {
+ SkRasterClip fRC;
+ int fDeferredCount; // 0 for a "normal" entry
+ };
+
+ enum {
+ ELEM_COUNT = 16,
+ PTR_COUNT = ELEM_COUNT * sizeof(Rec) / sizeof(void*)
+ };
+ void* fStorage[PTR_COUNT];
+ SkTStack<Rec> fStack;
+ SkIRect fRootBounds;
+
+ SkRasterClip& writable_rc() {
+ SkASSERT(fStack.top().fDeferredCount >= 0);
+ if (fStack.top().fDeferredCount > 0) {
+ fStack.top().fDeferredCount -= 1;
+ fStack.push(fStack.top());
+ fStack.top().fDeferredCount = 0;
+ }
+ return fStack.top().fRC;
+ }
+
+ void trimIfExpanding(SkClipOp op) {
+ if ((int)op > (int)SkClipOp::kIntersect) {
+ Rec& rec = fStack.top();
+ SkASSERT(rec.fDeferredCount == 0);
+ rec.fRC.op(fRootBounds, SkRegion::kIntersect_Op);
+ }
+ }
+};
+
+#endif