michael@0: michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkDeferredCanvas.h" michael@0: michael@0: #include "SkBitmapDevice.h" michael@0: #include "SkChunkAlloc.h" michael@0: #include "SkColorFilter.h" michael@0: #include "SkDrawFilter.h" michael@0: #include "SkGPipe.h" michael@0: #include "SkPaint.h" michael@0: #include "SkPaintPriv.h" michael@0: #include "SkRRect.h" michael@0: #include "SkShader.h" michael@0: #include "SkSurface.h" michael@0: michael@0: enum { michael@0: // Deferred canvas will auto-flush when recording reaches this limit michael@0: kDefaultMaxRecordingStorageBytes = 64*1024*1024, michael@0: kDeferredCanvasBitmapSizeThreshold = ~0U, // Disables this feature michael@0: }; michael@0: michael@0: enum PlaybackMode { michael@0: kNormal_PlaybackMode, michael@0: kSilent_PlaybackMode, michael@0: }; michael@0: michael@0: static bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint, michael@0: size_t bitmapSizeThreshold) { michael@0: if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) || michael@0: (bitmap->getSize() > bitmapSizeThreshold))) { michael@0: return true; michael@0: } michael@0: if (paint) { michael@0: SkShader* shader = paint->getShader(); michael@0: // Here we detect the case where the shader is an SkBitmapProcShader michael@0: // with a gpu texture attached. Checking this without RTTI michael@0: // requires making the assumption that only gradient shaders michael@0: // and SkBitmapProcShader implement asABitmap(). The following michael@0: // code may need to be revised if that assumption is ever broken. michael@0: if (shader && !shader->asAGradient(NULL)) { michael@0: SkBitmap bm; michael@0: if (shader->asABitmap(&bm, NULL, NULL) && michael@0: NULL != bm.getTexture()) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // DeferredPipeController michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class DeferredPipeController : public SkGPipeController { michael@0: public: michael@0: DeferredPipeController(); michael@0: void setPlaybackCanvas(SkCanvas*); michael@0: virtual ~DeferredPipeController(); michael@0: virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE; michael@0: virtual void notifyWritten(size_t bytes) SK_OVERRIDE; michael@0: void playback(bool silent); michael@0: bool hasPendingCommands() const { return fAllocator.blockCount() != 0; } michael@0: size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); } michael@0: private: michael@0: enum { michael@0: kMinBlockSize = 4096 michael@0: }; michael@0: struct PipeBlock { michael@0: PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; } michael@0: void* fBlock; michael@0: size_t fSize; michael@0: }; michael@0: void* fBlock; michael@0: size_t fBytesWritten; michael@0: SkChunkAlloc fAllocator; michael@0: SkTDArray fBlockList; michael@0: SkGPipeReader fReader; michael@0: }; michael@0: michael@0: DeferredPipeController::DeferredPipeController() : michael@0: fAllocator(kMinBlockSize) { michael@0: fBlock = NULL; michael@0: fBytesWritten = 0; michael@0: } michael@0: michael@0: DeferredPipeController::~DeferredPipeController() { michael@0: fAllocator.reset(); michael@0: } michael@0: michael@0: void DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) { michael@0: fReader.setCanvas(canvas); michael@0: } michael@0: michael@0: void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) { michael@0: if (fBlock) { michael@0: // Save the previous block for later michael@0: PipeBlock previousBloc(fBlock, fBytesWritten); michael@0: fBlockList.push(previousBloc); michael@0: } michael@0: size_t blockSize = SkTMax(minRequest, kMinBlockSize); michael@0: fBlock = fAllocator.allocThrow(blockSize); michael@0: fBytesWritten = 0; michael@0: *actual = blockSize; michael@0: return fBlock; michael@0: } michael@0: michael@0: void DeferredPipeController::notifyWritten(size_t bytes) { michael@0: fBytesWritten += bytes; michael@0: } michael@0: michael@0: void DeferredPipeController::playback(bool silent) { michael@0: uint32_t flags = silent ? SkGPipeReader::kSilent_PlaybackFlag : 0; michael@0: for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) { michael@0: fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize, michael@0: flags); michael@0: } michael@0: fBlockList.reset(); michael@0: michael@0: if (fBlock) { michael@0: fReader.playback(fBlock, fBytesWritten, flags); michael@0: fBlock = NULL; michael@0: } michael@0: michael@0: // Release all allocated blocks michael@0: fAllocator.reset(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // SkDeferredDevice michael@0: //----------------------------------------------------------------------------- michael@0: class SkDeferredDevice : public SkBaseDevice { michael@0: public: michael@0: explicit SkDeferredDevice(SkSurface* surface); michael@0: ~SkDeferredDevice(); michael@0: michael@0: void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient); michael@0: SkCanvas* recordingCanvas(); michael@0: SkCanvas* immediateCanvas() const {return fImmediateCanvas;} michael@0: SkBaseDevice* immediateDevice() const {return fImmediateCanvas->getTopDevice();} michael@0: SkImage* newImageSnapshot(); michael@0: void setSurface(SkSurface* surface); michael@0: bool isFreshFrame(); michael@0: bool hasPendingCommands(); michael@0: size_t storageAllocatedForRecording() const; michael@0: size_t freeMemoryIfPossible(size_t bytesToFree); michael@0: size_t getBitmapSizeThreshold() const; michael@0: void setBitmapSizeThreshold(size_t sizeThreshold); michael@0: void flushPendingCommands(PlaybackMode); michael@0: void skipPendingCommands(); michael@0: void setMaxRecordingStorage(size_t); michael@0: void recordedDrawCommand(); michael@0: michael@0: virtual int width() const SK_OVERRIDE; michael@0: virtual int height() const SK_OVERRIDE; michael@0: virtual SkBitmap::Config config() const SK_OVERRIDE; michael@0: virtual bool isOpaque() const SK_OVERRIDE; michael@0: virtual SkImageInfo imageInfo() const SK_OVERRIDE; michael@0: michael@0: virtual GrRenderTarget* accessRenderTarget() SK_OVERRIDE; michael@0: michael@0: virtual SkBaseDevice* onCreateDevice(const SkImageInfo&, Usage) SK_OVERRIDE; michael@0: michael@0: #ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG michael@0: virtual void writePixels(const SkBitmap& bitmap, int x, int y, michael@0: SkCanvas::Config8888 config8888) SK_OVERRIDE; michael@0: #endif michael@0: virtual SkSurface* newSurface(const SkImageInfo&) SK_OVERRIDE; michael@0: michael@0: protected: michael@0: virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE; michael@0: virtual bool onReadPixels(const SkBitmap& bitmap, michael@0: int x, int y, michael@0: SkCanvas::Config8888 config8888) SK_OVERRIDE; michael@0: virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y) SK_OVERRIDE; michael@0: michael@0: // The following methods are no-ops on a deferred device michael@0: virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) SK_OVERRIDE { michael@0: return false; michael@0: } michael@0: michael@0: // None of the following drawing methods should ever get called on the michael@0: // deferred device michael@0: virtual void clear(SkColor color) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, michael@0: size_t count, const SkPoint[], michael@0: const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawRect(const SkDraw&, const SkRect& r, michael@0: const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawOval(const SkDraw&, const SkRect&, const SkPaint&) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawRRect(const SkDraw&, const SkRRect& rr, michael@0: const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawPath(const SkDraw&, const SkPath& path, michael@0: const SkPaint& paint, michael@0: const SkMatrix* prePathMatrix = NULL, michael@0: bool pathIsMutable = false) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, michael@0: const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect*, michael@0: const SkRect&, const SkPaint&, michael@0: SkCanvas::DrawBitmapRectFlags) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, michael@0: int x, int y, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawText(const SkDraw&, const void* text, size_t len, michael@0: SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawPosText(const SkDraw&, const void* text, size_t len, michael@0: const SkScalar pos[], SkScalar constY, michael@0: int scalarsPerPos, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawTextOnPath(const SkDraw&, const void* text, michael@0: size_t len, const SkPath& path, michael@0: const SkMatrix* matrix, michael@0: const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, michael@0: int vertexCount, const SkPoint verts[], michael@0: const SkPoint texs[], const SkColor colors[], michael@0: SkXfermode* xmode, const uint16_t indices[], michael@0: int indexCount, const SkPaint& paint) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, michael@0: const SkPaint&) SK_OVERRIDE michael@0: {SkASSERT(0);} michael@0: michael@0: virtual void lockPixels() SK_OVERRIDE {} michael@0: virtual void unlockPixels() SK_OVERRIDE {} michael@0: michael@0: virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE { michael@0: return false; michael@0: } michael@0: virtual bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE { michael@0: return false; michael@0: } michael@0: virtual bool filterImage(const SkImageFilter*, const SkBitmap&, michael@0: const SkImageFilter::Context&, SkBitmap*, SkIPoint*) SK_OVERRIDE { michael@0: return false; michael@0: } michael@0: michael@0: private: michael@0: virtual void flush() SK_OVERRIDE; michael@0: virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {} michael@0: michael@0: void beginRecording(); michael@0: void init(); michael@0: void aboutToDraw(); michael@0: void prepareForImmediatePixelWrite(); michael@0: michael@0: DeferredPipeController fPipeController; michael@0: SkGPipeWriter fPipeWriter; michael@0: SkCanvas* fImmediateCanvas; michael@0: SkCanvas* fRecordingCanvas; michael@0: SkSurface* fSurface; michael@0: SkDeferredCanvas::NotificationClient* fNotificationClient; michael@0: bool fFreshFrame; michael@0: bool fCanDiscardCanvasContents; michael@0: size_t fMaxRecordingStorageBytes; michael@0: size_t fPreviousStorageAllocated; michael@0: size_t fBitmapSizeThreshold; michael@0: }; michael@0: michael@0: SkDeferredDevice::SkDeferredDevice(SkSurface* surface) { michael@0: fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; michael@0: fNotificationClient = NULL; michael@0: fImmediateCanvas = NULL; michael@0: fSurface = NULL; michael@0: this->setSurface(surface); michael@0: this->init(); michael@0: } michael@0: michael@0: void SkDeferredDevice::setSurface(SkSurface* surface) { michael@0: SkRefCnt_SafeAssign(fImmediateCanvas, surface->getCanvas()); michael@0: SkRefCnt_SafeAssign(fSurface, surface); michael@0: fPipeController.setPlaybackCanvas(fImmediateCanvas); michael@0: } michael@0: michael@0: void SkDeferredDevice::init() { michael@0: fRecordingCanvas = NULL; michael@0: fFreshFrame = true; michael@0: fCanDiscardCanvasContents = false; michael@0: fPreviousStorageAllocated = 0; michael@0: fBitmapSizeThreshold = kDeferredCanvasBitmapSizeThreshold; michael@0: fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; michael@0: fNotificationClient = NULL; michael@0: this->beginRecording(); michael@0: } michael@0: michael@0: SkDeferredDevice::~SkDeferredDevice() { michael@0: this->flushPendingCommands(kSilent_PlaybackMode); michael@0: SkSafeUnref(fImmediateCanvas); michael@0: SkSafeUnref(fSurface); michael@0: } michael@0: michael@0: void SkDeferredDevice::setMaxRecordingStorage(size_t maxStorage) { michael@0: fMaxRecordingStorageBytes = maxStorage; michael@0: this->recordingCanvas(); // Accessing the recording canvas applies the new limit. michael@0: } michael@0: michael@0: void SkDeferredDevice::beginRecording() { michael@0: SkASSERT(NULL == fRecordingCanvas); michael@0: fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0, michael@0: immediateDevice()->width(), immediateDevice()->height()); michael@0: } michael@0: michael@0: void SkDeferredDevice::setNotificationClient( michael@0: SkDeferredCanvas::NotificationClient* notificationClient) { michael@0: fNotificationClient = notificationClient; michael@0: } michael@0: michael@0: void SkDeferredDevice::skipPendingCommands() { michael@0: if (!fRecordingCanvas->isDrawingToLayer()) { michael@0: fCanDiscardCanvasContents = true; michael@0: if (fPipeController.hasPendingCommands()) { michael@0: fFreshFrame = true; michael@0: flushPendingCommands(kSilent_PlaybackMode); michael@0: if (fNotificationClient) { michael@0: fNotificationClient->skippedPendingDrawCommands(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool SkDeferredDevice::isFreshFrame() { michael@0: bool ret = fFreshFrame; michael@0: fFreshFrame = false; michael@0: return ret; michael@0: } michael@0: michael@0: bool SkDeferredDevice::hasPendingCommands() { michael@0: return fPipeController.hasPendingCommands(); michael@0: } michael@0: michael@0: void SkDeferredDevice::aboutToDraw() michael@0: { michael@0: if (NULL != fNotificationClient) { michael@0: fNotificationClient->prepareForDraw(); michael@0: } michael@0: if (fCanDiscardCanvasContents) { michael@0: if (NULL != fSurface) { michael@0: fSurface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); michael@0: } michael@0: fCanDiscardCanvasContents = false; michael@0: } michael@0: } michael@0: michael@0: void SkDeferredDevice::flushPendingCommands(PlaybackMode playbackMode) { michael@0: if (!fPipeController.hasPendingCommands()) { michael@0: return; michael@0: } michael@0: if (playbackMode == kNormal_PlaybackMode) { michael@0: aboutToDraw(); michael@0: } michael@0: fPipeWriter.flushRecording(true); michael@0: fPipeController.playback(kSilent_PlaybackMode == playbackMode); michael@0: if (playbackMode == kNormal_PlaybackMode && fNotificationClient) { michael@0: fNotificationClient->flushedDrawCommands(); michael@0: } michael@0: fPreviousStorageAllocated = storageAllocatedForRecording(); michael@0: } michael@0: michael@0: void SkDeferredDevice::flush() { michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: fImmediateCanvas->flush(); michael@0: } michael@0: michael@0: size_t SkDeferredDevice::freeMemoryIfPossible(size_t bytesToFree) { michael@0: size_t val = fPipeWriter.freeMemoryIfPossible(bytesToFree); michael@0: fPreviousStorageAllocated = storageAllocatedForRecording(); michael@0: return val; michael@0: } michael@0: michael@0: size_t SkDeferredDevice::getBitmapSizeThreshold() const { michael@0: return fBitmapSizeThreshold; michael@0: } michael@0: michael@0: void SkDeferredDevice::setBitmapSizeThreshold(size_t sizeThreshold) { michael@0: fBitmapSizeThreshold = sizeThreshold; michael@0: } michael@0: michael@0: size_t SkDeferredDevice::storageAllocatedForRecording() const { michael@0: return (fPipeController.storageAllocatedForRecording() michael@0: + fPipeWriter.storageAllocatedForRecording()); michael@0: } michael@0: michael@0: void SkDeferredDevice::recordedDrawCommand() { michael@0: size_t storageAllocated = this->storageAllocatedForRecording(); michael@0: michael@0: if (storageAllocated > fMaxRecordingStorageBytes) { michael@0: // First, attempt to reduce cache without flushing michael@0: size_t tryFree = storageAllocated - fMaxRecordingStorageBytes; michael@0: if (this->freeMemoryIfPossible(tryFree) < tryFree) { michael@0: // Flush is necessary to free more space. michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes michael@0: // which could cause a high flushing frequency. michael@0: this->freeMemoryIfPossible(~0U); michael@0: } michael@0: storageAllocated = this->storageAllocatedForRecording(); michael@0: } michael@0: michael@0: if (fNotificationClient && michael@0: storageAllocated != fPreviousStorageAllocated) { michael@0: fPreviousStorageAllocated = storageAllocated; michael@0: fNotificationClient->storageAllocatedForRecordingChanged(storageAllocated); michael@0: } michael@0: } michael@0: michael@0: SkCanvas* SkDeferredDevice::recordingCanvas() { michael@0: return fRecordingCanvas; michael@0: } michael@0: michael@0: SkImage* SkDeferredDevice::newImageSnapshot() { michael@0: this->flush(); michael@0: return fSurface ? fSurface->newImageSnapshot() : NULL; michael@0: } michael@0: michael@0: int SkDeferredDevice::width() const { michael@0: return immediateDevice()->width(); michael@0: } michael@0: michael@0: int SkDeferredDevice::height() const { michael@0: return immediateDevice()->height(); michael@0: } michael@0: michael@0: SkBitmap::Config SkDeferredDevice::config() const { michael@0: return immediateDevice()->config(); michael@0: } michael@0: michael@0: bool SkDeferredDevice::isOpaque() const { michael@0: return immediateDevice()->isOpaque(); michael@0: } michael@0: michael@0: SkImageInfo SkDeferredDevice::imageInfo() const { michael@0: return immediateDevice()->imageInfo(); michael@0: } michael@0: michael@0: GrRenderTarget* SkDeferredDevice::accessRenderTarget() { michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: return immediateDevice()->accessRenderTarget(); michael@0: } michael@0: michael@0: void SkDeferredDevice::prepareForImmediatePixelWrite() { michael@0: // The purpose of the following code is to make sure commands are flushed, that michael@0: // aboutToDraw() is called and that notifyContentWillChange is called, without michael@0: // calling anything redundantly. michael@0: if (fPipeController.hasPendingCommands()) { michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: } else { michael@0: bool mustNotifyDirectly = !fCanDiscardCanvasContents; michael@0: this->aboutToDraw(); michael@0: if (mustNotifyDirectly) { michael@0: fSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode); michael@0: } michael@0: } michael@0: michael@0: fImmediateCanvas->flush(); michael@0: } michael@0: michael@0: #ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG michael@0: void SkDeferredDevice::writePixels(const SkBitmap& bitmap, int x, int y, michael@0: SkCanvas::Config8888 config8888) { michael@0: michael@0: if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() && michael@0: (y + bitmap.height()) >= height()) { michael@0: this->skipPendingCommands(); michael@0: } michael@0: michael@0: if (SkBitmap::kARGB_8888_Config == bitmap.config() && michael@0: SkCanvas::kNative_Premul_Config8888 != config8888 && michael@0: kPMColorAlias != config8888) { michael@0: //Special case config: no deferral michael@0: prepareForImmediatePixelWrite(); michael@0: immediateDevice()->writePixels(bitmap, x, y, config8888); michael@0: return; michael@0: } michael@0: michael@0: SkPaint paint; michael@0: paint.setXfermodeMode(SkXfermode::kSrc_Mode); michael@0: if (shouldDrawImmediately(&bitmap, NULL, getBitmapSizeThreshold())) { michael@0: prepareForImmediatePixelWrite(); michael@0: fImmediateCanvas->drawSprite(bitmap, x, y, &paint); michael@0: } else { michael@0: this->recordingCanvas()->drawSprite(bitmap, x, y, &paint); michael@0: this->recordedDrawCommand(); michael@0: michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool SkDeferredDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, michael@0: int x, int y) { michael@0: SkASSERT(x >= 0 && y >= 0); michael@0: SkASSERT(x + info.width() <= width()); michael@0: SkASSERT(y + info.height() <= height()); michael@0: michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: michael@0: const SkImageInfo deviceInfo = this->imageInfo(); michael@0: if (info.width() == deviceInfo.width() && info.height() == deviceInfo.height()) { michael@0: this->skipPendingCommands(); michael@0: } michael@0: michael@0: this->prepareForImmediatePixelWrite(); michael@0: return immediateDevice()->onWritePixels(info, pixels, rowBytes, x, y); michael@0: } michael@0: michael@0: const SkBitmap& SkDeferredDevice::onAccessBitmap() { michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: return immediateDevice()->accessBitmap(false); michael@0: } michael@0: michael@0: SkBaseDevice* SkDeferredDevice::onCreateDevice(const SkImageInfo& info, Usage usage) { michael@0: // Save layer usage not supported, and not required by SkDeferredCanvas. michael@0: SkASSERT(usage != kSaveLayer_Usage); michael@0: // Create a compatible non-deferred device. michael@0: // We do not create a deferred device because we know the new device michael@0: // will not be used with a deferred canvas (there is no API for that). michael@0: // And connecting a SkDeferredDevice to non-deferred canvas can result michael@0: // in unpredictable behavior. michael@0: return immediateDevice()->createCompatibleDevice(info); michael@0: } michael@0: michael@0: SkSurface* SkDeferredDevice::newSurface(const SkImageInfo& info) { michael@0: return this->immediateDevice()->newSurface(info); michael@0: } michael@0: michael@0: bool SkDeferredDevice::onReadPixels( michael@0: const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) { michael@0: this->flushPendingCommands(kNormal_PlaybackMode); michael@0: return fImmediateCanvas->readPixels(const_cast(&bitmap), michael@0: x, y, config8888); michael@0: } michael@0: michael@0: class AutoImmediateDrawIfNeeded { michael@0: public: michael@0: AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap, michael@0: const SkPaint* paint) { michael@0: this->init(canvas, bitmap, paint); michael@0: } michael@0: michael@0: AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) { michael@0: this->init(canvas, NULL, paint); michael@0: } michael@0: michael@0: ~AutoImmediateDrawIfNeeded() { michael@0: if (fCanvas) { michael@0: fCanvas->setDeferredDrawing(true); michael@0: } michael@0: } michael@0: private: michael@0: void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint) michael@0: { michael@0: SkDeferredDevice* device = static_cast(canvas.getDevice()); michael@0: if (canvas.isDeferredDrawing() && (NULL != device) && michael@0: shouldDrawImmediately(bitmap, paint, device->getBitmapSizeThreshold())) { michael@0: canvas.setDeferredDrawing(false); michael@0: fCanvas = &canvas; michael@0: } else { michael@0: fCanvas = NULL; michael@0: } michael@0: } michael@0: michael@0: SkDeferredCanvas* fCanvas; michael@0: }; michael@0: michael@0: SkDeferredCanvas* SkDeferredCanvas::Create(SkSurface* surface) { michael@0: SkAutoTUnref deferredDevice(SkNEW_ARGS(SkDeferredDevice, (surface))); michael@0: return SkNEW_ARGS(SkDeferredCanvas, (deferredDevice)); michael@0: } michael@0: michael@0: SkDeferredCanvas::SkDeferredCanvas(SkDeferredDevice* device) : SkCanvas (device) { michael@0: this->init(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::init() { michael@0: fDeferredDrawing = true; // On by default michael@0: } michael@0: michael@0: void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) { michael@0: this->validate(); michael@0: this->getDeferredDevice()->setMaxRecordingStorage(maxStorage); michael@0: } michael@0: michael@0: size_t SkDeferredCanvas::storageAllocatedForRecording() const { michael@0: return this->getDeferredDevice()->storageAllocatedForRecording(); michael@0: } michael@0: michael@0: size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) { michael@0: return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree); michael@0: } michael@0: michael@0: void SkDeferredCanvas::setBitmapSizeThreshold(size_t sizeThreshold) { michael@0: SkDeferredDevice* deferredDevice = this->getDeferredDevice(); michael@0: SkASSERT(deferredDevice); michael@0: deferredDevice->setBitmapSizeThreshold(sizeThreshold); michael@0: } michael@0: michael@0: void SkDeferredCanvas::recordedDrawCommand() { michael@0: if (fDeferredDrawing) { michael@0: this->getDeferredDevice()->recordedDrawCommand(); michael@0: } michael@0: } michael@0: michael@0: void SkDeferredCanvas::validate() const { michael@0: SkASSERT(this->getDevice()); michael@0: } michael@0: michael@0: SkCanvas* SkDeferredCanvas::drawingCanvas() const { michael@0: this->validate(); michael@0: return fDeferredDrawing ? this->getDeferredDevice()->recordingCanvas() : michael@0: this->getDeferredDevice()->immediateCanvas(); michael@0: } michael@0: michael@0: SkCanvas* SkDeferredCanvas::immediateCanvas() const { michael@0: this->validate(); michael@0: return this->getDeferredDevice()->immediateCanvas(); michael@0: } michael@0: michael@0: SkDeferredDevice* SkDeferredCanvas::getDeferredDevice() const { michael@0: return static_cast(this->getDevice()); michael@0: } michael@0: michael@0: void SkDeferredCanvas::setDeferredDrawing(bool val) { michael@0: this->validate(); // Must set device before calling this method michael@0: if (val != fDeferredDrawing) { michael@0: if (fDeferredDrawing) { michael@0: // Going live. michael@0: this->getDeferredDevice()->flushPendingCommands(kNormal_PlaybackMode); michael@0: } michael@0: fDeferredDrawing = val; michael@0: } michael@0: } michael@0: michael@0: bool SkDeferredCanvas::isDeferredDrawing() const { michael@0: return fDeferredDrawing; michael@0: } michael@0: michael@0: bool SkDeferredCanvas::isFreshFrame() const { michael@0: return this->getDeferredDevice()->isFreshFrame(); michael@0: } michael@0: michael@0: bool SkDeferredCanvas::hasPendingCommands() const { michael@0: return this->getDeferredDevice()->hasPendingCommands(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::silentFlush() { michael@0: if (fDeferredDrawing) { michael@0: this->getDeferredDevice()->flushPendingCommands(kSilent_PlaybackMode); michael@0: } michael@0: } michael@0: michael@0: SkDeferredCanvas::~SkDeferredCanvas() { michael@0: } michael@0: michael@0: SkSurface* SkDeferredCanvas::setSurface(SkSurface* surface) { michael@0: SkDeferredDevice* deferredDevice = this->getDeferredDevice(); michael@0: SkASSERT(NULL != deferredDevice); michael@0: // By swapping the surface into the existing device, we preserve michael@0: // all pending commands, which can help to seamlessly recover from michael@0: // a lost accelerated graphics context. michael@0: deferredDevice->setSurface(surface); michael@0: return surface; michael@0: } michael@0: michael@0: SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient( michael@0: NotificationClient* notificationClient) { michael@0: michael@0: SkDeferredDevice* deferredDevice = this->getDeferredDevice(); michael@0: SkASSERT(deferredDevice); michael@0: if (deferredDevice) { michael@0: deferredDevice->setNotificationClient(notificationClient); michael@0: } michael@0: return notificationClient; michael@0: } michael@0: michael@0: SkImage* SkDeferredCanvas::newImageSnapshot() { michael@0: SkDeferredDevice* deferredDevice = this->getDeferredDevice(); michael@0: SkASSERT(deferredDevice); michael@0: return deferredDevice ? deferredDevice->newImageSnapshot() : NULL; michael@0: } michael@0: michael@0: bool SkDeferredCanvas::isFullFrame(const SkRect* rect, michael@0: const SkPaint* paint) const { michael@0: SkCanvas* canvas = this->drawingCanvas(); michael@0: SkISize canvasSize = this->getDeviceSize(); michael@0: if (rect) { michael@0: if (!canvas->getTotalMatrix().rectStaysRect()) { michael@0: return false; // conservative michael@0: } michael@0: michael@0: SkRect transformedRect; michael@0: canvas->getTotalMatrix().mapRect(&transformedRect, *rect); michael@0: michael@0: if (paint) { michael@0: SkPaint::Style paintStyle = paint->getStyle(); michael@0: if (!(paintStyle == SkPaint::kFill_Style || michael@0: paintStyle == SkPaint::kStrokeAndFill_Style)) { michael@0: return false; michael@0: } michael@0: if (paint->getMaskFilter() || paint->getLooper() michael@0: || paint->getPathEffect() || paint->getImageFilter()) { michael@0: return false; // conservative michael@0: } michael@0: } michael@0: michael@0: // The following test holds with AA enabled, and is conservative michael@0: // by a 0.5 pixel margin with AA disabled michael@0: if (transformedRect.fLeft > SkIntToScalar(0) || michael@0: transformedRect.fTop > SkIntToScalar(0) || michael@0: transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) || michael@0: transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return this->getClipStack()->quickContains(SkRect::MakeXYWH(0, 0, michael@0: SkIntToScalar(canvasSize.fWidth), SkIntToScalar(canvasSize.fHeight))); michael@0: } michael@0: michael@0: void SkDeferredCanvas::willSave(SaveFlags flags) { michael@0: this->drawingCanvas()->save(flags); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::willSave(flags); michael@0: } michael@0: michael@0: SkCanvas::SaveLayerStrategy SkDeferredCanvas::willSaveLayer(const SkRect* bounds, michael@0: const SkPaint* paint, SaveFlags flags) { michael@0: this->drawingCanvas()->saveLayer(bounds, paint, flags); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::willSaveLayer(bounds, paint, flags); michael@0: // No need for a full layer. michael@0: return kNoLayer_SaveLayerStrategy; michael@0: } michael@0: michael@0: void SkDeferredCanvas::willRestore() { michael@0: this->drawingCanvas()->restore(); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::willRestore(); michael@0: } michael@0: michael@0: bool SkDeferredCanvas::isDrawingToLayer() const { michael@0: return this->drawingCanvas()->isDrawingToLayer(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didTranslate(SkScalar dx, SkScalar dy) { michael@0: this->drawingCanvas()->translate(dx, dy); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didTranslate(dx, dy); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didScale(SkScalar sx, SkScalar sy) { michael@0: this->drawingCanvas()->scale(sx, sy); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didScale(sx, sy); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didRotate(SkScalar degrees) { michael@0: this->drawingCanvas()->rotate(degrees); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didRotate(degrees); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didSkew(SkScalar sx, SkScalar sy) { michael@0: this->drawingCanvas()->skew(sx, sy); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didSkew(sx, sy); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didConcat(const SkMatrix& matrix) { michael@0: this->drawingCanvas()->concat(matrix); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didConcat(matrix); michael@0: } michael@0: michael@0: void SkDeferredCanvas::didSetMatrix(const SkMatrix& matrix) { michael@0: this->drawingCanvas()->setMatrix(matrix); michael@0: this->recordedDrawCommand(); michael@0: this->INHERITED::didSetMatrix(matrix); michael@0: } michael@0: michael@0: void SkDeferredCanvas::onClipRect(const SkRect& rect, michael@0: SkRegion::Op op, michael@0: ClipEdgeStyle edgeStyle) { michael@0: this->drawingCanvas()->clipRect(rect, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: this->INHERITED::onClipRect(rect, op, edgeStyle); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::onClipRRect(const SkRRect& rrect, michael@0: SkRegion::Op op, michael@0: ClipEdgeStyle edgeStyle) { michael@0: this->drawingCanvas()->clipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: this->INHERITED::onClipRRect(rrect, op, edgeStyle); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::onClipPath(const SkPath& path, michael@0: SkRegion::Op op, michael@0: ClipEdgeStyle edgeStyle) { michael@0: this->drawingCanvas()->clipPath(path, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: this->INHERITED::onClipPath(path, op, edgeStyle); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { michael@0: this->drawingCanvas()->clipRegion(deviceRgn, op); michael@0: this->INHERITED::onClipRegion(deviceRgn, op); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::clear(SkColor color) { michael@0: // purge pending commands michael@0: if (fDeferredDrawing) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: michael@0: this->drawingCanvas()->clear(color); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPaint(const SkPaint& paint) { michael@0: if (fDeferredDrawing && this->isFullFrame(NULL, &paint) && michael@0: isPaintOpaque(&paint)) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawPaint(paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPoints(PointMode mode, size_t count, michael@0: const SkPoint pts[], const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawPoints(mode, count, pts, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawOval(const SkRect& rect, const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawOval(rect, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) { michael@0: if (fDeferredDrawing && this->isFullFrame(&rect, &paint) && michael@0: isPaintOpaque(&paint)) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawRect(rect, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { michael@0: if (rrect.isRect()) { michael@0: this->SkDeferredCanvas::drawRect(rrect.getBounds(), paint); michael@0: } else if (rrect.isOval()) { michael@0: this->SkDeferredCanvas::drawOval(rrect.getBounds(), paint); michael@0: } else { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawRRect(rrect, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: } michael@0: michael@0: void SkDeferredCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, michael@0: const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawDRRect(outer, inner, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawPath(path, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left, michael@0: SkScalar top, const SkPaint* paint) { michael@0: SkRect bitmapRect = SkRect::MakeXYWH(left, top, michael@0: SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); michael@0: if (fDeferredDrawing && michael@0: this->isFullFrame(&bitmapRect, paint) && michael@0: isPaintOpaque(paint, &bitmap)) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); michael@0: this->drawingCanvas()->drawBitmap(bitmap, left, top, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, michael@0: const SkRect* src, michael@0: const SkRect& dst, michael@0: const SkPaint* paint, michael@0: DrawBitmapRectFlags flags) { michael@0: if (fDeferredDrawing && michael@0: this->isFullFrame(&dst, paint) && michael@0: isPaintOpaque(paint, &bitmap)) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); michael@0: this->drawingCanvas()->drawBitmapRectToRect(bitmap, src, dst, paint, flags); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: michael@0: void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap, michael@0: const SkMatrix& m, michael@0: const SkPaint* paint) { michael@0: // TODO: reset recording canvas if paint+bitmap is opaque and clip rect michael@0: // covers canvas entirely and transformed bitmap covers canvas entirely michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); michael@0: this->drawingCanvas()->drawBitmapMatrix(bitmap, m, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap, michael@0: const SkIRect& center, const SkRect& dst, michael@0: const SkPaint* paint) { michael@0: // TODO: reset recording canvas if paint+bitmap is opaque and clip rect michael@0: // covers canvas entirely and dst covers canvas entirely michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); michael@0: this->drawingCanvas()->drawBitmapNine(bitmap, center, dst, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, michael@0: const SkPaint* paint) { michael@0: SkRect bitmapRect = SkRect::MakeXYWH( michael@0: SkIntToScalar(left), michael@0: SkIntToScalar(top), michael@0: SkIntToScalar(bitmap.width()), michael@0: SkIntToScalar(bitmap.height())); michael@0: if (fDeferredDrawing && michael@0: this->isFullFrame(&bitmapRect, paint) && michael@0: isPaintOpaque(paint, &bitmap)) { michael@0: this->getDeferredDevice()->skipPendingCommands(); michael@0: } michael@0: michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint); michael@0: this->drawingCanvas()->drawSprite(bitmap, left, top, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawText(const void* text, size_t byteLength, michael@0: SkScalar x, SkScalar y, const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawText(text, byteLength, x, y, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength, michael@0: const SkPoint pos[], const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawPosText(text, byteLength, pos, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength, michael@0: const SkScalar xpos[], SkScalar constY, michael@0: const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength, michael@0: const SkPath& path, michael@0: const SkMatrix* matrix, michael@0: const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawTextOnPath(text, byteLength, path, matrix, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawPicture(SkPicture& picture) { michael@0: this->drawingCanvas()->drawPicture(picture); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount, michael@0: const SkPoint vertices[], michael@0: const SkPoint texs[], michael@0: const SkColor colors[], SkXfermode* xmode, michael@0: const uint16_t indices[], int indexCount, michael@0: const SkPaint& paint) { michael@0: AutoImmediateDrawIfNeeded autoDraw(*this, &paint); michael@0: this->drawingCanvas()->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode, michael@0: indices, indexCount, paint); michael@0: this->recordedDrawCommand(); michael@0: } michael@0: michael@0: SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) { michael@0: this->drawingCanvas()->setBounder(bounder); michael@0: this->INHERITED::setBounder(bounder); michael@0: this->recordedDrawCommand(); michael@0: return bounder; michael@0: } michael@0: michael@0: SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) { michael@0: this->drawingCanvas()->setDrawFilter(filter); michael@0: this->INHERITED::setDrawFilter(filter); michael@0: this->recordedDrawCommand(); michael@0: return filter; michael@0: } michael@0: michael@0: SkCanvas* SkDeferredCanvas::canvasForDrawIter() { michael@0: return this->drawingCanvas(); michael@0: }