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
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index 53bea82..97b4a66 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -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 @@
*/
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();
/**
@@ -93,6 +101,15 @@
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
* 64MB.
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 5119729..21f717c 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -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 @@
//-----------------------------------------------------------------------------
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 @@
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 @@
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() {
this->flushPendingCommands(kSilent_PlaybackMode);
SkSafeUnref(fImmediateCanvas);
+ SkSafeUnref(fSurface);
}
void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
@@ -376,6 +400,11 @@
return fRecordingCanvas;
}
+SkImage* DeferredDevice::newImageShapshot() {
+ this->flush();
+ return fSurface ? fSurface->newImageShapshot() : NULL;
+}
+
uint32_t DeferredDevice::getDeviceCapabilities() {
return fImmediateDevice->getDeviceCapabilities();
}
@@ -437,7 +466,9 @@
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 @@
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 @@
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();
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index a95e09c..37e6721 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -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 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 @@
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)
+