Adding SkSurface support to SkDeferredCanvas

Review URL: https://codereview.chromium.org/14178002

git-svn-id: http://skia.googlecode.com/svn/trunk@8648 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
junov@chromium.org 2013-04-12 13:33:01 +00:00
parent d2b1d900dc
commit 67d7422844
3 changed files with 157 additions and 19 deletions

View File

@ -12,6 +12,8 @@
#include "SkPixelRef.h"
class DeferredDevice;
class SkImage;
class SkSurface;
/** \class SkDeferredCanvas
Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
@ -33,6 +35,12 @@ public:
*/
explicit SkDeferredCanvas(SkDevice* device);
/** Construct a canvas with the specified surface to draw into.
This constructor must be used for newImageSnapshot to work.
@param surface Specifies a surface for the canvas to draw into.
*/
explicit SkDeferredCanvas(SkSurface* surface);
virtual ~SkDeferredCanvas();
/**
@ -92,6 +100,15 @@ public:
*/
bool hasPendingCommands() const;
/**
* Flushes pending draw commands, if any, and returns an image of the
* current state of the surface pixels up to this point. Subsequent
* changes to the surface (by drawing into its canvas) will not be
* reflected in this image. Will return NULL if the deferred canvas
* was not constructed from an SkSurface.
*/
SkImage* newImageShapshot();
/**
* Specify the maximum number of bytes to be allocated for the purpose
* of recording draw commands to this canvas. The default limit, is

View File

@ -17,6 +17,7 @@
#include "SkPaintPriv.h"
#include "SkRRect.h"
#include "SkShader.h"
#include "SkSurface.h"
enum {
// Deferred canvas will auto-flush when recording reaches this limit
@ -138,14 +139,15 @@ void DeferredPipeController::playback(bool silent) {
//-----------------------------------------------------------------------------
class DeferredDevice : public SkDevice {
public:
DeferredDevice(SkDevice* immediateDevice,
SkDeferredCanvas::NotificationClient* notificationClient = NULL);
explicit DeferredDevice(SkDevice* immediateDevice);
explicit DeferredDevice(SkSurface* surface);
~DeferredDevice();
void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient);
SkCanvas* recordingCanvas();
SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
SkDevice* immediateDevice() const {return fImmediateDevice;}
SkImage* newImageShapshot();
bool isFreshFrame();
bool hasPendingCommands();
size_t storageAllocatedForRecording() const;
@ -237,12 +239,14 @@ private:
virtual void flush();
void beginRecording();
void init();
DeferredPipeController fPipeController;
SkGPipeWriter fPipeWriter;
SkDevice* fImmediateDevice;
SkCanvas* fImmediateCanvas;
SkCanvas* fRecordingCanvas;
SkSurface* fSurface;
SkDeferredCanvas::NotificationClient* fNotificationClient;
bool fFreshFrame;
size_t fMaxRecordingStorageBytes;
@ -250,21 +254,40 @@ private:
size_t fBitmapSizeThreshold;
};
DeferredDevice::DeferredDevice(
SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) :
SkDevice(SkBitmap::kNo_Config,
immediateDevice->width(), immediateDevice->height(),
immediateDevice->isOpaque(),
immediateDevice->getDeviceProperties())
, fRecordingCanvas(NULL)
, fFreshFrame(true)
, fPreviousStorageAllocated(0)
, fBitmapSizeThreshold(kDeferredCanvasBitmapSizeThreshold){
fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
fNotificationClient = notificationClient;
fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
DeferredDevice::DeferredDevice(SkDevice* immediateDevice)
: SkDevice(SkBitmap::kNo_Config,
immediateDevice->width(), immediateDevice->height(),
immediateDevice->isOpaque(),
immediateDevice->getDeviceProperties()) {
fSurface = NULL;
fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
this->init();
}
DeferredDevice::DeferredDevice(SkSurface* surface)
: SkDevice(SkBitmap::kNo_Config,
surface->getCanvas()->getDevice()->width(),
surface->getCanvas()->getDevice()->height(),
surface->getCanvas()->getDevice()->isOpaque(),
surface->getCanvas()->getDevice()->getDeviceProperties()) {
fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
fNotificationClient = NULL;
fImmediateCanvas = surface->getCanvas();
SkSafeRef(fImmediateCanvas);
fSurface = surface;
SkSafeRef(fSurface);
fImmediateDevice = fImmediateCanvas->getDevice(); // ref counted via fImmediateCanvas
this->init();
}
void DeferredDevice::init() {
fRecordingCanvas = NULL;
fFreshFrame = true;
fPreviousStorageAllocated = 0;
fBitmapSizeThreshold = kDeferredCanvasBitmapSizeThreshold;
fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
fNotificationClient = NULL;
fPipeController.setPlaybackCanvas(fImmediateCanvas);
this->beginRecording();
}
@ -272,6 +295,7 @@ DeferredDevice::DeferredDevice(
DeferredDevice::~DeferredDevice() {
this->flushPendingCommands(kSilent_PlaybackMode);
SkSafeUnref(fImmediateCanvas);
SkSafeUnref(fSurface);
}
void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
@ -376,6 +400,11 @@ SkCanvas* DeferredDevice::recordingCanvas() {
return fRecordingCanvas;
}
SkImage* DeferredDevice::newImageShapshot() {
this->flush();
return fSurface ? fSurface->newImageShapshot() : NULL;
}
uint32_t DeferredDevice::getDeviceCapabilities() {
return fImmediateDevice->getDeviceCapabilities();
}
@ -437,7 +466,9 @@ SkDevice* DeferredDevice::onCreateCompatibleDevice(
SkAutoTUnref<SkDevice> compatibleDevice
(fImmediateDevice->createCompatibleDevice(config, width, height,
isOpaque));
return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fNotificationClient));
DeferredDevice* device = SkNEW_ARGS(DeferredDevice, (compatibleDevice));
device->setNotificationClient(fNotificationClient);
return device;
}
bool DeferredDevice::onReadPixels(
@ -488,6 +519,11 @@ SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
this->setDevice(device);
}
SkDeferredCanvas::SkDeferredCanvas(SkSurface* surface) {
this->init();
this->INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (surface)))->unref();
}
void SkDeferredCanvas::init() {
fDeferredDrawing = true; // On by default
}
@ -584,6 +620,12 @@ SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient(
return notificationClient;
}
SkImage* SkDeferredCanvas::newImageShapshot() {
DeferredDevice* deferredDevice = this->getDeferredDevice();
SkASSERT(deferredDevice);
return deferredDevice ? deferredDevice->newImageShapshot() : NULL;
}
bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
const SkPaint* paint) const {
SkCanvas* canvas = this->drawingCanvas();

View File

@ -12,6 +12,12 @@
#include "SkDevice.h"
#include "SkGradientShader.h"
#include "SkShader.h"
#include "SkSurface.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#else
class GrContextFactory;
#endif
static const int gWidth = 2;
static const int gHeight = 2;
@ -465,7 +471,75 @@ static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter)
}
}
static void TestDeferredCanvas(skiatest::Reporter* reporter) {
typedef void* PixelPtr;
// Returns an opaque pointer which, either points to a GrTexture or RAM pixel
// buffer. Used to test pointer equality do determine whether a surface points
// to the same pixel data storage as before.
PixelPtr getSurfacePixelPtr(SkSurface* surface, bool useGpu) {
return useGpu ? surface->getCanvas()->getDevice()->accessBitmap(false).getTexture() :
surface->getCanvas()->getDevice()->accessBitmap(false).getPixels();
}
static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
SkImage::Info imageSpec = {
10, // width
10, // height
SkImage::kPMColor_ColorType,
SkImage::kPremul_AlphaType
};
SkSurface* surface;
bool useGpu = NULL != factory;
#if SK_SUPPORT_GPU
if (useGpu) {
GrContext* context = factory->get(GrContextFactory::kNative_GLContextType);
surface = SkSurface::NewRenderTarget(context, imageSpec);
} else {
surface = SkSurface::NewRaster(imageSpec);
}
#else
SkASSERT(!useGpu);
surface = SkSurface::NewRaster(imageSpec);
#endif
SkASSERT(NULL != surface);
SkAutoTUnref<SkSurface> aur(surface);
SkDeferredCanvas canvas(surface);
SkImage* image1 = canvas.newImageShapshot();
SkAutoTUnref<SkImage> aur_i1(image1);
PixelPtr pixels1 = getSurfacePixelPtr(surface, useGpu);
// The following clear would normally trigger a copy on write, but
// it won't because rendering is deferred.
canvas.clear(SK_ColorBLACK);
// Obtaining a snapshot directly from the surface (as opposed to the
// SkDeferredCanvas) will not trigger a flush of deferred draw operations
// and will therefore return the same image as the previous snapshot.
SkImage* image2 = surface->newImageShapshot();
SkAutoTUnref<SkImage> aur_i2(image2);
// Images identical because of deferral
REPORTER_ASSERT(reporter, image1->uniqueID() == image2->uniqueID());
// Now we obtain a snpshot via the deferred canvas, which triggers a flush.
// Because there is a pending clear, this will generate a different image.
SkImage* image3 = canvas.newImageShapshot();
SkAutoTUnref<SkImage> aur_i3(image3);
REPORTER_ASSERT(reporter, image1->uniqueID() != image3->uniqueID());
// Verify that backing store is now a different buffer because of copy on
// write
PixelPtr pixels2 = getSurfacePixelPtr(surface, useGpu);
REPORTER_ASSERT(reporter, pixels1 != pixels2);
canvas.clear(SK_ColorWHITE);
canvas.flush();
PixelPtr pixels3 = getSurfacePixelPtr(surface, useGpu);
// Verify that a direct canvas flush with a pending draw does not trigger
// a copy on write when the surface is not sharing its buffer with an
// SkImage.
canvas.clear(SK_ColorBLACK);
canvas.flush();
PixelPtr pixels4 = getSurfacePixelPtr(surface, useGpu);
REPORTER_ASSERT(reporter, pixels3 == pixels4);
}
static void TestDeferredCanvas(skiatest::Reporter* reporter, GrContextFactory* factory) {
TestDeferredCanvasBitmapAccess(reporter);
TestDeferredCanvasFlush(reporter);
TestDeferredCanvasFreshFrame(reporter);
@ -474,7 +548,12 @@ static void TestDeferredCanvas(skiatest::Reporter* reporter) {
TestDeferredCanvasSkip(reporter);
TestDeferredCanvasBitmapShaderNoLeak(reporter);
TestDeferredCanvasBitmapSizeThreshold(reporter);
TestDeferredCanvasSurface(reporter, NULL);
if (NULL != factory) {
TestDeferredCanvasSurface(reporter, factory);
}
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)
DEFINE_GPUTESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)