Adding class SkDeferredCanvas for deferred rendering.

TEST=added a new pass to gm, so all gm tests are run through SkDeferredCanvas
REVIEW=http://codereview.appspot.com/5430058/



git-svn-id: http://skia.googlecode.com/svn/trunk@3059 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
junov@google.com 2012-01-18 16:21:08 +00:00
parent 20ad5ac8f6
commit 4370aedf7f
6 changed files with 999 additions and 11 deletions

View File

@ -11,6 +11,7 @@
#include "SkColorPriv.h"
#include "SkData.h"
#include "SkDeferredCanvas.h"
#include "SkDevice.h"
#include "SkGpuCanvas.h"
#include "SkGpuDevice.h"
@ -268,26 +269,43 @@ static void invokeGM(GM* gm, SkCanvas* canvas) {
static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
GrContext* context,
GrRenderTarget* rt,
SkBitmap* bitmap) {
SkBitmap* bitmap,
bool deferred) {
SkISize size (gm->getISize());
setup_bitmap(gRec, size, bitmap);
SkCanvas canvas(*bitmap);
if (gRec.fBackend == kRaster_Backend) {
invokeGM(gm, &canvas);
SkCanvas* canvas;
if (deferred) {
canvas = new SkDeferredCanvas;
canvas->setDevice(new SkDevice(*bitmap))->unref();
} else {
canvas = new SkCanvas(*bitmap);
}
SkAutoUnref canvasUnref(canvas);
invokeGM(gm, canvas);
if (deferred) {
canvas->getDevice()->accessBitmap(false); // trigger a flush
}
} else { // GPU
if (NULL == context) {
return ERROR_NO_GPU_CONTEXT;
}
SkGpuCanvas gc(context, rt);
gc.setDevice(new SkGpuDevice(context, rt))->unref();
invokeGM(gm, &gc);
SkCanvas* gc;
if (deferred) {
gc = new SkDeferredCanvas;
} else {
gc = new SkGpuCanvas(context, rt);
}
SkAutoUnref gcUnref(gc);
gc->setDevice(new SkGpuDevice(context, rt))->unref();
invokeGM(gm, gc);
// the device is as large as the current rendertarget, so we explicitly
// only readback the amount we expect (in size)
// overwrite our previous allocation
bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
size.fHeight);
gc.readPixels(bitmap, 0, 0);
gc->readPixels(bitmap, 0, 0);
}
return ERROR_NONE;
}
@ -488,7 +506,8 @@ static ErrorBitfield test_drawing(GM* gm,
if (gRec.fBackend == kRaster_Backend ||
gRec.fBackend == kGPU_Backend) {
// Early exit if we can't generate the image.
ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap);
ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
false);
if (ERROR_NONE != errors) {
return errors;
}
@ -506,6 +525,28 @@ static ErrorBitfield test_drawing(GM* gm,
"", *bitmap, &document, NULL);
}
static ErrorBitfield test_deferred_drawing(GM* gm,
const ConfigData& gRec,
const SkBitmap& comparisonBitmap,
const char diffPath [],
GrContext* context,
GrRenderTarget* rt) {
SkDynamicMemoryWStream document;
if (gRec.fBackend == kRaster_Backend ||
gRec.fBackend == kGPU_Backend) {
SkBitmap bitmap;
// Early exit if we can't generate the image, but this is
// expected in some cases, so don't report a test failure.
if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
return ERROR_NONE;
}
return handle_test_results(gm, gRec, NULL, NULL, diffPath,
"-deferred", bitmap, NULL, &comparisonBitmap);
}
return ERROR_NONE;
}
static ErrorBitfield test_picture_playback(GM* gm,
const ConfigData& gRec,
const SkBitmap& comparisonBitmap,
@ -612,6 +653,7 @@ int main(int argc, char * const argv[]) {
bool doReplay = true;
bool doSerialize = false;
bool useMesa = false;
bool doDeferred = true;
const char* const commandName = argv[0];
char* const* stop = argv + argc;
@ -637,6 +679,8 @@ int main(int argc, char * const argv[]) {
doReplay = false;
} else if (strcmp(*argv, "--nopdf") == 0) {
doPDF = false;
} else if (strcmp(*argv, "--nodeferred") == 0) {
doDeferred = false;
} else if (strcmp(*argv, "--serialize") == 0) {
doSerialize = true;
} else if (strcmp(*argv, "--match") == 0) {
@ -758,6 +802,14 @@ int main(int argc, char * const argv[]) {
rt.get(), &forwardRenderedBitmap);
}
if (doDeferred && !testErrors &&
(kGPU_Backend == gRec[i].fBackend ||
kRaster_Backend == gRec[i].fBackend)) {
testErrors |= test_deferred_drawing(gm, gRec[i],
forwardRenderedBitmap,
diffPath, gGrContext, rt.get());
}
if ((ERROR_NONE == testErrors) && doReplay &&
!(gmFlags & GM::kSkipPicture_Flag)) {
testErrors |= test_picture_playback(gm, gRec[i],

View File

@ -22,6 +22,7 @@
'../include/utils/SkCamera.h',
'../include/utils/SkCubicInterval.h',
'../include/utils/SkCullPoints.h',
'../include/utils/SkDeferredCanvas.h',
'../include/utils/SkDumpCanvas.h',
'../include/utils/SkInterpolator.h',
'../include/utils/SkLayer.h',
@ -43,6 +44,7 @@
'../src/utils/SkColorMatrix.cpp',
'../src/utils/SkCubicInterval.cpp',
'../src/utils/SkCullPoints.cpp',
'../src/utils/SkDeferredCanvas.cpp',
'../src/utils/SkDumpCanvas.cpp',
'../src/utils/SkInterpolator.cpp',
'../src/utils/SkLayer.cpp',

View File

@ -78,7 +78,7 @@ public:
reference count is incremented. If the canvas was already holding a
device, its reference count is decremented. The new device is returned.
*/
SkDevice* setDevice(SkDevice* device);
virtual SkDevice* setDevice(SkDevice* device);
/**
* saveLayer() can create another device (which is later drawn onto
@ -278,7 +278,7 @@ public:
/** Returns the number of matrix/clip states on the SkCanvas' private stack.
This will equal # save() calls - # restore() calls.
*/
int getSaveCount() const;
virtual int getSaveCount() const;
/** Efficient way to pop any calls to save() that happened after the save
count reached saveCount. It is an error for saveCount to be less than
@ -845,7 +845,7 @@ public:
This does not account for the translate in any of the devices.
@return The current matrix on the canvas.
*/
const SkMatrix& getTotalMatrix() const;
virtual const SkMatrix& getTotalMatrix() const;
enum ClipType {
kEmpty_ClipType = 0,
@ -921,6 +921,12 @@ public:
};
protected:
// Returns the canvas to be used by DrawIter. Default implementation
// returns this. Subclasses that encapsulate an indirect canvas may
// need to overload this method. The impl must keep track of this, as it
// is not released or deleted by the caller.
virtual SkCanvas* canvasForDrawIter();
// all of the drawBitmap variants call this guy
virtual void commonDrawBitmap(const SkBitmap&, const SkIRect*,
const SkMatrix&, const SkPaint& paint);

View File

@ -0,0 +1,298 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDeferredCanvas_DEFINED
#define SkDeferredCanvas_DEFINED
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkPicture.h"
#include "SkPixelRef.h"
/** \class SkDeferredCanvas
Subclass of SkCanvas that encapsulates an SkPicture for deferred drawing.
The main difference between this class and SkPictureRecord (the canvas
provided by SkPicture) is that this is a full drop-in replacement for
SkCanvas, while SkPictureRecord only supports draw operations.
SkDeferredCanvas will transparently trigger the flushing of deferred
draw operations when an attempt is made to access the pixel data.
*/
class SK_API SkDeferredCanvas : public SkCanvas {
public:
class DeviceContext;
SkDeferredCanvas();
/** Construct a canvas with the specified device to draw into.
Equivalent to calling default constructor, then setDevice.
@param device Specifies a device for the canvas to draw into.
*/
explicit SkDeferredCanvas(SkDevice* device);
/** Construct a canvas with the specified device to draw into, and
* a device context. Equivalent to calling default constructor, then
* setDevice.
* @param device Specifies a device for the canvas to draw into.
* @param deviceContext interface for the device's the graphics context
*/
explicit SkDeferredCanvas(SkDevice* device, DeviceContext* deviceContext);
virtual ~SkDeferredCanvas();
/**
* Specify a device to be used by this canvas. Calling setDevice will
* release the previously set device, if any.
*
* @param device The device that the canvas will raw into
* @return The device argument, for convenience.
*/
virtual SkDevice* setDevice(SkDevice* device);
/**
* Specify a deviceContext to be used by this canvas. Calling
* setDeviceContext will release the previously set deviceContext, if any.
* A deviceContext must be specified if the device uses a graphics context
* that requires some form of state initialization prior to drawing
* and/or explicit flushing to synchronize the execution of rendering
* operations.
* Note: Must be called after the device is set with setDevice.
*
* @deviceContext interface for the device's the graphics context
* @return The deviceContext argument, for convenience.
*/
DeviceContext* setDeviceContext(DeviceContext* deviceContext);
/**
* Enable or disable deferred drawing. When deferral is disabled,
* pending draw operations are immediately flushed and from then on,
* the SkDeferredCanvas behaves just like a regular SkCanvas.
* This method must not be called while the save/restore stack is in use.
* @param deferred true/false
*/
void setDeferredDrawing(bool deferred);
// Overrides of the SkCanvas interface
virtual int save(SaveFlags flags) SK_OVERRIDE;
virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags) SK_OVERRIDE;
virtual void restore() SK_OVERRIDE;
virtual int getSaveCount() const SK_OVERRIDE;
virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE;
virtual bool rotate(SkScalar degrees) SK_OVERRIDE;
virtual bool skew(SkScalar sx, SkScalar sy) SK_OVERRIDE;
virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
virtual const SkMatrix& getTotalMatrix() const SK_OVERRIDE;
virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
bool doAntiAlias) SK_OVERRIDE;
virtual bool clipPath(const SkPath& path, SkRegion::Op op,
bool doAntiAlias) SK_OVERRIDE;
virtual bool clipRegion(const SkRegion& deviceRgn,
SkRegion::Op op) SK_OVERRIDE;
virtual void clear(SkColor) SK_OVERRIDE;
virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
const SkPaint& paint) SK_OVERRIDE;
virtual void drawRect(const SkRect& rect, const SkPaint& paint)
SK_OVERRIDE;
virtual void drawPath(const SkPath& path, const SkPaint& paint)
SK_OVERRIDE;
virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left,
SkScalar top, const SkPaint* paint)
SK_OVERRIDE;
virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
const SkRect& dst, const SkPaint* paint)
SK_OVERRIDE;
virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
const SkPaint* paint) SK_OVERRIDE;
virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint* paint)
SK_OVERRIDE;
virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
const SkPaint* paint) SK_OVERRIDE;
virtual void drawText(const void* text, size_t byteLength, SkScalar x,
SkScalar y, const SkPaint& paint) SK_OVERRIDE;
virtual void drawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint)
SK_OVERRIDE;
virtual void drawPosTextH(const void* text, size_t byteLength,
const SkScalar xpos[], SkScalar constY,
const SkPaint& paint) SK_OVERRIDE;
virtual void drawTextOnPath(const void* text, size_t byteLength,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) SK_OVERRIDE;
virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
virtual void drawVertices(VertexMode vmode, int vertexCount,
const SkPoint vertices[], const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) SK_OVERRIDE;
virtual SkBounder* setBounder(SkBounder* bounder) SK_OVERRIDE;
virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
private:
void flushIfNeeded(const SkBitmap& bitmap);
public:
class DeviceContext : public SkRefCnt {
public:
virtual void prepareForDraw() {}
virtual void flush() {}
};
public:
class DeferredDevice : public SkDevice {
public:
/**
* Constructor
* @param immediateDevice device to be drawn to when flushing
* deferred operations
* @param deviceContext callback interface for managing graphics
* context state, can be NULL.
*/
DeferredDevice(SkDevice* immediateDevice,
DeviceContext* deviceContext = NULL);
~DeferredDevice();
/**
* Sets the device context to be use with the device.
* @param deviceContext callback interface for managing graphics
* context state, can be NULL.
*/
void setDeviceContext(DeviceContext* deviceContext);
/**
* Returns the recording canvas.
*/
SkCanvas* recordingCanvas() const {return fRecordingCanvas;}
/**
* Returns the immediate (non deferred) canvas.
*/
SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
/**
* Returns the immediate (non deferred) device.
*/
SkDevice* immediateDevice() const {return fImmediateDevice;}
void flushPending();
void flushContext();
void purgePending();
void flushIfNeeded(const SkBitmap& bitmap);
virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
virtual int width() const SK_OVERRIDE;
virtual int height() const SK_OVERRIDE;
virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
int width, int height,
bool isOpaque,
Usage usage) SK_OVERRIDE;
virtual void writePixels(const SkBitmap& bitmap, int x, int y,
SkCanvas::Config8888 config8888) SK_OVERRIDE;
protected:
virtual void onAccessBitmap(SkBitmap*) SK_OVERRIDE;
virtual bool onReadPixels(const SkBitmap& bitmap,
int x, int y,
SkCanvas::Config8888 config8888) SK_OVERRIDE;
// The following methods are no-ops on a deferred device
virtual bool filterTextFlags(const SkPaint& paint, TextFlags*)
SK_OVERRIDE
{return false;}
virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
const SkClipStack&) SK_OVERRIDE
{}
virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
const SkClipStack&) SK_OVERRIDE
{}
// None of the following drawing methods should ever get called on the
// deferred device
virtual void clear(SkColor color)
{SkASSERT(0);}
virtual void drawPaint(const SkDraw&, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
size_t count, const SkPoint[],
const SkPaint& paint)
{SkASSERT(0);}
virtual void drawRect(const SkDraw&, const SkRect& r,
const SkPaint& paint)
{SkASSERT(0);}
virtual void drawPath(const SkDraw&, const SkPath& path,
const SkPaint& paint,
const SkMatrix* prePathMatrix = NULL,
bool pathIsMutable = false)
{SkASSERT(0);}
virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
const SkIRect* srcRectOrNull,
const SkMatrix& matrix, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawText(const SkDraw&, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawPosText(const SkDraw&, const void* text, size_t len,
const SkScalar pos[], SkScalar constY,
int scalarsPerPos, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawTextOnPath(const SkDraw&, const void* text,
size_t len, const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint)
{SkASSERT(0);}
virtual void drawPosTextOnPath(const SkDraw& draw, const void* text,
size_t len, const SkPoint pos[],
const SkPaint& paint,
const SkPath& path,
const SkMatrix* matrix)
{SkASSERT(0);}
virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
int vertexCount, const SkPoint verts[],
const SkPoint texs[], const SkColor colors[],
SkXfermode* xmode, const uint16_t indices[],
int indexCount, const SkPaint& paint)
{SkASSERT(0);}
virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
const SkPaint&)
{SkASSERT(0);}
private:
virtual void flush();
SkPicture fPicture;
SkDevice* fImmediateDevice;
SkCanvas* fImmediateCanvas;
SkCanvas* fRecordingCanvas;
DeviceContext* fDeviceContext;
};
DeferredDevice* getDeferredDevice() const;
protected:
virtual SkCanvas* canvasForDrawIter();
private:
SkCanvas* drawingCanvas() const;
bool isFullFrame(const SkRect*, const SkPaint*) const;
void validate() const;
void init();
bool fDeferredDrawing;
typedef SkCanvas INHERITED;
};
#endif

View File

@ -214,6 +214,7 @@ private:
class SkDrawIter : public SkDraw {
public:
SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
canvas = canvas->canvasForDrawIter();
fCanvas = canvas;
canvas->updateDeviceCMCache();
@ -591,6 +592,10 @@ void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
}
}
SkCanvas* SkCanvas::canvasForDrawIter() {
return this;
}
//////////////////////////////////////////////////////////////////////////////
void SkCanvas::updateDeviceCMCache() {

View File

@ -0,0 +1,625 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkDeferredCanvas.h"
#include "SkPaint.h"
#include "SkShader.h"
#include "SkColorFilter.h"
#include "SkDrawFilter.h"
namespace {
bool isPaintOpaque(const SkPaint& paint, const SkBitmap* bmpReplacesShader = NULL) {
// TODO: SkXfermode should have a virtual isOpaque method, which would
// make it possible to test modes that do not have a Coeff representation.
SkXfermode::Coeff srcCoeff, dstCoeff;
if (SkXfermode::AsCoeff(paint.getXfermode(), &srcCoeff, &dstCoeff)){
switch (dstCoeff) {
case SkXfermode::kZero_Coeff:
return true;
case SkXfermode::kISA_Coeff:
if (paint.getAlpha() != 255) {
break;
}
if (bmpReplacesShader) {
if (!bmpReplacesShader->isOpaque()) {
break;
}
} else if (paint.getShader() && !paint.getShader()->isOpaque()) {
break;
}
if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
case SkXfermode::kSA_Coeff:
if (paint.getAlpha() != 0) {
break;
}
if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
case SkXfermode::kSC_Coeff:
if (paint.getColor() != 0) { // all components must be 0
break;
}
if (bmpReplacesShader || paint.getShader()) {
break;
}
if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
default:
break;
}
}
return false;
}
} // unnamed namespace
SkDeferredCanvas::SkDeferredCanvas()
{
init();
}
SkDeferredCanvas::SkDeferredCanvas(SkDevice* device)
{
init();
setDevice(device);
}
SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
DeviceContext* deviceContext)
{
init();
setDevice(device);
setDeviceContext(deviceContext);
}
void SkDeferredCanvas::init()
{
fDeferredDrawing = true; // On by default
}
void SkDeferredCanvas::validate() const
{
SkASSERT(getDevice());
SkASSERT(INHERITED::getTotalMatrix().isIdentity());
}
SkCanvas* SkDeferredCanvas::drawingCanvas() const
{
validate();
return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
getDeferredDevice()->immediateCanvas();
}
void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap)
{
validate();
if (fDeferredDrawing) {
getDeferredDevice()->flushIfNeeded(bitmap);
}
}
SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const
{
return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
}
void SkDeferredCanvas::setDeferredDrawing(bool val)
{
validate(); // Must set device before calling this method
SkASSERT(drawingCanvas()->getSaveCount() == 1);
if (val != fDeferredDrawing) {
if (fDeferredDrawing) {
// Going live.
getDeferredDevice()->flushPending();
}
fDeferredDrawing = val;
}
}
SkDeferredCanvas::~SkDeferredCanvas()
{
}
SkDevice* SkDeferredCanvas::setDevice(SkDevice* device)
{
INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
return device;
}
SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
DeviceContext* deviceContext)
{
DeferredDevice* deferredDevice = getDeferredDevice();
SkASSERT(deferredDevice);
if (deferredDevice) {
deferredDevice->setDeviceContext(deviceContext);
}
return deviceContext;
}
bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
const SkPaint* paint) const
{
SkCanvas* canvas = drawingCanvas();
SkISize canvasSize = getDeviceSize();
if (rect) {
if (!canvas->getTotalMatrix().rectStaysRect()) {
return false; // conservative
}
SkRect transformedRect;
canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
if (paint) {
SkPaint::Style paintStyle = paint->getStyle();
if (!(paintStyle == SkPaint::kFill_Style ||
paintStyle == SkPaint::kStrokeAndFill_Style)) {
return false;
}
if (paint->getMaskFilter() || paint->getLooper()
|| paint->getPathEffect() || paint->getImageFilter()) {
return false; // conservative
}
}
// The following test holds with AA enabled, and is conservative
// by a 0.5 pixel margin with AA disabled
if (transformedRect.fLeft > 0 || transformedRect.fTop > 0 ||
transformedRect.fRight < canvasSize.fWidth ||
transformedRect.fBottom < canvasSize.fHeight) {
return false;
}
}
switch (canvas->getClipType()) {
case SkCanvas::kRect_ClipType :
{
SkIRect bounds;
canvas->getClipDeviceBounds(&bounds);
if (bounds.fLeft > 0 || bounds.fTop > 0 ||
bounds.fRight < canvasSize.fWidth ||
bounds.fBottom < canvasSize.fHeight)
return false;
}
break;
case SkCanvas::kComplex_ClipType :
return false; // conservative
case SkCanvas::kEmpty_ClipType:
default:
break;
};
return true;
}
int SkDeferredCanvas::save(SaveFlags flags)
{
return drawingCanvas()->save(flags);
}
int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags)
{
return drawingCanvas()->saveLayer(bounds, paint, flags);
}
void SkDeferredCanvas::restore()
{
drawingCanvas()->restore();
}
int SkDeferredCanvas::getSaveCount() const
{
return drawingCanvas()->getSaveCount();
}
bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy)
{
return drawingCanvas()->translate(dx, dy);
}
bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy)
{
return drawingCanvas()->scale(sx, sy);
}
bool SkDeferredCanvas::rotate(SkScalar degrees)
{
return drawingCanvas()->rotate(degrees);
}
bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy)
{
return drawingCanvas()->skew(sx, sy);
}
bool SkDeferredCanvas::concat(const SkMatrix& matrix)
{
return drawingCanvas()->concat(matrix);
}
void SkDeferredCanvas::setMatrix(const SkMatrix& matrix)
{
drawingCanvas()->setMatrix(matrix);
}
const SkMatrix& SkDeferredCanvas::getTotalMatrix() const
{
return drawingCanvas()->getTotalMatrix();
}
bool SkDeferredCanvas::clipRect(const SkRect& rect,
SkRegion::Op op,
bool doAntiAlias)
{
return drawingCanvas()->clipRect(rect, op, doAntiAlias);
}
bool SkDeferredCanvas::clipPath(const SkPath& path,
SkRegion::Op op,
bool doAntiAlias)
{
return drawingCanvas()->clipPath(path, op, doAntiAlias);
}
bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
SkRegion::Op op)
{
return drawingCanvas()->clipRegion(deviceRgn, op);
}
void SkDeferredCanvas::clear(SkColor color)
{
// purge pending commands
if (fDeferredDrawing) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->clear(color);
}
void SkDeferredCanvas::drawPaint(const SkPaint& paint)
{
if (fDeferredDrawing && isFullFrame(NULL, &paint) && isPaintOpaque(paint)) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->drawPaint(paint);
}
void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint)
{
drawingCanvas()->drawPoints(mode, count, pts, paint);
}
void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint)
{
if (fDeferredDrawing && isFullFrame(&rect, &paint) && isPaintOpaque(paint)) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->drawRect(rect, paint);
}
void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint)
{
drawingCanvas()->drawPath(path, paint);
}
void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
SkScalar top, const SkPaint* paint)
{
SkRect bitmapRect = SkRect::MakeXYWH(left, top, bitmap.width(),
bitmap.height());
if (fDeferredDrawing &&
isFullFrame(&bitmapRect, paint) &&
isPaintOpaque(*paint, &bitmap)) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->drawBitmap(bitmap, left, top, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
const SkIRect* src,
const SkRect& dst, const SkPaint* paint)
{
if (fDeferredDrawing &&
isFullFrame(&dst, paint) &&
isPaintOpaque(*paint, &bitmap)) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->drawBitmapRect(bitmap, src,
dst, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
const SkMatrix& m,
const SkPaint* paint)
{
// TODO: reset recording canvas if paint+bitmap is opaque and clip rect
// covers canvas entirely and transformed bitmap covers canvas entirely
drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
const SkIRect& center, const SkRect& dst,
const SkPaint* paint)
{
// TODO: reset recording canvas if paint+bitmap is opaque and clip rect
// covers canvas entirely and dst covers canvas entirely
drawingCanvas()->drawBitmapNine(bitmap, center,
dst, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
const SkPaint* paint)
{
SkRect bitmapRect = SkRect::MakeXYWH(left, top, bitmap.width(),
bitmap.height());
if (fDeferredDrawing &&
isFullFrame(&bitmapRect, paint) &&
isPaintOpaque(*paint, &bitmap)) {
getDeferredDevice()->purgePending();
}
drawingCanvas()->drawSprite(bitmap, left, top,
paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
SkScalar x, SkScalar y, const SkPaint& paint)
{
drawingCanvas()->drawText(text, byteLength, x,
y, paint);
}
void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint)
{
drawingCanvas()->drawPosText(text, byteLength,
pos, paint);
}
void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
const SkScalar xpos[], SkScalar constY,
const SkPaint& paint)
{
drawingCanvas()->drawPosTextH(text, byteLength,
xpos, constY,
paint);
}
void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint)
{
drawingCanvas()->drawTextOnPath(text, byteLength,
path, matrix,
paint);
}
void SkDeferredCanvas::drawPicture(SkPicture& picture)
{
drawingCanvas()->drawPicture(picture);
}
void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint)
{
drawingCanvas()->drawVertices(vmode, vertexCount,
vertices, texs,
colors, xmode,
indices, indexCount,
paint);
}
SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder)
{
INHERITED::setBounder(bounder); // So non-virtual getBounder works
return drawingCanvas()->setBounder(bounder);
}
SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter)
{
INHERITED::setDrawFilter(filter); // So non-virtual getDrawFilter works
return drawingCanvas()->setDrawFilter(filter);
}
SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
return drawingCanvas();
}
// SkDeferredCanvas::DeferredDevice
//------------------------------------
SkDeferredCanvas::DeferredDevice::DeferredDevice(
SkDevice* immediateDevice, DeviceContext* deviceContext) :
SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
immediateDevice->height(), immediateDevice->isOpaque())
{
fDeviceContext = deviceContext;
SkSafeRef(fDeviceContext);
fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
SkSafeRef(fImmediateCanvas);
fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
fImmediateDevice->height(),
SkPicture::kUsePathBoundsForClip_RecordingFlag);
}
SkDeferredCanvas::DeferredDevice::~DeferredDevice()
{
SkSafeUnref(fImmediateCanvas);
SkSafeUnref(fDeviceContext);
}
void SkDeferredCanvas::DeferredDevice::setDeviceContext(
DeviceContext* deviceContext)
{
SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
}
void SkDeferredCanvas::DeferredDevice::purgePending()
{
// TODO: find a way to transfer the state stack and layers
// to the new recording canvas. For now, purge only works
// with an empty stack.
if (fRecordingCanvas->getSaveCount() > 1)
return;
// Save state that is trashed by the purge
SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
SkSafeRef(drawFilter); // So that it survives the purge
SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
SkRegion clipRegion = fRecordingCanvas->getTotalClip();
// beginRecording creates a new recording canvas and discards the old one,
// hence purging deferred draw ops.
fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
fImmediateDevice->height(),
SkPicture::kUsePathBoundsForClip_RecordingFlag);
// Restore pre-purge state
if (!clipRegion.isEmpty()) {
fRecordingCanvas->clipRegion(clipRegion, SkRegion::kReplace_Op);
}
if (!matrix.isIdentity()) {
fRecordingCanvas->setMatrix(matrix);
}
if (drawFilter) {
fRecordingCanvas->setDrawFilter(drawFilter)->unref();
}
}
void SkDeferredCanvas::DeferredDevice::flushPending()
{
if (fDeviceContext) {
fDeviceContext->prepareForDraw();
}
fPicture.draw(fImmediateCanvas);
fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
fImmediateDevice->height(),
SkPicture::kUsePathBoundsForClip_RecordingFlag);
}
void SkDeferredCanvas::DeferredDevice::flush()
{
flushPending();
}
void SkDeferredCanvas::DeferredDevice::flushContext()
{
if (fDeviceContext) {
fDeviceContext->flush();
}
}
void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap)
{
if (bitmap.isImmutable()) {
return; // safe to deffer without registering a dependency
}
// For now, drawing a writable bitmap triggers a flush
// TODO: implement read-only semantics and auto buffer duplication on write
// in SkBitmap/SkPixelRef, which will make deferral possible in this case.
flushPending();
}
uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities()
{
return fImmediateDevice->getDeviceCapabilities();
}
int SkDeferredCanvas::DeferredDevice::width() const
{
return fImmediateDevice->width();
}
int SkDeferredCanvas::DeferredDevice::height() const
{
return fImmediateDevice->height();
}
SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget()
{
flushPending();
return fImmediateDevice->accessRenderTarget();
}
void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
int x, int y, SkCanvas::Config8888 config8888)
{
if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
(y + bitmap.height()) >= height()) {
purgePending();
}
if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
SkCanvas::kNative_Premul_Config8888 != config8888 &&
kPMColorAlias != config8888) {
//Special case config: no deferral
flushPending();
fImmediateDevice->writePixels(bitmap, x, y, config8888);
}
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap* bitmap)
{
SkASSERT(bitmap);
flushPending();
}
SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
SkBitmap::Config config, int width, int height, bool isOpaque, Usage usage)
{
// Save layer usage not supported, and not required by SkDeferredCanvas.
SkASSERT(usage != kSaveLayer_Usage);
// Create a compatible non-deferred device.
SkDevice* compatibleDevice = fImmediateDevice->createCompatibleDevice(config, width,
height, isOpaque);
return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
}
bool SkDeferredCanvas::DeferredDevice::onReadPixels(
const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888)
{
flushPending();
return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
x, y, config8888);
}