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:
parent
d2b1d900dc
commit
67d7422844
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user