#include "SkStream.h"
#include "SkData.h"
+extern GrContext* GetGr();
+
static SkData* fileToData(const char path[]) {
SkFILEStream stream(path);
if (!stream.isValid()) {
drawContents(surf, SK_ColorRED);
SkImage* imgR = surf->newImageShapshot();
+ if (true) {
+ SkImage* imgR2 = surf->newImageShapshot();
+ SkASSERT(imgR == imgR2);
+ imgR2->unref();
+ }
+
drawContents(surf, SK_ColorGREEN);
SkImage* imgG = surf->newImageShapshot();
+ // since we've drawn after we snapped imgR, imgG will be a different obj
+ SkASSERT(imgR != imgG);
+
drawContents(surf, SK_ColorBLUE);
SkPaint paint;
test_surface(canvas, surf2);
}
+ virtual uint32_t onGetFlags() const SK_OVERRIDE {
+ return GM::kSkipPicture_Flag;
+ }
+
private:
typedef skiagm::GM INHERITED;
};
'../include/ports',
'../include/xml',
'../src/core',
+ '../src/image',
],
'msvs_disabled_warnings': [4244, 4267,4345, 4390, 4554, 4800],
'conditions': [
'<(skia_src_path)/core/SkUtils.cpp',
'<(skia_src_path)/core/SkWriter32.cpp',
'<(skia_src_path)/core/SkXfermode.cpp',
+
+ '<(skia_src_path)/image/SkDataPixelRef.cpp',
+ '<(skia_src_path)/image/SkImage.cpp',
+ '<(skia_src_path)/image/SkImagePriv.cpp',
+ '<(skia_src_path)/image/SkImage_Codec.cpp',
+# '<(skia_src_path)/image/SkImage_Gpu.cpp',
+ '<(skia_src_path)/image/SkImage_Picture.cpp',
+ '<(skia_src_path)/image/SkImage_Raster.cpp',
+ '<(skia_src_path)/image/SkSurface.cpp',
+# '<(skia_src_path)/image/SkSurface_Gpu.cpp',
+ '<(skia_src_path)/image/SkSurface_Picture.cpp',
+ '<(skia_src_path)/image/SkSurface_Raster.cpp',
+
'<(skia_src_path)/pipe/SkGPipeRead.cpp',
'<(skia_src_path)/pipe/SkGPipeWrite.cpp',
'../gm/imageblur.cpp',
'../gm/imagemagnifier.cpp',
'../gm/lighting.cpp',
+ '../gm/image.cpp',
'../gm/imagefiltersbase.cpp',
'../gm/imagefiltersgraph.cpp',
'../gm/lcdtext.cpp',
class SkDraw;
class SkDrawFilter;
class SkPicture;
+class SkSurface_Base;
/** \class SkCanvas
bool clipRectBounds(const SkRect* bounds, SaveFlags flags,
SkIRect* intersection);
+ // notify our surface (if we have one) that we are about to draw, so it
+ // can perform copy-on-write or invalidate any cached images
+ void predrawNotify();
+
private:
class MCRec;
SkDevice* fLastDeviceToGainFocus;
int fSaveLayerCount; // number of successful saveLayer calls
+ SkSurface_Base* fSurfaceBase;
+ SkSurface_Base* getSurfaceBase() const { return fSurfaceBase; }
+ void setSurfaceBase(SkSurface_Base* sb) {
+ fSurfaceBase = sb;
+ }
+ friend class SkSurface_Base;
+
void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&);
bool fDeviceCMDirty; // cleared by updateDeviceCMCache()
friend class SkDeviceFilteredPaint;
friend class DeviceImageFilterProxy;
+ friend class SkSurface_Raster;
+ // used to change the backend's pixels (and possibly config/rowbytes)
+ // but cannot change the width/height, so there should be no change to
+ // any clip information.
+ void replaceBitmapBackendForRasterSurface(const SkBitmap&);
+
// just called by SkCanvas when built as a layer
void setOrigin(int x, int y) { fOrigin.set(x, y); }
// just called by SkCanvas for saveLayer
*/
class SkImage : public SkRefCnt {
public:
- SK_DECLARE_INST_COUNT(SkImage)
+// SK_DECLARE_INST_COUNT(SkImage)
enum ColorType {
kAlpha_8_ColorType,
*/
class SkSurface : public SkRefCnt {
public:
- SK_DECLARE_INST_COUNT(SkSurface)
+// SK_DECLARE_INST_COUNT(SkSurface)
/**
* Create a new surface, using the specified pixels/rowbytes as its
* If this surface is empty (i.e. has a zero-dimention), this will return
* 0.
*/
- uint32_t generationID() const;
+ uint32_t generationID();
/**
* Call this if the contents have changed. This will (lazily) force a new
protected:
SkSurface(int width, int height);
+ // called by subclass if their contents have changed
+ void dirtyGenerationID() {
+ fGenerationID = 0;
+ }
+
private:
- const int fWidth;
- const int fHeight;
- mutable uint32_t fGenerationID;
+ const int fWidth;
+ const int fHeight;
+ uint32_t fGenerationID;
};
#endif
#include "SkPicture.h"
#include "SkRasterClip.h"
#include "SkScalarCompare.h"
+#include "SkSurface_Base.h"
#include "SkTemplates.h"
#include "SkTextFormatParams.h"
#include "SkTLazy.h"
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.
#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); \
#define LOOPER_BEGIN(paint, type) \
/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
+ this->predrawNotify(); \
AutoDrawLooper looper(this, paint); \
while (looper.next(type)) { \
SkAutoBounderCommit ac(fBounder); \
fExternalMatrix.reset();
fExternalInverse.reset();
fUseExternalMatrix = false;
+
+ fSurfaceBase = NULL;
return this->setDevice(device);
}
///////////////////////////////////////////////////////////////////////////////
SkCanvas::ClipVisitor::~ClipVisitor() { }
+
+
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) {
};
void addDraw(DrawType drawType) {
+ this->predrawNotify();
+
#ifdef SK_DEBUG_TRACE
SkDebugf("add %s\n", DrawTypeToString(drawType));
#endif
#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);
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();
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*);
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
// 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) {}
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);
return SkNEW_ARGS(SkImage_Raster, (info, pr, rowBytes));
}
+SkPixelRef* SkBitmapImageGetPixelRef(SkImage* image) {
+ return ((SkImage_Raster*)image)->getPixelRef();
+}
+
#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);
}
}
}
+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);
}
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,
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;
};
#include "SkSurface_Base.h"
#include "SkImagePriv.h"
#include "SkCanvas.h"
+#include "SkDevice.h"
#include "SkMallocPixelRef.h"
static const size_t kIgnoreRowBytesValue = (size_t)~0;
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;
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,
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,