aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-28 12:19:02 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-28 12:19:02 +0000
commit97af1a64ae6bdddd346d8babfd9f188279dd6644 (patch)
tree00ed8f9919b9f2623b4d08e33ef61f80018d6eb4 /src
parent91ee3a11ed476f4f08e1e4ae183002c56349ec19 (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.cpp13
-rw-r--r--src/core/SkDevice.cpp7
-rw-r--r--src/core/SkPictureRecord.h2
-rw-r--r--src/image/SkImage.cpp2
-rw-r--r--src/image/SkImagePriv.cpp4
-rw-r--r--src/image/SkImagePriv.h17
-rw-r--r--src/image/SkImage_Raster.cpp8
-rw-r--r--src/image/SkSurface.cpp85
-rw-r--r--src/image/SkSurface_Base.h27
-rw-r--r--src/image/SkSurface_Raster.cpp28
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,