diff options
author | reed <reed@google.com> | 2015-08-19 08:18:04 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-19 08:18:04 -0700 |
commit | 451af5062e6bf9c665126d84516e0baafa27ecc4 (patch) | |
tree | 34a59d58a3d5667d2af2fa7dfbbba2f773d4bdea | |
parent | da04e0e80a0113e1d838f7406cd8a8d545b8c02b (diff) |
remove SkDeferredCanvas
Waiting a day or so to see if the blink-removal of SkDeferredCanvas sticks
BUG=skia:
Review URL: https://codereview.chromium.org/1269093002
-rw-r--r-- | bench/DeferredSurfaceCopyBench.cpp | 74 | ||||
-rw-r--r-- | dm/DM.cpp | 1 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 27 | ||||
-rw-r--r-- | gyp/utils.gypi | 2 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 1 | ||||
-rw-r--r-- | include/core/SkDevice.h | 1 | ||||
-rw-r--r-- | include/utils/SkDeferredCanvas.h | 261 | ||||
-rw-r--r-- | samplecode/SampleApp.cpp | 20 | ||||
-rw-r--r-- | samplecode/SampleApp.h | 4 | ||||
-rw-r--r-- | src/core/SkPictureRecord.cpp | 3 | ||||
-rw-r--r-- | src/utils/SkDeferredCanvas.cpp | 1027 | ||||
-rw-r--r-- | tests/CanvasTest.cpp | 62 | ||||
-rw-r--r-- | tests/DeferredCanvasTest.cpp | 949 |
13 files changed, 2 insertions, 2430 deletions
diff --git a/bench/DeferredSurfaceCopyBench.cpp b/bench/DeferredSurfaceCopyBench.cpp deleted file mode 100644 index 3644627d31..0000000000 --- a/bench/DeferredSurfaceCopyBench.cpp +++ /dev/null @@ -1,74 +0,0 @@ - -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Benchmark.h" -#include "SkDeferredCanvas.h" -#include "SkDevice.h" -#include "SkImage.h" -#include "SkSurface.h" -#if SK_SUPPORT_GPU -#include "GrRenderTarget.h" -#endif - -class DeferredSurfaceCopyBench : public Benchmark { - enum { - kSurfaceWidth = 1000, - kSurfaceHeight = 1000, - }; -public: - DeferredSurfaceCopyBench(bool discardableContents) { - fDiscardableContents = discardableContents; - } - -protected: - const char* onGetName() override { - return fDiscardableContents ? "DeferredSurfaceCopy_discardable" : - "DeferredSurfaceCopy_nonDiscardable"; - } - - void onDraw(const int loops, SkCanvas* canvas) override { - // The canvas is not actually used for this test except to provide - // configuration information: gpu, multisampling, size, etc? - SkImageInfo info = SkImageInfo::MakeN32Premul(kSurfaceWidth, kSurfaceHeight); - const SkRect fullCanvasRect = SkRect::MakeWH( - SkIntToScalar(kSurfaceWidth), SkIntToScalar(kSurfaceHeight)); - SkAutoTUnref<SkSurface> surface(canvas->newSurface(info)); - - // newSurface() can return NULL for several reasons, so we need to check - if (NULL == surface.get()) { - SkDebugf("DeferredSurfaceCopyBench newSurface failed, bench results are meaningless\n"); - return; // should we signal the caller that we hit an error? - } - - SkAutoTUnref<SkDeferredCanvas> drawingCanvas(SkDeferredCanvas::Create(surface)); - - for (int iteration = 0; iteration < loops; iteration++) { - drawingCanvas->clear(0); - SkAutoTUnref<SkImage> image(drawingCanvas->newImageSnapshot()); - SkPaint paint; - if (!fDiscardableContents) { - // If paint is not opaque, prior canvas contents are - // not discardable because they are needed for compositing. - paint.setAlpha(127); - } - drawingCanvas->drawRect(fullCanvasRect, paint); - // Trigger copy on write, which should be faster in the discardable case. - drawingCanvas->flush(); - } - } - -private: - bool fDiscardableContents; - - typedef Benchmark INHERITED; -}; - -////////////////////////////////////////////////////////////////////////////// - -DEF_BENCH( return new DeferredSurfaceCopyBench(false); ) -DEF_BENCH( return new DeferredSurfaceCopyBench(true); ) @@ -418,7 +418,6 @@ static Sink* create_via(const char* tag, Sink* wrapped) { VIA("twice", ViaTwice, wrapped); VIA("pipe", ViaPipe, wrapped); VIA("serialize", ViaSerialization, wrapped); - VIA("deferred", ViaDeferred, wrapped); VIA("2ndpic", ViaSecondPicture, wrapped); VIA("sp", ViaSingletonPictures, wrapped); VIA("tiles", ViaTiles, 256, 256, NULL, wrapped); diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index f766b49c79..5b2ccc8f57 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -10,7 +10,6 @@ #include "SkCodec.h" #include "SkCommonFlags.h" #include "SkData.h" -#include "SkDeferredCanvas.h" #include "SkDocument.h" #include "SkError.h" #include "SkFunction.h" @@ -874,37 +873,13 @@ Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) { PipeController controller(canvas, &SkImageDecoder::DecodeMemory); SkGPipeWriter pipe; - const uint32_t kFlags = 0; // We mirror SkDeferredCanvas, which doesn't use any flags. + const uint32_t kFlags = 0; return src.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height())); }); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -Error ViaDeferred::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { - // We draw via a deferred canvas into a surface that's compatible with the original canvas, - // then snap that surface as an image and draw it into the original canvas. - return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) -> Error { - SkAutoTUnref<SkSurface> surface(canvas->newSurface(canvas->imageInfo())); - if (!surface.get()) { - return "can't make surface for deferred canvas"; - } - SkAutoTDelete<SkDeferredCanvas> defcan(SkDeferredCanvas::Create(surface)); - Error err = src.draw(defcan); - if (!err.isEmpty()) { - return err; - } - SkAutoTUnref<SkImage> image(defcan->newImageSnapshot()); - if (!image) { - return "failed to create deferred image snapshot"; - } - canvas->drawImage(image, 0, 0, NULL); - return ""; - }); -} - -/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - Error ViaSerialization::draw( const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { // Record our Src into a picture. diff --git a/gyp/utils.gypi b/gyp/utils.gypi index e4033e9020..347cc24fef 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -18,7 +18,6 @@ '<(skia_include_path)/utils/SkCubicInterval.h', '<(skia_include_path)/utils/SkCullPoints.h', '<(skia_include_path)/utils/SkDebugUtils.h', - '<(skia_include_path)/utils/SkDeferredCanvas.h', '<(skia_include_path)/utils/SkDumpCanvas.h', '<(skia_include_path)/utils/SkEventTracer.h', '<(skia_include_path)/utils/SkInterpolator.h', @@ -54,7 +53,6 @@ '<(skia_src_path)/utils/SkCullPoints.cpp', '<(skia_src_path)/utils/SkDashPath.cpp', '<(skia_src_path)/utils/SkDashPathPriv.h', - '<(skia_src_path)/utils/SkDeferredCanvas.cpp', '<(skia_src_path)/utils/SkDumpCanvas.cpp', '<(skia_src_path)/utils/SkEventTracer.cpp', '<(skia_src_path)/utils/SkFloatUtils.h', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 6a88d68322..35c0fb75b5 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -1370,7 +1370,6 @@ private: friend class AutoDrawLooper; friend class SkLua; // needs top layer size and offset friend class SkDebugCanvas; // needs experimental fAllowSimplifyClip - friend class SkDeferredDevice; // needs getTopDevice() friend class SkSurface_Raster; // needs getDevice() friend class SkRecorder; // InitFlags friend class SkNoSaveLayerCanvas; // InitFlags diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 3715b1d615..9e01e62649 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -373,7 +373,6 @@ private: friend class SkDrawIter; friend class SkDeviceFilteredPaint; friend class SkImageFilter::Proxy; - friend class SkDeferredDevice; // for newSurface friend class SkNoPixelsBitmapDevice; friend class SkSurface_Raster; diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h deleted file mode 100644 index 9b50caf89b..0000000000 --- a/include/utils/SkDeferredCanvas.h +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2012 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 "SkPixelRef.h" - -class SkDeferredDevice; -class SkImage; -class SkSurface; - -/** \class SkDeferredCanvas - Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe 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 SK_API NotificationClient; - - /** Construct a canvas with the specified surface to draw into. - This factory must be used for newImageSnapshot to work. - @param surface Specifies a surface for the canvas to draw into. - */ - static SkDeferredCanvas* Create(SkSurface* surface); - - virtual ~SkDeferredCanvas(); - - /** - * Specify the surface to be used by this canvas. Calling setSurface will - * release the previously set surface or device. Takes a reference on the - * surface. - * - * @param surface The surface that the canvas will raw into - * @return The surface argument, for convenience. - */ - SkSurface* setSurface(SkSurface* surface); - - /** - * Specify a NotificationClient to be used by this canvas. Calling - * setNotificationClient will release the previously set - * NotificationClient, if any. SkDeferredCanvas does not take ownership - * of the notification client. Therefore user code is resposible - * for its destruction. The notification client must be unregistered - * by calling setNotificationClient(NULL) if it is destroyed before - * this canvas. - * Note: Must be called after the device is set with setDevice. - * - * @param notificationClient interface for dispatching notifications - * @return The notificationClient argument, for convenience. - */ - NotificationClient* setNotificationClient(NotificationClient* notificationClient); - - /** - * 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); - - /** - * Returns true if deferred drawing is currenlty enabled. - */ - bool isDeferredDrawing() const; - - /** - * Returns true if the canvas contains a fresh frame. A frame is - * considered fresh when its content do not depend on the contents - * of the previous frame. For example, if a canvas is cleared before - * drawing each frame, the frames will all be considered fresh. - * A frame is defined as the graphics image produced by as a result - * of all the canvas draws operation executed between two successive - * calls to isFreshFrame. The result of isFreshFrame is computed - * conservatively, so it may report false negatives. - */ - bool isFreshFrame() const; - - /** - * Returns canvas's size. - */ - SkISize getCanvasSize() const; - - /** - * Returns true if the canvas has recorded draw commands that have - * not yet been played back. - */ - 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* newImageSnapshot(); - - /** - * Specify the maximum number of bytes to be allocated for the purpose - * of recording draw commands to this canvas. The default limit, is - * 64MB. - * @param maxStorage The maximum number of bytes to be allocated. - */ - void setMaxRecordingStorage(size_t maxStorage); - - /** - * Returns the number of bytes currently allocated for the purpose of - * recording draw commands. - */ - size_t storageAllocatedForRecording() const; - - /** - * Attempt to reduce the storage allocated for recording by evicting - * cache resources. - * @param bytesToFree minimum number of bytes that should be attempted to - * be freed. - * @return number of bytes actually freed. - */ - size_t freeMemoryIfPossible(size_t bytesToFree); - - /** - * Specifies the maximum size (in bytes) allowed for a given image to be - * rendered using the deferred canvas. - */ - void setBitmapSizeThreshold(size_t sizeThreshold); - size_t getBitmapSizeThreshold() const { return fBitmapSizeThreshold; } - - /** - * Executes all pending commands without drawing - */ - void silentFlush(); - - SkDrawFilter* setDrawFilter(SkDrawFilter* filter) override; - -protected: - void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; - void willRestore() override; - - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; - - void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; - virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint&) override; - virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint&) override; - virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint&) override; - virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint&) override; - virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) override; - virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) override; - - void onDrawPaint(const SkPaint&) override; - void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; - void onDrawRect(const SkRect&, const SkPaint&) override; - void onDrawOval(const SkRect&, const SkPaint&) override; - void onDrawRRect(const SkRRect&, const SkPaint&) override; - void onDrawPath(const SkPath&, const SkPaint&) override; - void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override; - void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*, - SrcRectConstraint) override; - void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; - void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, - const SkPaint*, SrcRectConstraint) override; - void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint*) override; - void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, - const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; - void onDrawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint&) override; - void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count, - SkXfermode::Mode, const SkRect* cullRect, const SkPaint*) override; - - void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRegion(const SkRegion&, SkRegion::Op) override; - - void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; - -public: - class NotificationClient { - public: - virtual ~NotificationClient() {} - - /** - * Called before executing one or several draw commands, which means - * once per flush when deferred rendering is enabled. - */ - virtual void prepareForDraw() {} - - /** - * Called after a recording a draw command if additional memory - * had to be allocated for recording. - * @param newAllocatedStorage same value as would be returned by - * storageAllocatedForRecording(), for convenience. - */ - virtual void storageAllocatedForRecordingChanged(size_t /*newAllocatedStorage*/) {} - - /** - * Called after pending draw commands have been flushed - */ - virtual void flushedDrawCommands() {} - - /** - * Called after pending draw commands have been skipped, meaning - * that they were optimized-out because the canvas is cleared - * or completely overwritten by the command currently being recorded. - */ - virtual void skippedPendingDrawCommands() {} - }; - -protected: - SkCanvas* canvasForDrawIter() override; - SkDeferredDevice* getDeferredDevice() const; - -private: - SkDeferredCanvas(SkDeferredDevice*); - - void recordedDrawCommand(); - SkCanvas* drawingCanvas() const; - SkCanvas* immediateCanvas() const; - bool isFullFrame(const SkRect*, const SkPaint*) const; - void validate() const; - void init(); - - - int fSaveLevel; - int fFirstSaveLayerIndex; - size_t fBitmapSizeThreshold; - bool fDeferredDrawing; - - mutable SkISize fCachedCanvasSize; - mutable bool fCachedCanvasSizeDirty; - - friend class SkDeferredCanvasTester; // for unit testing - typedef SkCanvas INHERITED; -}; - - -#endif diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp index ae9c88b7da..0a89b48e3c 100644 --- a/samplecode/SampleApp.cpp +++ b/samplecode/SampleApp.cpp @@ -15,7 +15,6 @@ #include "SkCanvas.h" #include "SkCommandLineFlags.h" #include "SkData.h" -#include "SkDeferredCanvas.h" #include "SkDevice.h" #include "SkDocument.h" #include "SkGPipe.h" @@ -188,7 +187,6 @@ public: switch (win->getDeviceType()) { case kRaster_DeviceType: // fallthrough case kPicture_DeviceType: // fallthrough - case kDeferred_DeviceType: // fallthrough case kGPU_DeviceType: // all these guys use the native backend fBackend = kNativeGL_BackEndType; @@ -216,7 +214,6 @@ public: switch (win->getDeviceType()) { case kRaster_DeviceType: // fallthrough case kPicture_DeviceType: // fallthrough - case kDeferred_DeviceType: // fallthrough case kGPU_DeviceType: // all these guys use the native interface glInterface.reset(GrGLCreateNativeInterface()); @@ -674,7 +671,6 @@ static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType SampleWindow::kANGLE_DeviceType, #endif // SK_ANGLE #endif // SK_SUPPORT_GPU - SampleWindow::kDeferred_DeviceType, SampleWindow::kRaster_DeviceType, }; SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, array_size_mismatch); @@ -830,7 +826,7 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev int itemID; itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0, - "Raster", "Picture", "OpenGL", "Deferred", + "Raster", "Picture", "OpenGL", #if SK_ANGLE "ANGLE", #endif @@ -1264,12 +1260,6 @@ SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) { canvas = fPDFDocument->beginPage(this->width(), this->height()); } else if (kPicture_DeviceType == fDeviceType) { canvas = fRecorder.beginRecording(9999, 9999, NULL, 0); - } else if (kDeferred_DeviceType == fDeviceType) { - fDeferredSurface.reset(canvas->newSurface(canvas->imageInfo())); - if (fDeferredSurface.get()) { - fDeferredCanvas.reset(SkDeferredCanvas::Create(fDeferredSurface)); - canvas = fDeferredCanvas; - } } else { canvas = this->INHERITED::beforeChildren(canvas); } @@ -1376,13 +1366,6 @@ void SampleWindow::afterChildren(SkCanvas* orig) { } else { picture->playback(orig); } - } else if (kDeferred_DeviceType == fDeviceType) { - SkAutoTUnref<SkImage> image(fDeferredCanvas->newImageSnapshot()); - if (image) { - orig->drawImage(image, 0, 0, NULL); - } - fDeferredCanvas.reset(NULL); - fDeferredSurface.reset(NULL); } // Do this after presentGL and other finishing, rather than in afterChild @@ -1949,7 +1932,6 @@ static const char* gDeviceTypePrefix[] = { "angle: ", #endif // SK_ANGLE #endif // SK_SUPPORT_GPU - "deferred: ", }; SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt, array_size_mismatch); diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h index c0f845d32c..1f8c33389a 100644 --- a/samplecode/SampleApp.h +++ b/samplecode/SampleApp.h @@ -23,7 +23,6 @@ class GrRenderTarget; class SkCanvas; class SkData; -class SkDeferredCanvas; class SkDocument; class SkEvent; class SkTypeface; @@ -41,7 +40,6 @@ public: kANGLE_DeviceType, #endif // SK_ANGLE #endif // SK_SUPPORT_GPU - kDeferred_DeviceType, kDeviceTypeCnt }; @@ -162,8 +160,6 @@ private: int fCurrIndex; SkPictureRecorder fRecorder; - SkAutoTDelete<SkSurface> fDeferredSurface; - SkAutoTDelete<SkDeferredCanvas> fDeferredCanvas; SkAutoTDelete<SkCanvas> fFlagsFilterCanvas; SkPath fClipPath; diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 0383dda3d5..55262ad571 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -209,9 +209,6 @@ static DrawType peek_op_and_size(SkWriter32* writer, size_t offset, uint32_t* si #endif//SK_DEBUG void SkPictureRecord::willRestore() { - // FIXME: SkDeferredCanvas needs to be refactored to respect - // save/restore balancing so that the following test can be - // turned on permanently. #if 0 SkASSERT(fRestoreOffsetStack.count() > 1); #endif diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp deleted file mode 100644 index 6ccfb5e4b3..0000000000 --- a/src/utils/SkDeferredCanvas.cpp +++ /dev/null @@ -1,1027 +0,0 @@ - -/* - * Copyright 2013 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 "SkChunkAlloc.h" -#include "SkClipStack.h" -#include "SkColorFilter.h" -#include "SkDevice.h" -#include "SkDrawFilter.h" -#include "SkGPipe.h" -#include "SkImage_Base.h" -#include "SkPaint.h" -#include "SkPaintPriv.h" -#include "SkRRect.h" -#include "SkShader.h" -#include "SkSurface.h" - -enum { - // Deferred canvas will auto-flush when recording reaches this limit - kDefaultMaxRecordingStorageBytes = 64*1024*1024, - kDeferredCanvasBitmapSizeThreshold = ~0U, // Disables this feature - - kNoSaveLayerIndex = -1, -}; - -enum PlaybackMode { - kNormal_PlaybackMode, - kSilent_PlaybackMode, -}; - -static uint64_t image_area(const SkImage* image) { - return sk_64_mul(image->width(), image->height()); -} - -// HACK -- see crbug.com/485243 -// -// Work around case where Blink gives us an image, but will "mutate" it (by changing its contents -// directly using webgl). Until that is fixed at the call-site, we treat gpu-backed-images as -// mutable for now (at least for the purposes of deferred canvas) -// -static bool should_draw_gpu_image_immediately(const SkImage* image) { - return as_IB(image)->getTexture() != NULL; -} - -static bool should_draw_immediately(const SkBitmap* bitmap, const SkImage* image, - const SkPaint* paint, size_t bitmapSizeThreshold) { - if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) || - (bitmap->getSize() > bitmapSizeThreshold))) { - return true; - } - if (image) { - if (should_draw_gpu_image_immediately(image) || image_area(image) > bitmapSizeThreshold) { - return true; - } - } - if (paint) { - SkShader* shader = paint->getShader(); - // Here we detect the case where the shader is an SkBitmapProcShader - // with a gpu texture attached. Checking this without RTTI - // requires making the assumption that only gradient shaders - // and SkBitmapProcShader implement asABitmap(). The following - // code may need to be revised if that assumption is ever broken. - if (shader && !shader->asAGradient(NULL)) { - SkBitmap bm; - if (shader->asABitmap(&bm, NULL, NULL) && - bm.getTexture()) { - return true; - } - } - } - return false; -} - -//----------------------------------------------------------------------------- -// DeferredPipeController -//----------------------------------------------------------------------------- - -class DeferredPipeController : public SkGPipeController { -public: - DeferredPipeController(); - void setPlaybackCanvas(SkCanvas*); - virtual ~DeferredPipeController(); - void* requestBlock(size_t minRequest, size_t* actual) override; - void notifyWritten(size_t bytes) override; - void playback(bool silent); - bool hasPendingCommands() const { return fAllocator.totalUsed() != 0; } - size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); } -private: - enum { - kMinBlockSize = 4096 - }; - struct PipeBlock { - PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; } - void* fBlock; - size_t fSize; - }; - void* fBlock; - size_t fBytesWritten; - SkChunkAlloc fAllocator; - SkTDArray<PipeBlock> fBlockList; - SkGPipeReader fReader; -}; - -DeferredPipeController::DeferredPipeController() : - fAllocator(kMinBlockSize) { - fBlock = NULL; - fBytesWritten = 0; -} - -DeferredPipeController::~DeferredPipeController() { - fAllocator.reset(); -} - -void DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) { - fReader.setCanvas(canvas); -} - -void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) { - if (fBlock) { - // Save the previous block for later - PipeBlock previousBloc(fBlock, fBytesWritten); - fBlockList.push(previousBloc); - } - size_t blockSize = SkTMax<size_t>(minRequest, kMinBlockSize); - fBlock = fAllocator.allocThrow(blockSize); - fBytesWritten = 0; - *actual = blockSize; - return fBlock; -} - -void DeferredPipeController::notifyWritten(size_t bytes) { - fBytesWritten += bytes; -} - -void DeferredPipeController::playback(bool silent) { - uint32_t flags = silent ? SkGPipeReader::kSilent_PlaybackFlag : 0; - for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) { - fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize, - flags); - } - fBlockList.reset(); - - if (fBlock) { - fReader.playback(fBlock, fBytesWritten, flags); - fBlock = NULL; - } - - // Release all allocated blocks - fAllocator.reset(); - - this->purgeCaches(); -} - -//----------------------------------------------------------------------------- -// SkDeferredDevice -//----------------------------------------------------------------------------- -class SkDeferredDevice : public SkBaseDevice { -public: - explicit SkDeferredDevice(SkSurface* surface); - ~SkDeferredDevice(); - - void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient); - SkCanvas* recordingCanvas(); - SkCanvas* immediateCanvas() const {return fImmediateCanvas;} - SkBaseDevice* immediateDevice() const {return fImmediateCanvas->getTopDevice();} - SkImage* newImageSnapshot(); - void setSurface(SkSurface* surface); - bool isFreshFrame(); - bool hasPendingCommands(); - size_t storageAllocatedForRecording() const; - size_t freeMemoryIfPossible(size_t bytesToFree); - void flushPendingCommands(PlaybackMode); - void skipPendingCommands(); - void setMaxRecordingStorage(size_t); - void recordedDrawCommand(); - void setIsDrawingToLayer(bool value) {fIsDrawingToLayer = value;} - - SkImageInfo imageInfo() const override; - - GrRenderTarget* accessRenderTarget() override; - - SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override; - - SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override; - -protected: - const SkBitmap& onAccessBitmap() override; - bool onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) override; - bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y) override; - - // None of the following drawing methods should ever get called on the - // deferred device - void drawPaint(const SkDraw&, const SkPaint& paint) override - {SkASSERT(0);} - void drawPoints(const SkDraw&, SkCanvas::PointMode mode, - size_t count, const SkPoint[], - const SkPaint& paint) override - {SkASSERT(0);} - void drawRect(const SkDraw&, const SkRect& r, - const SkPaint& paint) override - {SkASSERT(0);} - void drawOval(const SkDraw&, const SkRect&, const SkPaint&) override - {SkASSERT(0);} - void drawRRect(const SkDraw&, const SkRRect& rr, - const SkPaint& paint) override - {SkASSERT(0);} - void drawPath(const SkDraw&, const SkPath& path, - const SkPaint& paint, - const SkMatrix* prePathMatrix = NULL, - bool pathIsMutable = false) override - {SkASSERT(0);} - void drawBitmap(const SkDraw&, const SkBitmap& bitmap, - const SkMatrix& matrix, const SkPaint& paint) override - {SkASSERT(0);} - void drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect*, - const SkRect&, const SkPaint&, SkCanvas::SrcRectConstraint) override - {SkASSERT(0);} - void drawSprite(const SkDraw&, const SkBitmap& bitmap, - int x, int y, const SkPaint& paint) override - {SkASSERT(0);} - void drawImage(const SkDraw&, const SkImage*, SkScalar, SkScalar, const SkPaint&) override - {SkASSERT(0);} - void drawImageRect(const SkDraw&, const SkImage*, const SkRect*, const SkRect&, - const SkPaint&, SkCanvas::SrcRectConstraint) override - {SkASSERT(0);} - void drawImageNine(const SkDraw&, const SkImage*, const SkIRect&, const SkRect&, - const SkPaint&) override - {SkASSERT(0);} - void drawText(const SkDraw&, const void* text, size_t len, - SkScalar x, SkScalar y, const SkPaint& paint) override - {SkASSERT(0);} - void drawPosText(const SkDraw&, const void* text, size_t len, - const SkScalar pos[], int scalarsPerPos, - const SkPoint& offset, const SkPaint& paint) override - {SkASSERT(0);} - void drawTextOnPath(const SkDraw&, const void* text, - size_t len, const SkPath& path, - const SkMatrix* matrix, - const SkPaint& paint) override - {SkASSERT(0);} - 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) override - {SkASSERT(0);} - void drawPatch(const SkDraw&, const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) override - {SkASSERT(0);} - void drawAtlas(const SkDraw&, const SkImage* atlas, const SkRSXform[], const SkRect[], - const SkColor[], int count, SkXfermode::Mode, const SkPaint&) override - {SkASSERT(0);} - - void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, - const SkPaint&) override - {SkASSERT(0);} - - bool canHandleImageFilter(const SkImageFilter*) override { - return false; - } - bool filterImage(const SkImageFilter*, const SkBitmap&, - const SkImageFilter::Context&, SkBitmap*, SkIPoint*) override { - return false; - } - -private: - void flush() override; - void replaceBitmapBackendForRasterSurface(const SkBitmap&) override {} - - void beginRecording(); - void init(); - void aboutToDraw(); - void prepareForImmediatePixelWrite(); - - DeferredPipeController fPipeController; - SkGPipeWriter fPipeWriter; - SkCanvas* fImmediateCanvas; - SkCanvas* fRecordingCanvas; - SkSurface* fSurface; - SkDeferredCanvas::NotificationClient* fNotificationClient; - bool fFreshFrame; - bool fCanDiscardCanvasContents; - bool fIsDrawingToLayer; - size_t fMaxRecordingStorageBytes; - size_t fPreviousStorageAllocated; - - typedef SkBaseDevice INHERITED; -}; - -SkDeferredDevice::SkDeferredDevice(SkSurface* surface) - : INHERITED(surface->props()) { - fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; - fNotificationClient = NULL; - fImmediateCanvas = NULL; - fSurface = NULL; - this->setSurface(surface); - this->init(); -} - -void SkDeferredDevice::setSurface(SkSurface* surface) { - SkRefCnt_SafeAssign(fImmediateCanvas, surface->getCanvas()); - SkRefCnt_SafeAssign(fSurface, surface); - fPipeController.setPlaybackCanvas(fImmediateCanvas); -} - -void SkDeferredDevice::init() { - fRecordingCanvas = NULL; - fFreshFrame = true; - fIsDrawingToLayer = false; - fCanDiscardCanvasContents = false; - fPreviousStorageAllocated = 0; - fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; - fNotificationClient = NULL; - this->beginRecording(); -} - -SkDeferredDevice::~SkDeferredDevice() { - this->flushPendingCommands(kSilent_PlaybackMode); - SkSafeUnref(fImmediateCanvas); - SkSafeUnref(fSurface); -} - -void SkDeferredDevice::setMaxRecordingStorage(size_t maxStorage) { - fMaxRecordingStorageBytes = maxStorage; - this->recordingCanvas(); // Accessing the recording canvas applies the new limit. -} - -void SkDeferredDevice::beginRecording() { - SkASSERT(NULL == fRecordingCanvas); - fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0, - immediateDevice()->width(), immediateDevice()->height()); -} - -void SkDeferredDevice::setNotificationClient( - SkDeferredCanvas::NotificationClient* notificationClient) { - fNotificationClient = notificationClient; -} - -void SkDeferredDevice::skipPendingCommands() { - if (!fIsDrawingToLayer) { - fCanDiscardCanvasContents = true; - if (fPipeController.hasPendingCommands()) { - fFreshFrame = true; - flushPendingCommands(kSilent_PlaybackMode); - } - } -} - -bool SkDeferredDevice::isFreshFrame() { - bool ret = fFreshFrame; - fFreshFrame = false; - return ret; -} - -bool SkDeferredDevice::hasPendingCommands() { - return fPipeController.hasPendingCommands(); -} - -void SkDeferredDevice::aboutToDraw() { - if (fNotificationClient) { - fNotificationClient->prepareForDraw(); - } - if (fCanDiscardCanvasContents) { - if (fSurface) { - fSurface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); - } - fCanDiscardCanvasContents = false; - } -} - -void SkDeferredDevice::flushPendingCommands(PlaybackMode playbackMode) { - if (!fPipeController.hasPendingCommands()) { - return; - } - if (playbackMode == kNormal_PlaybackMode) { - aboutToDraw(); - } - fPipeWriter.flushRecording(true); - fPipeController.playback(kSilent_PlaybackMode == playbackMode); - if (fNotificationClient) { - if (playbackMode == kSilent_PlaybackMode) { - fNotificationClient->skippedPendingDrawCommands(); - } else { - fNotificationClient->flushedDrawCommands(); - } - } - - fPreviousStorageAllocated = storageAllocatedForRecording(); -} - -void SkDeferredDevice::flush() { - this->flushPendingCommands(kNormal_PlaybackMode); - fImmediateCanvas->flush(); -} - -size_t SkDeferredDevice::freeMemoryIfPossible(size_t bytesToFree) { - size_t val = fPipeWriter.freeMemoryIfPossible(bytesToFree); - fPreviousStorageAllocated = storageAllocatedForRecording(); - return val; -} - -size_t SkDeferredDevice::storageAllocatedForRecording() const { - return (fPipeController.storageAllocatedForRecording() - + fPipeWriter.storageAllocatedForRecording()); -} - -void SkDeferredDevice::recordedDrawCommand() { - size_t storageAllocated = this->storageAllocatedForRecording(); - - if (storageAllocated > fMaxRecordingStorageBytes) { - // First, attempt to reduce cache without flushing - size_t tryFree = storageAllocated - fMaxRecordingStorageBytes; - if (this->freeMemoryIfPossible(tryFree) < tryFree) { - // Flush is necessary to free more space. - this->flushPendingCommands(kNormal_PlaybackMode); - // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes - // which could cause a high flushing frequency. - this->freeMemoryIfPossible(~0U); - } - storageAllocated = this->storageAllocatedForRecording(); - } - - if (fNotificationClient && - storageAllocated != fPreviousStorageAllocated) { - fPreviousStorageAllocated = storageAllocated; - fNotificationClient->storageAllocatedForRecordingChanged(storageAllocated); - } -} - -SkCanvas* SkDeferredDevice::recordingCanvas() { - return fRecordingCanvas; -} - -SkImage* SkDeferredDevice::newImageSnapshot() { - this->flush(); - return fSurface ? fSurface->newImageSnapshot() : NULL; -} - -SkImageInfo SkDeferredDevice::imageInfo() const { - return immediateDevice()->imageInfo(); -} - -GrRenderTarget* SkDeferredDevice::accessRenderTarget() { - this->flushPendingCommands(kNormal_PlaybackMode); - return immediateDevice()->accessRenderTarget(); -} - -void SkDeferredDevice::prepareForImmediatePixelWrite() { - // The purpose of the following code is to make sure commands are flushed, that - // aboutToDraw() is called and that notifyContentWillChange is called, without - // calling anything redundantly. - if (fPipeController.hasPendingCommands()) { - this->flushPendingCommands(kNormal_PlaybackMode); - } else { - bool mustNotifyDirectly = !fCanDiscardCanvasContents; - this->aboutToDraw(); - if (mustNotifyDirectly) { - fSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode); - } - } - - fImmediateCanvas->flush(); -} - -bool SkDeferredDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, - int x, int y) { - SkASSERT(x >= 0 && y >= 0); - SkASSERT(x + info.width() <= width()); - SkASSERT(y + info.height() <= height()); - - const SkImageInfo deviceInfo = this->imageInfo(); - if (info.width() == deviceInfo.width() && info.height() == deviceInfo.height()) { - this->skipPendingCommands(); - } else { - this->flushPendingCommands(kNormal_PlaybackMode); - } - - this->prepareForImmediatePixelWrite(); - return immediateDevice()->onWritePixels(info, pixels, rowBytes, x, y); -} - -const SkBitmap& SkDeferredDevice::onAccessBitmap() { - this->flushPendingCommands(kNormal_PlaybackMode); - return immediateDevice()->accessBitmap(false); -} - -SkBaseDevice* SkDeferredDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) { - // Create a compatible non-deferred device. - // We do not create a deferred device because we know the new device - // will not be used with a deferred canvas (there is no API for that). - // And connecting a SkDeferredDevice to non-deferred canvas can result - // in unpredictable behavior. - return this->immediateDevice()->onCreateDevice(cinfo, layerPaint); -} - -SkSurface* SkDeferredDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) { - return this->immediateDevice()->newSurface(info, props); -} - -bool SkDeferredDevice::onReadPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, - int x, int y) { - this->flushPendingCommands(kNormal_PlaybackMode); - return fImmediateCanvas->readPixels(info, pixels, rowBytes, x, y); -} - -class AutoImmediateDrawIfNeeded { -public: - AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap, - const SkPaint* paint) { - this->init(canvas, bitmap, NULL, paint); - } - AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkImage* image, - const SkPaint* paint) { - this->init(canvas, NULL, image, paint); - } - - AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) { - this->init(canvas, NULL, NULL, paint); - } - - ~AutoImmediateDrawIfNeeded() { - if (fCanvas) { - fCanvas->setDeferredDrawing(true); - } - } -private: - void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkImage* image, - const SkPaint* paint) { - if (canvas.isDeferredDrawing() && - should_draw_immediately(bitmap, image, paint, canvas.getBitmapSizeThreshold())) { - canvas.setDeferredDrawing(false); - fCanvas = &canvas; - } else { - fCanvas = NULL; - } - } - - SkDeferredCanvas* fCanvas; -}; - -SkDeferredCanvas* SkDeferredCanvas::Create(SkSurface* surface) { - if (!surface) { - return NULL; - } - - SkAutoTUnref<SkDeferredDevice> deferredDevice(SkNEW_ARGS(SkDeferredDevice, (surface))); - return SkNEW_ARGS(SkDeferredCanvas, (deferredDevice)); -} - -SkDeferredCanvas::SkDeferredCanvas(SkDeferredDevice* device) : SkCanvas (device) { - this->init(); -} - -void SkDeferredCanvas::init() { - fBitmapSizeThreshold = kDeferredCanvasBitmapSizeThreshold; - fDeferredDrawing = true; // On by default - fCachedCanvasSize.setEmpty(); - fCachedCanvasSizeDirty = true; - fSaveLevel = 0; - fFirstSaveLayerIndex = kNoSaveLayerIndex; -} - -void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) { - this->validate(); - this->getDeferredDevice()->setMaxRecordingStorage(maxStorage); -} - -size_t SkDeferredCanvas::storageAllocatedForRecording() const { - return this->getDeferredDevice()->storageAllocatedForRecording(); -} - -size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) { - return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree); -} - -void SkDeferredCanvas::setBitmapSizeThreshold(size_t sizeThreshold) { - fBitmapSizeThreshold = sizeThreshold; -} - -void SkDeferredCanvas::recordedDrawCommand() { - if (fDeferredDrawing) { - this->getDeferredDevice()->recordedDrawCommand(); - } -} - -void SkDeferredCanvas::validate() const { - SkASSERT(this->getDevice()); -} - -SkCanvas* SkDeferredCanvas::drawingCanvas() const { - this->validate(); - return fDeferredDrawing ? this->getDeferredDevice()->recordingCanvas() : - this->getDeferredDevice()->immediateCanvas(); -} - -SkCanvas* SkDeferredCanvas::immediateCanvas() const { - this->validate(); - return this->getDeferredDevice()->immediateCanvas(); -} - -SkDeferredDevice* SkDeferredCanvas::getDeferredDevice() const { - return static_cast<SkDeferredDevice*>(this->getDevice()); -} - -void SkDeferredCanvas::setDeferredDrawing(bool val) { - this->validate(); // Must set device before calling this method - if (val != fDeferredDrawing) { - if (fDeferredDrawing) { - // Going live. - this->getDeferredDevice()->flushPendingCommands(kNormal_PlaybackMode); - } - fDeferredDrawing = val; - } -} - -bool SkDeferredCanvas::isDeferredDrawing() const { - return fDeferredDrawing; -} - -bool SkDeferredCanvas::isFreshFrame() const { - return this->getDeferredDevice()->isFreshFrame(); -} - -SkISize SkDeferredCanvas::getCanvasSize() const { - if (fCachedCanvasSizeDirty) { - fCachedCanvasSize = this->getBaseLayerSize(); - fCachedCanvasSizeDirty = false; - } - return fCachedCanvasSize; -} - -bool SkDeferredCanvas::hasPendingCommands() const { - return this->getDeferredDevice()->hasPendingCommands(); -} - -void SkDeferredCanvas::silentFlush() { - if (fDeferredDrawing) { - this->getDeferredDevice()->flushPendingCommands(kSilent_PlaybackMode); - } -} - -SkDeferredCanvas::~SkDeferredCanvas() { -} - -SkSurface* SkDeferredCanvas::setSurface(SkSurface* surface) { - SkDeferredDevice* deferredDevice = this->getDeferredDevice(); - SkASSERT(deferredDevice); - // By swapping the surface into the existing device, we preserve - // all pending commands, which can help to seamlessly recover from - // a lost accelerated graphics context. - deferredDevice->setSurface(surface); - fCachedCanvasSizeDirty = true; - return surface; -} - -SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient( - NotificationClient* notificationClient) { - - SkDeferredDevice* deferredDevice = this->getDeferredDevice(); - SkASSERT(deferredDevice); - if (deferredDevice) { - deferredDevice->setNotificationClient(notificationClient); - } - return notificationClient; -} - -SkImage* SkDeferredCanvas::newImageSnapshot() { - SkDeferredDevice* deferredDevice = this->getDeferredDevice(); - SkASSERT(deferredDevice); - return deferredDevice ? deferredDevice->newImageSnapshot() : NULL; -} - -bool SkDeferredCanvas::isFullFrame(const SkRect* rect, - const SkPaint* paint) const { - SkCanvas* canvas = this->drawingCanvas(); - SkISize canvasSize = this->getCanvasSize(); - 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 > SkIntToScalar(0) || - transformedRect.fTop > SkIntToScalar(0) || - transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) || - transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) { - return false; - } - } - - return this->getClipStack()->quickContains(SkRect::MakeXYWH(0, 0, - SkIntToScalar(canvasSize.fWidth), SkIntToScalar(canvasSize.fHeight))); -} - -void SkDeferredCanvas::willSave() { - fSaveLevel++; - this->drawingCanvas()->save(); - this->recordedDrawCommand(); - this->INHERITED::willSave(); -} - -SkCanvas::SaveLayerStrategy SkDeferredCanvas::willSaveLayer(const SkRect* bounds, - const SkPaint* paint, SaveFlags flags) { - fSaveLevel++; - if (fFirstSaveLayerIndex == kNoSaveLayerIndex) { - fFirstSaveLayerIndex = fSaveLevel; - this->getDeferredDevice()->setIsDrawingToLayer(true); - } - this->drawingCanvas()->saveLayer(bounds, paint, flags); - this->recordedDrawCommand(); - this->INHERITED::willSaveLayer(bounds, paint, flags); - // No need for a full layer. - return kNoLayer_SaveLayerStrategy; -} - -void SkDeferredCanvas::willRestore() { - SkASSERT(fFirstSaveLayerIndex == kNoSaveLayerIndex || fFirstSaveLayerIndex <= fSaveLevel); - if (fFirstSaveLayerIndex == fSaveLevel) { - fFirstSaveLayerIndex = kNoSaveLayerIndex; - this->getDeferredDevice()->setIsDrawingToLayer(false); - } - fSaveLevel--; - this->drawingCanvas()->restore(); - this->recordedDrawCommand(); - this->INHERITED::willRestore(); -} - -void SkDeferredCanvas::didConcat(const SkMatrix& matrix) { - this->drawingCanvas()->concat(matrix); - this->recordedDrawCommand(); - this->INHERITED::didConcat(matrix); -} - -void SkDeferredCanvas::didSetMatrix(const SkMatrix& matrix) { - this->drawingCanvas()->setMatrix(matrix); - this->recordedDrawCommand(); - this->INHERITED::didSetMatrix(matrix); -} - -void SkDeferredCanvas::onClipRect(const SkRect& rect, - SkRegion::Op op, - ClipEdgeStyle edgeStyle) { - this->drawingCanvas()->clipRect(rect, op, kSoft_ClipEdgeStyle == edgeStyle); - this->INHERITED::onClipRect(rect, op, edgeStyle); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onClipRRect(const SkRRect& rrect, - SkRegion::Op op, - ClipEdgeStyle edgeStyle) { - this->drawingCanvas()->clipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle); - this->INHERITED::onClipRRect(rrect, op, edgeStyle); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onClipPath(const SkPath& path, - SkRegion::Op op, - ClipEdgeStyle edgeStyle) { - this->drawingCanvas()->clipPath(path, op, kSoft_ClipEdgeStyle == edgeStyle); - this->INHERITED::onClipPath(path, op, edgeStyle); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { - this->drawingCanvas()->clipRegion(deviceRgn, op); - this->INHERITED::onClipRegion(deviceRgn, op); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPaint(const SkPaint& paint) { - if (fDeferredDrawing && this->isFullFrame(NULL, &paint) && SkPaintPriv::Overwrites(paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPaint(paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPoints(PointMode mode, size_t count, - const SkPoint pts[], const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPoints(mode, count, pts, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawOval(rect, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { - if (fDeferredDrawing && this->isFullFrame(&rect, &paint) && SkPaintPriv::Overwrites(paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawRect(rect, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - if (rrect.isRect()) { - this->SkDeferredCanvas::drawRect(rrect.getBounds(), paint); - } else if (rrect.isOval()) { - this->SkDeferredCanvas::drawOval(rrect.getBounds(), paint); - } else { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawRRect(rrect, paint); - this->recordedDrawCommand(); - } -} - -void SkDeferredCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawDRRect(outer, inner, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPath(path, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, - SkScalar top, const SkPaint* paint) { - SkRect bitmapRect = SkRect::MakeXYWH(left, top, - SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); - if (fDeferredDrawing && - this->isFullFrame(&bitmapRect, paint) && - SkPaintPriv::Overwrites(bitmap, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); - this->drawingCanvas()->drawBitmap(bitmap, left, top, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, - const SkRect& dst, const SkPaint* paint, - SrcRectConstraint constraint) { - if (fDeferredDrawing && - this->isFullFrame(&dst, paint) && - SkPaintPriv::Overwrites(bitmap, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); - this->drawingCanvas()->legacy_drawBitmapRect(bitmap, src, dst, paint, (SrcRectConstraint)constraint); - this->recordedDrawCommand(); -} - - -void SkDeferredCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, - const SkPaint* paint) { - SkRect bounds = SkRect::MakeXYWH(x, y, - SkIntToScalar(image->width()), SkIntToScalar(image->height())); - if (fDeferredDrawing && - this->isFullFrame(&bounds, paint) && - SkPaintPriv::Overwrites(image, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, image, paint); - this->drawingCanvas()->drawImage(image, x, y, paint); - this->recordedDrawCommand(); -} -void SkDeferredCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) { - if (fDeferredDrawing && - this->isFullFrame(&dst, paint) && - SkPaintPriv::Overwrites(image, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, image, paint); - this->drawingCanvas()->legacy_drawImageRect(image, src, dst, paint, constraint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, - const SkRect& dst, const SkPaint* paint) { - if (fDeferredDrawing && - this->isFullFrame(&dst, paint) && - SkPaintPriv::Overwrites(image, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, image, paint); - this->drawingCanvas()->drawImageNine(image, center, dst, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawBitmapNine(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 - AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); - this->drawingCanvas()->drawBitmapNine(bitmap, center, dst, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint) { - SkRect bitmapRect = SkRect::MakeXYWH( - SkIntToScalar(left), - SkIntToScalar(top), - SkIntToScalar(bitmap.width()), - SkIntToScalar(bitmap.height())); - if (fDeferredDrawing && - this->isFullFrame(&bitmapRect, paint) && - SkPaintPriv::Overwrites(bitmap, paint)) { - this->getDeferredDevice()->skipPendingCommands(); - } - - AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); - this->drawingCanvas()->drawSprite(bitmap, left, top, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawText(text, byteLength, x, y, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPosText(text, byteLength, pos, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawTextOnPath(text, byteLength, path, matrix, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawTextBlob(blob, x, y, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { - this->drawingCanvas()->drawPicture(picture, matrix, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], - const SkPoint texs[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode, - indices, indexCount, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, &paint); - this->drawingCanvas()->drawPatch(cubics, colors, texCoords, xmode, paint); - this->recordedDrawCommand(); -} - -void SkDeferredCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], - const SkRect tex[], const SkColor colors[], int count, - SkXfermode::Mode mode, const SkRect* cullRect, - const SkPaint* paint) { - AutoImmediateDrawIfNeeded autoDraw(*this, paint); - this->drawingCanvas()->drawAtlas(atlas, xform, tex, colors, count, mode, cullRect, paint); - this->recordedDrawCommand(); -} - -SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) { - this->drawingCanvas()->setDrawFilter(filter); - this->INHERITED::setDrawFilter(filter); - this->recordedDrawCommand(); - return filter; -} - -SkCanvas* SkDeferredCanvas::canvasForDrawIter() { - return this->drawingCanvas(); -} diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp index 5aa3d46628..c80e6bef95 100644 --- a/tests/CanvasTest.cpp +++ b/tests/CanvasTest.cpp @@ -46,7 +46,6 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkClipStack.h" -#include "SkDeferredCanvas.h" #include "SkDevice.h" #include "SkDocument.h" #include "SkMatrix.h" @@ -72,12 +71,6 @@ static void createBitmap(SkBitmap* bm, SkColor color) { bm->eraseColor(color); } -static SkSurface* createSurface(SkColor color) { - SkSurface* surface = SkSurface::NewRasterN32Premul(kWidth, kHeight); - surface->getCanvas()->clear(color); - return surface; -} - /////////////////////////////////////////////////////////////////////////////// // Constants used by test steps const SkPoint kTestPoints[] = { @@ -239,16 +232,8 @@ static void test_clipstack(skiatest::Reporter* reporter) { static const char* const kDefaultAssertMessageFormat = "%s"; static const char* const kCanvasDrawAssertMessageFormat = "Drawing test step %s with SkCanvas"; -static const char* const kDeferredDrawAssertMessageFormat = - "Drawing test step %s with SkDeferredCanvas"; static const char* const kNWayDrawAssertMessageFormat = "Drawing test step %s with SkNWayCanvas"; -static const char* const kDeferredPreFlushAssertMessageFormat = - "test step %s, SkDeferredCanvas state consistency before flush"; -static const char* const kDeferredPostFlushPlaybackAssertMessageFormat = - "test step %s, SkDeferredCanvas playback canvas state consistency after flush"; -static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat = - "test step %s, SkDeferredCanvas playback canvas state consistency after silent flush"; static const char* const kNWayStateAssertMessageFormat = "test step %s, SkNWayCanvas state consistency"; static const char* const kNWayIndirect1StateAssertMessageFormat = @@ -586,49 +571,6 @@ static void TestPdfDevice(skiatest::Reporter* reporter, REPORTER_ASSERT(reporter, doc->close()); } -// The following class groups static functions that need to access -// the privates members of SkDeferredCanvas -class SkDeferredCanvasTester { -public: - static void TestDeferredCanvasStateConsistency( - skiatest::Reporter* reporter, - const TestData& d, - CanvasTestStep* testStep, - const SkCanvas& referenceCanvas, bool silent) { - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> deferredCanvas(SkDeferredCanvas::Create(surface.get())); - - testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat); - testStep->draw(deferredCanvas, d, reporter); - testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, d, deferredCanvas, &referenceCanvas, testStep); - - if (silent) { - deferredCanvas->silentFlush(); - } else { - deferredCanvas->flush(); - } - - testStep->setAssertMessageFormat( - silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat : - kDeferredPostFlushPlaybackAssertMessageFormat); - AssertCanvasStatesEqual(reporter, d, deferredCanvas->immediateCanvas(), - &referenceCanvas, testStep); - - // Verified that deferred canvas state is not affected by flushing - // pending draw operations - - // The following test code is commented out because it currently fails. - // Issue: http://code.google.com/p/skia/issues/detail?id=496 - /* - testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, - testStep); - */ - } -}; - // unused static void TestNWayCanvasStateConsistency( skiatest::Reporter* reporter, @@ -675,10 +617,6 @@ static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const Tes testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat); testStep->draw(&referenceCanvas, d, reporter); - SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, d, testStep, referenceCanvas, false); - - SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, d, testStep, referenceCanvas, true); - // The following test code is disabled because SkNWayCanvas does not // report correct clipping and device bounds information // Issue: http://code.google.com/p/skia/issues/detail?id=501 diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp deleted file mode 100644 index 600315ea2c..0000000000 --- a/tests/DeferredCanvasTest.cpp +++ /dev/null @@ -1,949 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "../src/image/SkImagePriv.h" -#include "../src/image/SkSurface_Base.h" -#include "SkBitmap.h" -#include "SkBitmapProcShader.h" -#include "SkDeferredCanvas.h" -#include "SkGradientShader.h" -#include "SkPath.h" -#include "SkShader.h" -#include "SkSurface.h" -#include "Test.h" -#include "sk_tool_utils.h" - -#if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#else -class GrContextFactory; -#endif - -static const int gWidth = 2; -static const int gHeight = 2; - -static void create(SkBitmap* bm, SkColor color) { - bm->allocN32Pixels(gWidth, gHeight); - bm->eraseColor(color); -} - -static SkSurface* createSurface(SkColor color) { - SkSurface* surface = SkSurface::NewRasterN32Premul(gWidth, gHeight); - surface->getCanvas()->clear(color); - return surface; -} - -static SkPMColor read_pixel(SkSurface* surface, int x, int y) { - SkPMColor pixel = 0; - SkBitmap bitmap; - bitmap.installPixels(SkImageInfo::MakeN32Premul(1, 1), &pixel, 4); - SkCanvas canvas(bitmap); - - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - surface->draw(&canvas, -SkIntToScalar(x), -SkIntToScalar(y), &paint); - return pixel; -} - -class MockSurface : public SkSurface_Base { -public: - MockSurface(int width, int height) : SkSurface_Base(width, height, NULL) { - clearCounts(); - fBitmap.allocN32Pixels(width, height); - } - - SkCanvas* onNewCanvas() override { - return SkNEW_ARGS(SkCanvas, (fBitmap)); - } - - SkSurface* onNewSurface(const SkImageInfo&) override { - return NULL; - } - - SkImage* onNewImageSnapshot(Budgeted) override { - return SkNewImageFromRasterBitmap(fBitmap, &this->props()); - } - - void onCopyOnWrite(ContentChangeMode mode) override { - if (mode == SkSurface::kDiscard_ContentChangeMode) { - fCOWDiscardCount++; - } else { - fCOWRetainCount++; - } - } - - void onDiscard() override { - fDiscardCount++; - } - - void clearCounts() { - fCOWDiscardCount = 0; - fCOWRetainCount = 0; - fDiscardCount = 0; - } - - int fCOWDiscardCount; - int fCOWRetainCount; - int fDiscardCount; - SkBitmap fBitmap; -}; - -static void TestDeferredCanvasWritePixelsToSurface(skiatest::Reporter* reporter) { - SkAutoTUnref<MockSurface> surface(SkNEW_ARGS(MockSurface, (10, 10))); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - SkBitmap srcBitmap; - srcBitmap.allocPixels(SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType)); - srcBitmap.eraseColor(SK_ColorGREEN); - // Tests below depend on this bitmap being recognized as opaque - - // Preliminary sanity check: no copy on write if no active snapshot - // Discard notification happens on SkSurface::onDiscard, since no - // active snapshot. - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); - - // Case 1: Discard notification happens upon flushing - // with an Image attached. - surface->clearCounts(); - SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 2: Opaque writePixels - surface->clearCounts(); - SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 3: writePixels that partially covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 4: unpremultiplied opaque writePixels that entirely - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 0, 0); - REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 5: unpremultiplied opaque writePixels that partially - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 6: unpremultiplied opaque writePixels that entirely - // covers the canvas, preceded by clear - surface->clearCounts(); - SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 0, 0); - REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 7: unpremultiplied opaque writePixels that partially - // covers the canvas, preceeded by a clear - surface->clearCounts(); - SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); // because of the clear - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - // Case 8: unpremultiplied opaque writePixels that partially - // covers the canvas, preceeded by a drawREct that partially - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - SkPaint paint; - canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); -} - -static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - canvas->clear(0x00000000); - - // verify that clear was deferred - REPORTER_ASSERT(reporter, 0xFFFFFFFF == read_pixel(surface, 0, 0)); - - canvas->flush(); - - // verify that clear was executed - REPORTER_ASSERT(reporter, 0 == read_pixel(surface, 0, 0)); -} - -static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { - SkRect fullRect; - fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), - SkIntToScalar(gHeight)); - SkRect partialRect; - partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(1), SkIntToScalar(1)); - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - // verify that frame is intially fresh - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - // no clearing op since last call to isFreshFrame -> not fresh - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - - // Verify that clear triggers a fresh frame - canvas->clear(0x00000000); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - - // Verify that clear with saved state triggers a fresh frame - canvas->save(); - canvas->clear(0x00000000); - canvas->restore(); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - - // Verify that clear within a layer does NOT trigger a fresh frame - canvas->saveLayer(NULL, NULL); - canvas->clear(0x00000000); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - - // Verify that full frame rects with different forms of opaque paint - // trigger frames to be marked as fresh - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - paint.setXfermodeMode(SkXfermode::kSrcIn_Mode); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - SkBitmap bmp; - create(&bmp, 0xFFFFFFFF); - bmp.setAlphaType(kOpaque_SkAlphaType); - SkShader* shader = SkShader::CreateBitmapShader(bmp, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } - - // Verify that full frame rects with different forms of non-opaque paint - // do not trigger frames to be marked as fresh - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(254); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - // Defining a cone that partially overlaps the canvas - const SkPoint pt1 = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0)); - const SkScalar r1 = SkIntToScalar(1); - const SkPoint pt2 = SkPoint::Make(SkIntToScalar(10), SkIntToScalar(0)); - const SkScalar r2 = SkIntToScalar(5); - const SkColor colors[2] = {SK_ColorWHITE, SK_ColorWHITE}; - const SkScalar pos[2] = {0, SK_Scalar1}; - SkShader* shader = SkGradientShader::CreateTwoPointConical( - pt1, r1, pt2, r2, colors, pos, 2, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - SkBitmap bmp; - create(&bmp, 0xFFFFFFFF); - bmp.setAlphaType(kPremul_SkAlphaType); - SkShader* shader = SkShader::CreateBitmapShader(bmp, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that incomplete coverage does not trigger a fresh frame - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(partialRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that incomplete coverage due to clipping does not trigger a fresh - // frame - { - canvas->save(); - canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - canvas->save(); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - SkPath path; - path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2)); - canvas->clipPath(path, SkRegion::kIntersect_Op, false); - canvas->drawRect(fullRect, paint); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that stroked rect does not trigger a fresh frame - { - SkPaint paint; - paint.setStyle(SkPaint::kStroke_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify kSrcMode triggers a fresh frame even with transparent color - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(100); - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } -} - -class NotificationCounter : public SkDeferredCanvas::NotificationClient { -public: - NotificationCounter() { - fPrepareForDrawCount = fStorageAllocatedChangedCount = - fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0; - } - - void prepareForDraw() override { - fPrepareForDrawCount++; - } - void storageAllocatedForRecordingChanged(size_t) override { - fStorageAllocatedChangedCount++; - } - void flushedDrawCommands() override { - fFlushedDrawCommandsCount++; - } - void skippedPendingDrawCommands() override { - fSkippedPendingDrawCommandsCount++; - } - - int fPrepareForDrawCount; - int fStorageAllocatedChangedCount; - int fFlushedDrawCommandsCount; - int fSkippedPendingDrawCommandsCount; - -private: - typedef SkDeferredCanvas::NotificationClient INHERITED; -}; - -// Verifies that the deferred canvas triggers a flush when its memory -// limit is exceeded -static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - canvas->setMaxRecordingStorage(160000); - - SkBitmap sourceImage; - // 100 by 100 image, takes 40,000 bytes in memory - sourceImage.allocN32Pixels(100, 100); - sourceImage.eraseColor(SK_ColorGREEN); - - for (int i = 0; i < 5; i++) { - sourceImage.notifyPixelsChanged(); // to force re-serialization - canvas->drawBitmap(sourceImage, 0, 0, NULL); - } - - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); -} - -static void TestDeferredCanvasSilentFlush(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(createSurface(0)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - canvas->silentFlush(); // will skip the initial clear that was recorded in createSurface - - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); -} - -static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - const int imageCount = 2; - SkBitmap sourceImages[imageCount]; - for (int i = 0; i < imageCount; i++) { - sourceImages[i].allocN32Pixels(100, 100); - sourceImages[i].eraseColor(SK_ColorGREEN); - } - - size_t bitmapSize = sourceImages[0].getSize(); - - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount); - // stored bitmap + drawBitmap command - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > bitmapSize); - - // verify that nothing can be freed at this point - REPORTER_ASSERT(reporter, 0 == canvas->freeMemoryIfPossible(~0U)); - - // verify that flush leaves image in cache - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() >= bitmapSize); - - // verify that after a flush, cached image can be freed - REPORTER_ASSERT(reporter, canvas->freeMemoryIfPossible(~0U) >= bitmapSize); - - // Verify that caching works for avoiding multiple copies of the same bitmap - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount); - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < 2 * bitmapSize); - - // Verify partial eviction based on bytesToFree - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2 * bitmapSize); - size_t bytesFreed = canvas->freeMemoryIfPossible(1); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize); - REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize); - - // Verifiy that partial purge works, image zero is in cache but not reffed by - // a pending draw, while image 1 is locked-in. - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - canvas->flush(); - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - bytesFreed = canvas->freeMemoryIfPossible(~0U); - // only one bitmap should have been freed. - REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize); - REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize); - // Clear for next test - canvas->flush(); - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < bitmapSize); - - // Verify the image cache is sensitive to genID bumps - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - sourceImages[1].notifyPixelsChanged(); - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2*bitmapSize); - - // Verify that nothing in this test caused commands to be skipped - REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount); -} - -static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - canvas->clear(0x0); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - -} - -static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) { - // This is a regression test for crbug.com/155875 - // This test covers a code path that inserts bitmaps into the bitmap heap through the - // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through - // the flattening and unflattening of the shader. - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - // test will fail if nbIterations is not in sync with - // BITMAPS_TO_KEEP in SkGPipeWrite.cpp - const int nbIterations = 5; - size_t bytesAllocated = 0; - for(int pass = 0; pass < 2; ++pass) { - for(int i = 0; i < nbIterations; ++i) { - SkPaint paint; - SkBitmap paintPattern; - paintPattern.allocN32Pixels(10, 10); - paintPattern.eraseColor(SK_ColorGREEN); - paint.setShader(SkNEW_ARGS(SkBitmapProcShader, - (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref(); - canvas->drawPaint(paint); - canvas->flush(); - - // In the first pass, memory allocation should be monotonically increasing as - // the bitmap heap slots fill up. In the second pass memory allocation should be - // stable as bitmap heap slots get recycled. - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - if (pass == 0) { - REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated); - bytesAllocated = newBytesAllocated; - } else { - REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated); - } - } - } - // All cached resources should be evictable since last canvas call was flush() - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, 0 == canvas->storageAllocatedForRecording()); -} - -static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - - SkBitmap sourceImage; - // 100 by 100 image, takes 40,000 bytes in memory - sourceImage.allocN32Pixels(100, 100); - sourceImage.eraseColor(SK_ColorGREEN); - - // 1 under : should not store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(39999); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated == 0); - } - - // exact value : should store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(40000); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated > 0); - } - - // 1 over : should still store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(40001); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated > 0); - } -} - -static void TestDeferredCanvasImageFreeAfterFlush(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkImage> sourceImage(sourceSurface->newImageSnapshot()); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - canvas->drawImage(sourceImage, 0, 0, NULL); - - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated > 0); - - canvas->flush(); - - newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated == 0); -} - -typedef const 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. -static PixelPtr get_surface_ptr(SkSurface* surface, bool useGpu) { -#if SK_SUPPORT_GPU - if (useGpu) { - return surface->getCanvas()->internal_private_accessTopLayerRenderTarget()->asTexture(); - } else -#endif - { - return surface->peekPixels(NULL, NULL); - } -} - -static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) { - SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10); - bool useGpu = SkToBool(factory); - int cnt; -#if SK_SUPPORT_GPU - if (useGpu) { - cnt = GrContextFactory::kGLContextTypeCnt; - } else { - cnt = 1; - } -#else - SkASSERT(!useGpu); - cnt = 1; -#endif - for (int i = 0; i < cnt; ++i) { - SkSurface* surface; -#if SK_SUPPORT_GPU - if (useGpu) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - GrContext* context = factory->get(glCtxType); - if (NULL == context) { - return; - } - - surface = - SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); - } else -#endif - { - surface = SkSurface::NewRaster(imageSpec); - } - SkASSERT(surface); - SkAutoTUnref<SkSurface> aur(surface); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface)); - - SkImage* image1 = canvas->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_i1(image1); - PixelPtr pixels1 = get_surface_ptr(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->newImageSnapshot(); - 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->newImageSnapshot(); - 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 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels1 != pixels2); - // Verify copy-on write with a draw operation that gets deferred by - // the in order draw buffer. - SkPaint paint; - canvas->drawPaint(paint); - SkImage* image4 = canvas->newImageSnapshot(); // implicit flush - SkAutoTUnref<SkImage> aur_i4(image4); - REPORTER_ASSERT(reporter, image4->uniqueID() != image3->uniqueID()); - PixelPtr pixels3 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels2 != pixels3); - // 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_ColorWHITE); - canvas->flush(); - PixelPtr pixels4 = get_surface_ptr(surface, useGpu); - canvas->drawPaint(paint); - canvas->flush(); - PixelPtr pixels5 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels4 == pixels5); - } -} - -static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContextFactory* factory) { - SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10); - SkSurface* surface; - SkSurface* alternateSurface; - bool useGpu = SkToBool(factory); - int cnt; -#if SK_SUPPORT_GPU - if (useGpu) { - cnt = GrContextFactory::kGLContextTypeCnt; - } else { - cnt = 1; - } -#else - SkASSERT(!useGpu); - cnt = 1; -#endif - - for (int i = 0; i < cnt; ++i) { -#if SK_SUPPORT_GPU - if (useGpu) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - GrContext* context = factory->get(glCtxType); - if (NULL == context) { - continue; - } - surface = - SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); - alternateSurface = - SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); - } else -#endif - { - surface = SkSurface::NewRaster(imageSpec); - alternateSurface = SkSurface::NewRaster(imageSpec); - } - SkASSERT(surface); - SkASSERT(alternateSurface); - SkAutoTUnref<SkSurface> aur1(surface); - SkAutoTUnref<SkSurface> aur2(alternateSurface); - PixelPtr pixels1 = get_surface_ptr(surface, useGpu); - PixelPtr pixels2 = get_surface_ptr(alternateSurface, useGpu); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface)); - SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot()); - canvas->setSurface(alternateSurface); - SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, image1->uniqueID() != image2->uniqueID()); - // Verify that none of the above operations triggered a surface copy on write. - REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1); - REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) == pixels2); - // Verify that a flushed draw command will trigger a copy on write on alternateSurface. - canvas->clear(SK_ColorWHITE); - canvas->flush(); - REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1); - REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) != pixels2); - } -} - -static void TestDeferredCanvasCreateCompatibleDevice(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); - SkAutoTUnref<SkSurface> secondarySurface(canvas->newSurface(info)); - - SkRect rect = SkRect::MakeWH(5, 5); - SkPaint paint; - // After spawning a compatible canvas: - // 1) Verify that secondary canvas is usable and does not report to the notification client. - surface->getCanvas()->drawRect(rect, paint); - REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 0); - // 2) Verify that original canvas is usable and still reports to the notification client. - canvas->drawRect(rect, paint); - REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 1); -} - -static void TestDeferredCanvasGetCanvasSize(skiatest::Reporter* reporter) { - SkRect rect; - rect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight)); - SkRect clip; - clip.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1)); - - SkPaint paint; - SkISize size = SkISize::Make(gWidth, gHeight); - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - SkSurface* newSurface = SkSurface::NewRasterN32Premul(4, 4); - SkAutoTUnref<SkSurface> aur(newSurface); - - for (int i = 0; i < 2; ++i) { - if (i == 1) { - canvas->setSurface(newSurface); - size = SkISize::Make(4, 4); - } - - // verify that canvas size is correctly initialized or set - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear, clip and draw the canvas will not change its size - canvas->clear(0x00000000); - canvas->clipRect(clip, SkRegion::kIntersect_Op, false); - canvas->drawRect(rect, paint); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that flush the canvas will not change its size - canvas->flush(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear canvas with saved state will not change its size - canvas->save(); - canvas->clear(0xFFFFFFFF); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that restore canvas state will not change its size - canvas->restore(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear within a layer will not change canvas size - canvas->saveLayer(&clip, &paint); - canvas->clear(0x00000000); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that restore from a layer will not change canvas size - canvas->restore(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - } -} - -DEF_TEST(DeferredCanvas_CPU, reporter) { - TestDeferredCanvasFlush(reporter); - TestDeferredCanvasSilentFlush(reporter); - TestDeferredCanvasFreshFrame(reporter); - TestDeferredCanvasMemoryLimit(reporter); - TestDeferredCanvasBitmapCaching(reporter); - TestDeferredCanvasSkip(reporter); - TestDeferredCanvasBitmapShaderNoLeak(reporter); - TestDeferredCanvasBitmapSizeThreshold(reporter); - TestDeferredCanvasImageFreeAfterFlush(reporter); - TestDeferredCanvasCreateCompatibleDevice(reporter); - TestDeferredCanvasWritePixelsToSurface(reporter); - TestDeferredCanvasGetCanvasSize(reporter); - TestDeferredCanvasSurface(reporter, NULL); - TestDeferredCanvasSetSurface(reporter, NULL); -} - -DEF_GPUTEST(DeferredCanvas_GPU, reporter, factory) { - if (factory != NULL) { - TestDeferredCanvasSurface(reporter, factory); - TestDeferredCanvasSetSurface(reporter, factory); - } -} |