diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-28 12:19:02 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-28 12:19:02 +0000 |
commit | 97af1a64ae6bdddd346d8babfd9f188279dd6644 (patch) | |
tree | 00ed8f9919b9f2623b4d08e33ef61f80018d6eb4 /src | |
parent | 91ee3a11ed476f4f08e1e4ae183002c56349ec19 (diff) |
Add caching of the snapshot image form a surface
Notify the surface when the canvas draws into it, so it can invalidate the
cached image, and (if needed) perform a copy-on-write on the surface if it
was being shared with the image.
Review URL: https://codereview.appspot.com/6441115
git-svn-id: http://skia.googlecode.com/svn/trunk@5306 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkCanvas.cpp | 13 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 7 | ||||
-rw-r--r-- | src/core/SkPictureRecord.h | 2 | ||||
-rw-r--r-- | src/image/SkImage.cpp | 2 | ||||
-rw-r--r-- | src/image/SkImagePriv.cpp | 4 | ||||
-rw-r--r-- | src/image/SkImagePriv.h | 17 | ||||
-rw-r--r-- | src/image/SkImage_Raster.cpp | 8 | ||||
-rw-r--r-- | src/image/SkSurface.cpp | 85 | ||||
-rw-r--r-- | src/image/SkSurface_Base.h | 27 | ||||
-rw-r--r-- | src/image/SkSurface_Raster.cpp | 28 |
10 files changed, 165 insertions, 28 deletions
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index b7c06c3351..f5c6bc2a65 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -16,6 +16,7 @@ #include "SkPicture.h" #include "SkRasterClip.h" #include "SkScalarCompare.h" +#include "SkSurface_Base.h" #include "SkTemplates.h" #include "SkTextFormatParams.h" #include "SkTLazy.h" @@ -53,6 +54,12 @@ SK_DEFINE_INST_COUNT(SkDrawFilter) typedef SkTLazy<SkPaint> SkLazyPaint; +void SkCanvas::predrawNotify() { + if (fSurfaceBase) { + fSurfaceBase->aboutToDraw(this); + } +} + /////////////////////////////////////////////////////////////////////////////// /* This is the record we keep for each SkDevice that the user installs. @@ -425,6 +432,7 @@ private: #define LOOPER_BEGIN_DRAWDEVICE(paint, type) \ /* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \ + this->predrawNotify(); \ AutoDrawLooper looper(this, paint, true); \ while (looper.next(type)) { \ SkAutoBounderCommit ac(fBounder); \ @@ -432,6 +440,7 @@ private: #define LOOPER_BEGIN(paint, type) \ /* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \ + this->predrawNotify(); \ AutoDrawLooper looper(this, paint); \ while (looper.next(type)) { \ SkAutoBounderCommit ac(fBounder); \ @@ -459,6 +468,8 @@ SkDevice* SkCanvas::init(SkDevice* device) { fExternalMatrix.reset(); fExternalInverse.reset(); fUseExternalMatrix = false; + + fSurfaceBase = NULL; return this->setDevice(device); } @@ -2072,3 +2083,5 @@ int SkCanvas::LayerIter::y() const { return fImpl->getY(); } /////////////////////////////////////////////////////////////////////////////// SkCanvas::ClipVisitor::~ClipVisitor() { } + + diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 6146fd6c71..f8bed65e42 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -50,6 +50,13 @@ SkDevice::~SkDevice() { delete fMetaData; } +void SkDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { + SkASSERT(bm.width() == fBitmap.width()); + SkASSERT(bm.height() == fBitmap.height()); + fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config) + fBitmap.lockPixels(); +} + SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config, int width, int height, bool isOpaque) { diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index 50cf4db099..a19a084534 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -96,6 +96,8 @@ private: }; void addDraw(DrawType drawType) { + this->predrawNotify(); + #ifdef SK_DEBUG_TRACE SkDebugf("add %s\n", DrawTypeToString(drawType)); #endif diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index b6fc4899da..251721f00d 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -10,7 +10,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkImage) +//SK_DEFINE_INST_COUNT(SkImage) static SkImage_Base* asIB(SkImage* image) { return static_cast<SkImage_Base*>(image); diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp index a020eaff14..e7a244ab54 100644 --- a/src/image/SkImagePriv.cpp +++ b/src/image/SkImagePriv.cpp @@ -96,14 +96,14 @@ bool SkBitmapToImageInfo(const SkBitmap& bm, SkImage::Info* info) { return true; } -SkImage* SkNewImageFromBitmap(const SkBitmap& bm) { +SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) { SkImage::Info info; if (!SkBitmapToImageInfo(bm, &info)) { return NULL; } SkImage* image = NULL; - if (bm.isImmutable()) { + if (canSharePixelRef || bm.isImmutable()) { image = SkNewImageFromPixelRef(info, bm.pixelRef(), bm.rowBytes()); } else { bm.lockPixels(); diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h index c0aff983a2..9332abc7af 100644 --- a/src/image/SkImagePriv.h +++ b/src/image/SkImagePriv.h @@ -19,14 +19,22 @@ extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImage::Info&, extern int SkImageBytesPerPixel(SkImage::ColorType); extern bool SkBitmapToImageInfo(const SkBitmap&, SkImage::Info*); + +// Call this if you explicitly want to use/share this pixelRef in the image extern SkImage* SkNewImageFromPixelRef(const SkImage::Info&, SkPixelRef*, size_t rowBytes); /** * Examines the bitmap to decide if it can share the existing pixelRef, or - * if it needs to make a deep-copy of the pixels + * if it needs to make a deep-copy of the pixels. The bitmap's pixelref will + * be shared if either the bitmap is marked as immutable, or canSharePixelRef + * is true. + * + * If the bitmap's config cannot be converted into a corresponding + * SkImage::Info, or the bitmap's pixels cannot be accessed, this will return + * NULL. */ -extern SkImage* SkNewImageFromBitmap(const SkBitmap&); +extern SkImage* SkNewImageFromBitmap(const SkBitmap&, bool canSharePixelRef); extern void SkImagePrivDrawPicture(SkCanvas*, SkPicture*, SkScalar x, SkScalar y, const SkPaint*); @@ -42,4 +50,9 @@ static inline size_t SkImageMinRowBytes(const SkImage::Info& info) { return SkAlign4(rb); } +// Given an image created from SkNewImageFromBitmap, return its pixelref. This +// may be called to see if the surface and the image share the same pixelref, +// in which case the surface may need to perform a copy-on-write. +extern SkPixelRef* SkBitmapImageGetPixelRef(SkImage* rasterImage); + #endif diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index 216f094569..daed241360 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -59,6 +59,8 @@ public: // exposed for SkSurface_Raster via SkNewImageFromPixelRef SkImage_Raster(const SkImage::Info&, SkPixelRef*, size_t rowBytes); + SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); } + private: SkImage_Raster() : INHERITED(0, 0) {} @@ -93,8 +95,6 @@ SkImage_Raster::SkImage_Raster(const Info& info, SkColorSpace* cs, SkImage_Raster::SkImage_Raster(const Info& info, SkPixelRef* pr, size_t rowBytes) : INHERITED(info.fWidth, info.fHeight) { - SkASSERT(pr->isImmutable()); - bool isOpaque; SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque); @@ -159,3 +159,7 @@ SkImage* SkNewImageFromPixelRef(const SkImage::Info& info, SkPixelRef* pr, return SkNEW_ARGS(SkImage_Raster, (info, pr, rowBytes)); } +SkPixelRef* SkBitmapImageGetPixelRef(SkImage* image) { + return ((SkImage_Raster*)image)->getPixelRef(); +} + diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index 299a8d22e0..7b82a340da 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -9,15 +9,28 @@ #include "SkImagePriv.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkSurface) +//SK_DEFINE_INST_COUNT(SkSurface) /////////////////////////////////////////////////////////////////////////////// +void SkSurface_Base::installIntoCanvasForDirtyNotification() { + if (fCachedCanvas) { + fCachedCanvas->setSurfaceBase(this); + } +} + SkSurface_Base::SkSurface_Base(int width, int height) : INHERITED(width, height) { fCachedCanvas = NULL; + fCachedImage = NULL; } SkSurface_Base::~SkSurface_Base() { + // in case the canvas outsurvives us, we null the callback + if (fCachedCanvas) { + fCachedCanvas->setSurfaceBase(NULL); + } + + SkSafeUnref(fCachedImage); SkSafeUnref(fCachedCanvas); } @@ -30,6 +43,55 @@ void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, } } +void SkSurface_Base::onCopyOnWrite(SkImage*, SkCanvas*) {} + +SkCanvas* SkSurface_Base::getCachedCanvas() { + if (NULL == fCachedCanvas) { + fCachedCanvas = this->onNewCanvas(); + this->installIntoCanvasForDirtyNotification(); + } + return fCachedCanvas; +} + +SkImage* SkSurface_Base::getCachedImage() { + if (NULL == fCachedImage) { + fCachedImage = this->onNewImageShapshot(); + this->installIntoCanvasForDirtyNotification(); + } + return fCachedImage; +} + +void SkSurface_Base::aboutToDraw(SkCanvas* canvas) { + this->dirtyGenerationID(); + + if (canvas) { + SkASSERT(canvas == fCachedCanvas); + SkASSERT(canvas->getSurfaceBase() == this); + canvas->setSurfaceBase(NULL); + } + + if (fCachedImage) { + // the surface may need to fork its backend, if its sharing it with + // the cached image. Note: we only call if there is an outstanding owner + // on the image (besides us). + if (fCachedImage->getRefCnt() > 1) { + this->onCopyOnWrite(fCachedImage, canvas); + } + + // regardless of copy-on-write, we must drop our cached image now, so + // that the next request will get our new contents. + fCachedImage->unref(); + fCachedImage = NULL; + } +} + +uint32_t SkSurface_Base::newGenerationID() { + this->installIntoCanvasForDirtyNotification(); + + static int32_t gID; + return sk_atomic_inc(&gID) + 1; +} + static SkSurface_Base* asSB(SkSurface* surface) { return static_cast<SkSurface_Base*>(surface); } @@ -42,16 +104,29 @@ SkSurface::SkSurface(int width, int height) : fWidth(width), fHeight(height) { fGenerationID = 0; } +uint32_t SkSurface::generationID() { + if (0 == fGenerationID) { + fGenerationID = asSB(this)->newGenerationID(); + } + return fGenerationID; +} + +void SkSurface::notifyContentChanged() { + asSB(this)->aboutToDraw(NULL); +} + SkCanvas* SkSurface::getCanvas() { return asSB(this)->getCachedCanvas(); } -SkSurface* SkSurface::newSurface(const SkImage::Info& info, SkColorSpace* cs) { - return asSB(this)->onNewSurface(info, cs); +SkImage* SkSurface::newImageShapshot() { + SkImage* image = asSB(this)->getCachedImage(); + SkSafeRef(image); // the caller will call unref() to balance this + return image; } -SkImage* SkSurface::newImageShapshot() { - return asSB(this)->onNewImageShapshot(); +SkSurface* SkSurface::newSurface(const SkImage::Info& info, SkColorSpace* cs) { + return asSB(this)->onNewSurface(info, cs); } void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y, diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h index 8166c8c435..52b494f237 100644 --- a/src/image/SkSurface_Base.h +++ b/src/image/SkSurface_Base.h @@ -45,18 +45,29 @@ public: virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*); /** - * Returns a the result of onNewCanvas(), but caches it so that only one - * canvas never ever be created. + * If the surface is about to change, we call this so that our subclass + * can optionally fork their backend (copy-on-write) in case it was + * being shared with the cachedImage. + * + * The default implementation does nothing. */ - SkCanvas* getCachedCanvas() { - if (NULL == fCachedCanvas) { - fCachedCanvas = this->onNewCanvas(); - } - return fCachedCanvas; - } + virtual void onCopyOnWrite(SkImage* cachedImage, SkCanvas*); + + inline SkCanvas* getCachedCanvas(); + inline SkImage* getCachedImage(); + + // called by SkSurface to compute a new genID + uint32_t newGenerationID(); private: SkCanvas* fCachedCanvas; + SkImage* fCachedImage; + + void aboutToDraw(SkCanvas*); + friend class SkCanvas; + friend class SkSurface; + + inline void installIntoCanvasForDirtyNotification(); typedef SkSurface INHERITED; }; diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp index c4ac959730..8afab6b01c 100644 --- a/src/image/SkSurface_Raster.cpp +++ b/src/image/SkSurface_Raster.cpp @@ -8,6 +8,7 @@ #include "SkSurface_Base.h" #include "SkImagePriv.h" #include "SkCanvas.h" +#include "SkDevice.h" #include "SkMallocPixelRef.h" static const size_t kIgnoreRowBytesValue = (size_t)~0; @@ -24,6 +25,7 @@ public: virtual SkImage* onNewImageShapshot() SK_OVERRIDE; virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) SK_OVERRIDE; + virtual void onCopyOnWrite(SkImage*, SkCanvas*) SK_OVERRIDE; private: SkBitmap fBitmap; @@ -89,7 +91,7 @@ SkSurface_Raster::SkSurface_Raster(const SkImage::Info& info, SkColorSpace* cs, fBitmap.setConfig(config, info.fWidth, info.fHeight, rb); fBitmap.setPixels(pixels); fBitmap.setIsOpaque(isOpaque); - fWeOwnThePixels = false; + fWeOwnThePixels = false; // We are "Direct" } SkSurface_Raster::SkSurface_Raster(const SkImage::Info& info, SkColorSpace* cs, @@ -117,18 +119,28 @@ SkSurface* SkSurface_Raster::onNewSurface(const SkImage::Info& info, return SkSurface::NewRaster(info, cs); } -SkImage* SkSurface_Raster::onNewImageShapshot() { - // if we don't own the pixels, we need to make a deep-copy - // if we do, we need to perform a copy-on-write the next time - // we draw to this bitmap from our canvas... - return SkNewImageFromBitmap(fBitmap); -} - void SkSurface_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) { canvas->drawBitmap(fBitmap, x, y, paint); } +SkImage* SkSurface_Raster::onNewImageShapshot() { + return SkNewImageFromBitmap(fBitmap, fWeOwnThePixels); +} + +void SkSurface_Raster::onCopyOnWrite(SkImage* image, SkCanvas* canvas) { + // are we sharing pixelrefs with the image? + if (SkBitmapImageGetPixelRef(image) == fBitmap.pixelRef()) { + SkASSERT(fWeOwnThePixels); + SkBitmap prev(fBitmap); + prev.deepCopyTo(&fBitmap, prev.config()); + // Now fBitmap is a deep copy of itself (and therefore different from + // what is being used by the image. Next we update the canvas to use + // this as its backend, so we can't modify the image's pixels anymore. + canvas->getDevice()->replaceBitmapBackendForRasterSurface(fBitmap); + } +} + /////////////////////////////////////////////////////////////////////////////// SkSurface* SkSurface::NewRasterDirect(const SkImage::Info& info, |