From 889b09edfeb5f461ca283dfd08ee6b23560a7859 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Fri, 27 Jul 2012 21:10:42 +0000 Subject: [PATCH] check-point for surface experiment git-svn-id: http://skia.googlecode.com/svn/trunk@4819 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkImage.h | 59 ---------------- include/core/SkSurface.h | 122 ++++++++++++++++++++++++++++++++ src/image/SkImage.cpp | 97 ++++++++------------------ src/image/SkImagePriv.cpp | 109 +++++++++++++++++++++++++++++ src/image/SkImagePriv.h | 27 ++++++++ src/image/SkSurface.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 458 insertions(+), 129 deletions(-) create mode 100644 include/core/SkSurface.h create mode 100644 src/image/SkImagePriv.cpp create mode 100644 src/image/SkImagePriv.h create mode 100644 src/image/SkSurface.cpp diff --git a/include/core/SkImage.h b/include/core/SkImage.h index df71875..ebe275e 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 0000000..b399a2f --- /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 0573a83..4d96b9f 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 0000000..d0c56fd --- /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 0000000..1c732c3 --- /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 0000000..2a9e33a --- /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(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)); +} + -- 2.7.4