aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkImage.h59
-rw-r--r--include/core/SkSurface.h122
-rw-r--r--src/image/SkImage.cpp97
-rw-r--r--src/image/SkImagePriv.cpp109
-rw-r--r--src/image/SkImagePriv.h27
-rw-r--r--src/image/SkSurface.cpp173
6 files changed, 458 insertions, 129 deletions
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index df71875e61..ebe275e0c6 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -93,63 +93,4 @@ private:
static uint32_t NextUniqueID();
};
-/**
- * SkSurface represents the backend/results of drawing to a canvas. For raster
- * drawing, the surface will be pixels, but (for example) when drawing into
- * a PDF or Picture canvas, the surface stores the recorded commands.
- *
- * To draw into a canvas, first create the appropriate type of Surface, and
- * then request the canvas from the surface.
- */
-class SkSurface : public SkRefCnt {
-public:
- static SkSurface* NewRasterDirect(const SkImage::Info&, SkColorSpace*,
- const void* pixels, size_t rowBytes);
- static SkSurface* NewRaster(const SkImage::Info&, SkColorSpace*);
- static SkSurface* NewGpu(GrContext*);
- static SkSurface* NewPDF(...);
- static SkSurface* NewXPS(...);
- static SkSurface* NewPicture(int width, int height);
-
- /**
- * Return a canvas that will draw into this surface.
- *
- * LIFECYCLE QUESTIONS
- * 1. Is this owned by the surface or the caller?
- * 2. Can the caller get a 2nd canvas, or reset the state of the first?
- */
- SkCanvas* newCanvas();
-
- /**
- * Return a new surface that is "compatible" with this one, in that it will
- * efficiently be able to be drawn into this surface. Typical calling
- * pattern:
- *
- * SkSurface* A = SkSurface::New...();
- * SkCanvas* canvasA = surfaceA->newCanvas();
- * ...
- * SkSurface* surfaceB = surfaceA->newSurface(...);
- * SkCanvas* canvasB = surfaceB->newCanvas();
- * ... // draw using canvasB
- * canvasA->drawSurface(surfaceB); // <--- this will always be optimal!
- */
- SkSurface* newSurface(int width, int height);
-
- /**
- * 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.
- */
- SkImage* newImageShapshot();
-
- /**
- * Thought the caller could get a snapshot image explicitly, and draw that,
- * it seems that directly drawing a surface into another canvas might be
- * a common pattern, and that we could possibly be more efficient, since
- * we'd know that the "snapshot" need only live until we've handed it off
- * to the canvas.
- */
- void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
-};
-
#endif
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
new file mode 100644
index 0000000000..b399a2fe92
--- /dev/null
+++ b/include/core/SkSurface.h
@@ -0,0 +1,122 @@
+/*
+ * 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 SkSurface_DEFINED
+#define SkSurface_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkImage.h"
+
+class SkCanvas;
+class SkPaint;
+
+/**
+ * SkSurface represents the backend/results of drawing to a canvas. For raster
+ * drawing, the surface will be pixels, but (for example) when drawing into
+ * a PDF or Picture canvas, the surface stores the recorded commands.
+ *
+ * To draw into a canvas, first create the appropriate type of Surface, and
+ * then request the canvas from the surface.
+ */
+class SkSurface : public SkRefCnt {
+public:
+ /**
+ * Create a new surface, using the specified pixels/rowbytes as its
+ * backend.
+ *
+ * If the requested surface cannot be created, or the request is not a
+ * supported configuration, NULL will be returned.
+ */
+ static SkSurface* NewRasterDirect(const SkImage::Info&, SkColorSpace*,
+ void* pixels, size_t rowBytes);
+
+ /**
+ * Return a new surface, with the memory for the pixels automatically
+ * allocated.
+ *
+ * If the requested surface cannot be created, or the request is not a
+ * supported configuration, NULL will be returned.
+ */
+ static SkSurface* NewRaster(const SkImage::Info&, SkColorSpace*);
+
+ /**
+ * Return a new surface whose contents will be recorded into a picture.
+ * When this surface is drawn into another canvas, its contents will be
+ * "replayed" into that canvas.
+ */
+ static SkSurface* NewPicture(int width, int height);
+
+ int width() const { return fWidth; }
+ int height() const { return fHeight; }
+
+ /**
+ * Returns a unique non-zero, unique value identifying the content of this
+ * surface. Each time the content is changed changed, either by drawing
+ * into this surface, or explicitly calling notifyContentChanged()) this
+ * method will return a new value.
+ *
+ * If this surface is empty (i.e. has a zero-dimention), this will return
+ * 0.
+ */
+ uint32_t generationID() const;
+
+ /**
+ * Call this if the contents have changed. This will (lazily) force a new
+ * value to be returned from generationID() when it is called next.
+ */
+ void notifyContentChanged();
+
+ /**
+ * Return a canvas that will draw into this surface.
+ *
+ * LIFECYCLE QUESTIONS
+ * 1. Is this owned by the surface or the caller?
+ * 2. Can the caller get a 2nd canvas, or reset the state of the first?
+ */
+ SkCanvas* newCanvas();
+
+ /**
+ * Return a new surface that is "compatible" with this one, in that it will
+ * efficiently be able to be drawn into this surface. Typical calling
+ * pattern:
+ *
+ * SkSurface* A = SkSurface::New...();
+ * SkCanvas* canvasA = surfaceA->newCanvas();
+ * ...
+ * SkSurface* surfaceB = surfaceA->newSurface(...);
+ * SkCanvas* canvasB = surfaceB->newCanvas();
+ * ... // draw using canvasB
+ * canvasA->drawSurface(surfaceB); // <--- this will always be optimal!
+ */
+ SkSurface* newSurface(const SkImage::Info&, SkColorSpace*);
+
+ /**
+ * 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.
+ */
+ SkImage* newImageShapshot();
+
+ /**
+ * Thought the caller could get a snapshot image explicitly, and draw that,
+ * it seems that directly drawing a surface into another canvas might be
+ * a common pattern, and that we could possibly be more efficient, since
+ * we'd know that the "snapshot" need only live until we've handed it off
+ * to the canvas.
+ */
+ void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+protected:
+ SkSurface(int width, int height);
+
+private:
+ const int fWidth;
+ const int fHeight;
+ mutable uint32_t fGenerationID;
+};
+
+#endif
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 0573a83836..4d96b9ffff 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -1,5 +1,5 @@
#include "SkImage.h"
-
+#include "SkImagePriv.h"
#include "SkBitmap.h"
///////////////////////////////////////////////////////////////////////////////
@@ -20,70 +20,6 @@ static SkImage_Base* asIB(SkImage* image) {
///////////////////////////////////////////////////////////////////////////////
-static SkBitmap::Config InfoToConfig(const SkImage::Info& info, bool* isOpaque) {
- switch (info.fColorType) {
- case SkImage::kAlpha_8_ColorType:
- switch (info.fAlphaType) {
- case SkImage::kIgnore_AlphaType:
- // makes no sense
- return SkBitmap::kNo_Config;
-
- case SkImage::kOpaque_AlphaType:
- *isOpaque = true;
- return SkBitmap::kA8_Config;
-
- case SkImage::kPremul_AlphaType:
- case SkImage::kUnpremul_AlphaType:
- *isOpaque = false;
- return SkBitmap::kA8_Config;
- }
- break;
-
- case SkImage::kRGB_565_ColorType:
- // we ignore fAlpahType, though some would not make sense
- *isOpaque = true;
- return SkBitmap::kRGB_565_Config;
-
- case SkImage::kRGBA_8888_ColorType:
- case SkImage::kBGRA_8888_ColorType:
- // not supported yet
- return SkBitmap::kNo_Config;
-
- case SkImage::kPMColor_ColorType:
- switch (info.fAlphaType) {
- case SkImage::kIgnore_AlphaType:
- case SkImage::kUnpremul_AlphaType:
- // not supported yet
- return SkBitmap::kNo_Config;
- case SkImage::kOpaque_AlphaType:
- *isOpaque = true;
- return SkBitmap::kARGB_8888_Config;
- case SkImage::kPremul_AlphaType:
- *isOpaque = false;
- return SkBitmap::kARGB_8888_Config;
- }
- break;
- }
- SkASSERT(!"how did we get here");
- return SkBitmap::kNo_Config;
-}
-
-static int BytesPerPixel(SkImage::ColorType ct) {
- static const uint8_t gColorTypeBytesPerPixel[] = {
- 1, // kAlpha_8_ColorType
- 2, // kRGB_565_ColorType
- 4, // kRGBA_8888_ColorType
- 4, // kBGRA_8888_ColorType
- 4, // kPMColor_ColorType
- };
-
- SkASSERT((size_t)ct < SK_ARRAY_COUNT(gColorTypeBytesPerPixel));
- return gColorTypeBytesPerPixel[ct];
-}
-
-static size_t ComputeMinRowBytes(const SkImage::Info& info) {
- return info.fWidth * BytesPerPixel(info.fColorType);
-}
class SkImage_Raster : public SkImage_Base {
public:
@@ -105,13 +41,13 @@ public:
}
bool isOpaque;
- if (InfoToConfig(info, &isOpaque) == SkBitmap::kNo_Config) {
+ if (SkImageInfoToBitmapConfig(info, &isOpaque) == SkBitmap::kNo_Config) {
return false;
}
// TODO: check colorspace
- if (rowBytes < ComputeMinRowBytes(info)) {
+ if (rowBytes < SkImageMinRowBytes(info)) {
return false;
}
@@ -129,6 +65,9 @@ public:
virtual const SkBitmap* asABitmap() SK_OVERRIDE;
+ // exposed for SkSurface_Raster via SkNewImageFromPixelRef
+ SkImage_Raster(const SkImage::Info&, SkPixelRef*, size_t rowBytes);
+
private:
SkImage_Raster() : INHERITED(0, 0) {}
@@ -137,6 +76,11 @@ private:
typedef SkImage_Base INHERITED;
};
+SkImage* SkNewImageFromPixelRef(const SkImage::Info& info, SkPixelRef* pr,
+ size_t rowBytes) {
+ return SkNEW_ARGS(SkImage_Raster, (info, pr, rowBytes));
+}
+
///////////////////////////////////////////////////////////////////////////////
#include "SkData.h"
@@ -154,14 +98,27 @@ SkImage* SkImage_Raster::NewEmpty() {
SkImage_Raster::SkImage_Raster(const Info& info, SkColorSpace* cs,
SkData* data, size_t rowBytes)
+: INHERITED(info.fWidth, info.fHeight) {
+ bool isOpaque;
+ SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+ fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes);
+ fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (data)))->unref();
+ fBitmap.setIsOpaque(isOpaque);
+ fBitmap.setImmutable();
+}
+
+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 = InfoToConfig(info, &isOpaque);
+ SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes);
- fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (data)))->unref();
+ fBitmap.setPixelRef(pr);
fBitmap.setIsOpaque(isOpaque);
- fBitmap.setImmutable(); // Yea baby!
+ fBitmap.setImmutable();
}
SkImage_Raster::~SkImage_Raster() {}
diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp
new file mode 100644
index 0000000000..d0c56fd2ef
--- /dev/null
+++ b/src/image/SkImagePriv.cpp
@@ -0,0 +1,109 @@
+#include "SkImagePriv.h"
+
+SkBitmap::Config SkImageInfoToBitmapConfig(const SkImage::Info& info,
+ bool* isOpaque) {
+ switch (info.fColorType) {
+ case SkImage::kAlpha_8_ColorType:
+ switch (info.fAlphaType) {
+ case SkImage::kIgnore_AlphaType:
+ // makes no sense
+ return SkBitmap::kNo_Config;
+
+ case SkImage::kOpaque_AlphaType:
+ *isOpaque = true;
+ return SkBitmap::kA8_Config;
+
+ case SkImage::kPremul_AlphaType:
+ case SkImage::kUnpremul_AlphaType:
+ *isOpaque = false;
+ return SkBitmap::kA8_Config;
+ }
+ break;
+
+ case SkImage::kRGB_565_ColorType:
+ // we ignore fAlpahType, though some would not make sense
+ *isOpaque = true;
+ return SkBitmap::kRGB_565_Config;
+
+ case SkImage::kRGBA_8888_ColorType:
+ case SkImage::kBGRA_8888_ColorType:
+ // not supported yet
+ return SkBitmap::kNo_Config;
+
+ case SkImage::kPMColor_ColorType:
+ switch (info.fAlphaType) {
+ case SkImage::kIgnore_AlphaType:
+ case SkImage::kUnpremul_AlphaType:
+ // not supported yet
+ return SkBitmap::kNo_Config;
+ case SkImage::kOpaque_AlphaType:
+ *isOpaque = true;
+ return SkBitmap::kARGB_8888_Config;
+ case SkImage::kPremul_AlphaType:
+ *isOpaque = false;
+ return SkBitmap::kARGB_8888_Config;
+ }
+ break;
+ }
+ SkASSERT(!"how did we get here");
+ return SkBitmap::kNo_Config;
+}
+
+int SkImageBytesPerPixel(SkImage::ColorType ct) {
+ static const uint8_t gColorTypeBytesPerPixel[] = {
+ 1, // kAlpha_8_ColorType
+ 2, // kRGB_565_ColorType
+ 4, // kRGBA_8888_ColorType
+ 4, // kBGRA_8888_ColorType
+ 4, // kPMColor_ColorType
+ };
+
+ SkASSERT((size_t)ct < SK_ARRAY_COUNT(gColorTypeBytesPerPixel));
+ return gColorTypeBytesPerPixel[ct];
+}
+
+bool SkBitmapToImageInfo(const SkBitmap& bm, SkImage::Info* info) {
+ switch (bm.config()) {
+ case SkBitmap::kA8_Config:
+ info->fColorType = SkImage::kAlpha_8_ColorType;
+ break;
+
+ case SkBitmap::kRGB_565_Config:
+ info->fColorType = SkImage::kRGB_565_ColorType;
+ break;
+
+ case SkBitmap::kARGB_8888_Config:
+ info->fColorType = SkImage::kPMColor_ColorType;
+ break;
+
+ default:
+ return false;
+ }
+
+ info->fWidth = bm.width();
+ info->fHeight = bm.height();
+ info->fAlphaType = bm.isOpaque() ? SkImage::kOpaque_AlphaType :
+ SkImage::kPremul_AlphaType;
+ return true;
+}
+
+SkImage* SkNewImageFromBitmap(const SkBitmap& bm) {
+ SkImage::Info info;
+ if (!SkBitmapToImageInfo(bm, &info)) {
+ return NULL;
+ }
+
+ SkImage* image = NULL;
+ if (bm.isImmutable()) {
+ image = SkNewImageFromPixelRef(info, bm.pixelRef(), bm.rowBytes());
+ } else {
+ bm.lockPixels();
+ if (NULL == bm.getPixels()) {
+ image = SkImage::NewRasterCopy(info, NULL, bm.getPixels(),
+ bm.rowBytes());
+ }
+ bm.unlockPixels();
+ }
+ return image;
+}
+
diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h
new file mode 100644
index 0000000000..1c732c3b9f
--- /dev/null
+++ b/src/image/SkImagePriv.h
@@ -0,0 +1,27 @@
+
+#ifndef SkImagePriv_DEFINED
+#define SkImagePriv_DEFINED
+
+#include "SkBitmap.h"
+#include "SkImage.h"
+
+extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImage::Info&,
+ bool* isOpaque);
+
+extern int SkImageBytesPerPixel(SkImage::ColorType);
+
+extern bool SkBitmapToImageInfo(const SkBitmap&, SkImage::Info*);
+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
+ */
+extern SkImage* SkNewImageFromBitmap(const SkBitmap&);
+
+static inline size_t SkImageMinRowBytes(const SkImage::Info& info) {
+ return info.fWidth * SkImageBytesPerPixel(info.fColorType);
+}
+
+#endif
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
new file mode 100644
index 0000000000..2a9e33a8e2
--- /dev/null
+++ b/src/image/SkSurface.cpp
@@ -0,0 +1,173 @@
+#include "SkSurface.h"
+#include "SkImagePriv.h"
+#include "SkCanvas.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSurface_Base : public SkSurface {
+public:
+ SkSurface_Base(int width, int height) : INHERITED(width, height) {}
+
+ virtual SkCanvas* onNewCanvas() = 0;
+ virtual SkSurface* onNewSurface(const SkImage::Info&, SkColorSpace*) = 0;
+ virtual SkImage* onNewImageShapshot() = 0;
+
+ /**
+ * Default implementation:
+ *
+ * image = this->newImageSnapshot();
+ * if (image) {
+ * image->draw(canvas, ...);
+ * image->unref();
+ * }
+ */
+ virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+private:
+ typedef SkSurface INHERITED;
+};
+
+void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ SkImage* image = this->newImageShapshot();
+ if (image) {
+ image->draw(canvas, x, y, paint);
+ image->unref();
+ }
+}
+
+static SkSurface_Base* asSB(SkSurface* surface) {
+ return static_cast<SkSurface_Base*>(surface);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface::SkSurface(int width, int height) : fWidth(width), fHeight(height) {
+ SkASSERT(width >= 0);
+ SkASSERT(height >= 0);
+ fGenerationID = 0;
+}
+
+SkCanvas* SkSurface::newCanvas() {
+ return asSB(this)->onNewCanvas();
+}
+
+SkSurface* SkSurface::newSurface(const SkImage::Info& info, SkColorSpace* cs) {
+ return asSB(this)->onNewSurface(info, cs);
+}
+
+SkImage* SkSurface::newImageShapshot() {
+ return asSB(this)->onNewImageShapshot();
+}
+
+void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ return asSB(this)->onDraw(canvas, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSurface_Raster : public SkSurface {
+public:
+ static bool Valid(const SkImage::Info&, SkColorSpace*, size_t rb);
+
+ SkSurface_Raster(const SkImage::Info&, SkColorSpace*, void*, size_t rb);
+
+ virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+ virtual SkSurface* onNewSurface(const SkImage::Info&, SkColorSpace*) SK_OVERRIDE;
+ virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+ virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+ const SkPaint*) SK_OVERRIDE;
+
+private:
+ SkBitmap fBitmap;
+
+ typedef SkSurface INHERITED;
+};
+
+bool SkSurface_Raster::Valid(const SkImage::Info& info, SkColorSpace* cs,
+ size_t rowBytes) {
+ static size_t kMaxTotalSize = (1 << 31) - 1;
+
+ bool isOpaque;
+ SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+ int shift = 0;
+ switch (config) {
+ case SkBitmap::kA8_Config:
+ shift = 0;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ shift = 1;
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ shift = 2;
+ break;
+ default:
+ return false;
+ }
+
+ // TODO: examine colorspace
+
+ uint64_t minRB = (uint64_t)info.fWidth << shift;
+ if (minRB > rowBytes) {
+ return false;
+ }
+
+ size_t alignedRowBytes = rowBytes >> shift << shift;
+ if (alignedRowBytes != rowBytes) {
+ return false;
+ }
+
+ uint64_t size = (uint64_t)info.fHeight * rowBytes;
+ if (size > kMaxTotalSize) {
+ return false;
+ }
+
+ return true;
+}
+
+SkSurface_Raster::SkSurface_Raster(const SkImage::Info& info, SkColorSpace* cs,
+ void* pixels, size_t rb)
+ : INHERITED(info.fWidth, info.fHeight) {
+ bool isOpaque;
+ SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+ fBitmap.setConfig(config, info.fWidth, info.fHeight, rb);
+ fBitmap.setPixels(pixels);
+ fBitmap.setIsOpaque(isOpaque);
+}
+
+SkCanvas* SkSurface_Raster::onNewCanvas() {
+ return SkNEW_ARGS(SkCanvas, (fBitmap));
+}
+
+SkSurface* SkSurface_Raster::onNewSurface(const SkImage::Info& info,
+ SkColorSpace* cs) {
+ return SkSurface::NewRaster(info, cs);
+}
+
+SkImage* SkSurface_Raster::onNewImageShapshot() {
+ return SkNewImageFromBitmap(fBitmap);
+}
+
+void SkSurface_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface* SkSurface::NewRasterDirect(const SkImage::Info& info,
+ SkColorSpace* cs,
+ void* pixels, size_t rowBytes) {
+ if (!SkSurface_Raster::Valid(info, cs, rowBytes)) {
+ return NULL;
+ }
+ if (NULL == pixels) {
+ return NULL;
+ }
+
+ return SkNEW_ARGS(SkSurface_Raster, (info, cs, pixels, rowBytes));
+}
+