aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 14:11:56 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 14:11:56 +0000
commit4f7c61583b16e3056cf3350fcef42dcc6d3483b7 (patch)
treea88af2149ff7f8f08be9479321b30aba859bc2ce
parent999da9c5e45fb533efe8782c9096794b9ad1c1b3 (diff)
add ways to peer into an image to get its pixels
BUG=skia: R=bsalomon@google.com, scroggo@google.com Review URL: https://codereview.chromium.org/155763004 git-svn-id: http://skia.googlecode.com/svn/trunk@13339 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkImage.h34
-rw-r--r--src/image/SkImage.cpp87
-rw-r--r--src/image/SkImage_Base.h8
-rw-r--r--src/image/SkImage_Raster.cpp46
-rw-r--r--tests/SurfaceTest.cpp102
5 files changed, 260 insertions, 17 deletions
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 60f8bfb311..2353655781 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -76,6 +76,17 @@ public:
void draw(SkCanvas*, const SkRect* src, const SkRect& dst, const SkPaint*);
/**
+ * If the image has direct access to its pixels (i.e. they are in local
+ * RAM) return the (const) address of those pixels, and if not null, return
+ * the ImageInfo and rowBytes. The returned address is only valid while
+ * the image object is in scope.
+ *
+ * On failure, returns NULL and the info and rowBytes parameters are
+ * ignored.
+ */
+ const void* peekPixels(SkImageInfo* info, size_t* rowBytes) const;
+
+ /**
* Encode the image's pixels and return the result as a new SkData, which
* the caller must manage (i.e. call unref() when they are done).
*
@@ -103,6 +114,29 @@ private:
static uint32_t NextUniqueID();
typedef SkRefCnt INHERITED;
+
+ /**
+ * Return a copy of the image's pixels, limiting them to the subset
+ * rectangle's intersection wit the image bounds. If subset is NULL, then
+ * the entire image will be considered.
+ *
+ * If the bitmap's pixels have already been allocated, then readPixels()
+ * will succeed only if it can support converting the image's pixels into
+ * the bitmap's ColorType/AlphaType. Any pixels in the bitmap that do not
+ * intersect with the image's bounds and the subset (if not null) will be
+ * left untouched.
+ *
+ * If the bitmap is initially empty/unallocated, then it will be allocated
+ * using the default allocator, and the ColorType/AlphaType will be chosen
+ * to most closely fit the image's configuration.
+ *
+ * On failure, false will be returned, and bitmap will unmodified.
+ */
+ // On ice for now:
+ // - should it respect the particular colortype/alphatype of the src
+ // - should it have separate entrypoints for preallocated and not bitmaps?
+ // - isn't it enough to allow the caller to draw() the image into a canvas?
+ bool readPixels(SkBitmap* bitmap, const SkIRect* subset = NULL) const;
};
#endif
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 620922f645..9b0a0b2cf8 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -39,6 +39,41 @@ void SkImage::draw(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
as_IB(this)->onDrawRectToRect(canvas, src, dst, paint);
}
+const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const {
+ SkImageInfo infoStorage;
+ size_t rowBytesStorage;
+ if (NULL == info) {
+ info = &infoStorage;
+ }
+ if (NULL == rowBytes) {
+ rowBytes = &rowBytesStorage;
+ }
+ return as_IB(this)->onPeekPixels(info, rowBytes);
+}
+
+bool SkImage::readPixels(SkBitmap* bitmap, const SkIRect* subset) const {
+ if (NULL == bitmap) {
+ return false;
+ }
+
+ SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
+
+ // trim against the bitmap, if its already been allocated
+ if (bitmap->pixelRef()) {
+ bounds.fRight = SkMin32(bounds.fRight, bitmap->width());
+ bounds.fBottom = SkMin32(bounds.fBottom, bitmap->height());
+ if (bounds.isEmpty()) {
+ return false;
+ }
+ }
+
+ if (subset && !bounds.intersect(*subset)) {
+ // perhaps we could return true + empty-bitmap?
+ return false;
+ }
+ return as_IB(this)->onReadPixels(bitmap, bounds);
+}
+
GrTexture* SkImage::getTexture() {
return as_IB(this)->onGetTexture();
}
@@ -50,3 +85,55 @@ SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const {
}
return NULL;
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool raster_canvas_supports(const SkImageInfo& info) {
+ switch (info.fColorType) {
+ case kPMColor_SkColorType:
+ return kUnpremul_SkAlphaType != info.fAlphaType;
+ case kRGB_565_SkColorType:
+ return true;
+ case kAlpha_8_SkColorType:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool SkImage_Base::onReadPixels(SkBitmap* bitmap, const SkIRect& subset) const {
+ SkImageInfo info;
+
+ if (bitmap->pixelRef()) {
+ if (!bitmap->asImageInfo(&info)) {
+ return false;
+ }
+ if (!raster_canvas_supports(info)) {
+ return false;
+ }
+ } else {
+ SkImageInfo info = SkImageInfo::MakeN32Premul(subset.width(),
+ subset.height());
+ SkBitmap tmp;
+ if (!tmp.allocPixels(info)) {
+ return false;
+ }
+ *bitmap = tmp;
+ }
+
+ SkRect srcR, dstR;
+ srcR.set(subset);
+ dstR = srcR;
+ dstR.offset(-dstR.left(), -dstR.top());
+
+ SkCanvas canvas(*bitmap);
+
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ canvas.drawRect(dstR, paint);
+
+ const_cast<SkImage_Base*>(this)->onDrawRectToRect(&canvas, &srcR, dstR, NULL);
+ return true;
+}
+
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 7bd1f7e6c9..9fdfcd2677 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -17,6 +17,14 @@ public:
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) = 0;
virtual void onDrawRectToRect(SkCanvas*, const SkRect* src,
const SkRect& dst, const SkPaint*) = 0;
+
+ // Default impl calls onDraw
+ virtual bool onReadPixels(SkBitmap*, const SkIRect& subset) const;
+
+ virtual const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const {
+ return NULL;
+ }
+
virtual GrTexture* onGetTexture() { return NULL; }
// return a read-only copy of the pixels. We promise to not modify them,
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index ab8a635c1c..262fb97bfa 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -55,6 +55,8 @@ public:
virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
virtual void onDrawRectToRect(SkCanvas*, const SkRect*, const SkRect&, const SkPaint*) SK_OVERRIDE;
+ virtual bool onReadPixels(SkBitmap*, const SkIRect&) const SK_OVERRIDE;
+ virtual const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const SK_OVERRIDE;
virtual bool getROPixels(SkBitmap*) const SK_OVERRIDE;
// exposed for SkSurface_Raster via SkNewImageFromPixelRef
@@ -82,13 +84,20 @@ SkImage* SkImage_Raster::NewEmpty() {
return gEmpty;
}
+static void release_data(void* addr, void* context) {
+ SkData* data = static_cast<SkData*>(context);
+ data->unref();
+}
+
SkImage_Raster::SkImage_Raster(const Info& info, SkData* data, size_t rowBytes)
- : INHERITED(info.fWidth, info.fHeight) {
- fBitmap.setConfig(info, rowBytes);
- SkAutoTUnref<SkPixelRef> ref(
- SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data, 0));
- fBitmap.setPixelRef(ref);
+ : INHERITED(info.fWidth, info.fHeight)
+{
+ data->ref();
+ void* addr = const_cast<void*>(data->data());
+
+ fBitmap.installPixels(info, addr, rowBytes, release_data, data);
fBitmap.setImmutable();
+ fBitmap.lockPixels();
}
SkImage_Raster::SkImage_Raster(const Info& info, SkPixelRef* pr, size_t rowBytes)
@@ -96,6 +105,7 @@ SkImage_Raster::SkImage_Raster(const Info& info, SkPixelRef* pr, size_t rowBytes
{
fBitmap.setConfig(info, rowBytes);
fBitmap.setPixelRef(pr);
+ fBitmap.lockPixels();
}
SkImage_Raster::~SkImage_Raster() {}
@@ -104,10 +114,34 @@ void SkImage_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPa
canvas->drawBitmap(fBitmap, x, y, paint);
}
-void SkImage_Raster::onDrawRectToRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst, const SkPaint* paint) {
+void SkImage_Raster::onDrawRectToRect(SkCanvas* canvas, const SkRect* src,
+ const SkRect& dst, const SkPaint* paint) {
canvas->drawBitmapRectToRect(fBitmap, src, dst, paint);
}
+bool SkImage_Raster::onReadPixels(SkBitmap* dst, const SkIRect& subset) const {
+ if (dst->pixelRef()) {
+ return this->INHERITED::onReadPixels(dst, subset);
+ } else {
+ SkBitmap src;
+ if (!fBitmap.extractSubset(&src, subset)) {
+ return false;
+ }
+ return src.copyTo(dst, src.config());
+ }
+}
+
+const void* SkImage_Raster::onPeekPixels(SkImageInfo* infoPtr,
+ size_t* rowBytesPtr) const {
+ SkImageInfo info;
+ if (!fBitmap.asImageInfo(&info) || !fBitmap.getPixels()) {
+ return false;
+ }
+ *infoPtr = info;
+ *rowBytesPtr = fBitmap.rowBytes();
+ return fBitmap.getPixels();
+}
+
bool SkImage_Raster::getROPixels(SkBitmap* dst) const {
*dst = fBitmap;
return true;
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 20e7505966..179a33c8bf 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -6,8 +6,11 @@
*/
#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkImageEncoder.h"
#include "SkRRect.h"
#include "SkSurface.h"
+#include "SkUtils.h"
#include "Test.h"
#if SK_SUPPORT_GPU
@@ -24,31 +27,32 @@ enum SurfaceType {
};
static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context) {
- static const SkImageInfo imageSpec = {
- 10, // width
- 10, // height
- kPMColor_SkColorType,
- kPremul_SkAlphaType
- };
+ static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
switch (surfaceType) {
case kRaster_SurfaceType:
- return SkSurface::NewRaster(imageSpec);
+ return SkSurface::NewRaster(info);
case kGpu_SurfaceType:
#if SK_SUPPORT_GPU
SkASSERT(NULL != context);
- return SkSurface::NewRenderTarget(context, imageSpec);
+ return SkSurface::NewRenderTarget(context, info);
#else
SkASSERT(0);
#endif
case kPicture_SurfaceType:
- return SkSurface::NewPicture(10, 10);
+ return SkSurface::NewPicture(info.fWidth, info.fHeight);
}
SkASSERT(0);
return NULL;
}
-#include "SkData.h"
+enum ImageType {
+ kRasterCopy_ImageType,
+ kRasterData_ImageType,
+ kGpu_ImageType,
+ kPicture_ImageType,
+ kCodec_ImageType,
+};
static void test_image(skiatest::Reporter* reporter) {
SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
@@ -56,7 +60,7 @@ static void test_image(skiatest::Reporter* reporter) {
size_t size = info.getSafeSize(rowBytes);
void* addr = sk_malloc_throw(size);
SkData* data = SkData::NewFromMalloc(addr, size);
-
+
REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
REPORTER_ASSERT(reporter, 2 == data->getRefCnt());
@@ -65,6 +69,81 @@ static void test_image(skiatest::Reporter* reporter) {
data->unref();
}
+static SkImage* createImage(ImageType imageType, GrContext* context,
+ SkColor color) {
+ const SkPMColor pmcolor = SkPreMultiplyColor(color);
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
+ const size_t rowBytes = info.minRowBytes();
+ const size_t size = rowBytes * info.fHeight;
+
+ void* addr = sk_malloc_throw(size);
+ sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
+ SkAutoTUnref<SkData> data(SkData::NewFromMalloc(addr, size));
+
+ switch (imageType) {
+ case kRasterCopy_ImageType:
+ return SkImage::NewRasterCopy(info, addr, rowBytes);
+ case kRasterData_ImageType:
+ return SkImage::NewRasterData(info, data, rowBytes);
+ case kGpu_ImageType:
+ return NULL; // TODO
+ case kPicture_ImageType: {
+ SkAutoTUnref<SkSurface> surf(SkSurface::NewPicture(info.fWidth,
+ info.fHeight));
+ surf->getCanvas()->drawColor(SK_ColorRED);
+ return surf->newImageSnapshot();
+ }
+ case kCodec_ImageType: {
+ SkBitmap bitmap;
+ bitmap.installPixels(info, addr, rowBytes, NULL, NULL);
+ SkAutoTUnref<SkData> src(
+ SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type,
+ 100));
+ return SkImage::NewEncodedData(src);
+ }
+ }
+ SkASSERT(false);
+ return NULL;
+}
+
+static void test_imagepeek(skiatest::Reporter* reporter) {
+ static const struct {
+ ImageType fType;
+ bool fPeekShouldSucceed;
+ } gRec[] = {
+ { kRasterCopy_ImageType, true },
+ { kRasterData_ImageType, true },
+ { kGpu_ImageType, false },
+ { kPicture_ImageType, false },
+ { kCodec_ImageType, false },
+ };
+
+ const SkColor color = SK_ColorRED;
+ const SkPMColor pmcolor = SkPreMultiplyColor(color);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+ SkImageInfo info;
+ size_t rowBytes;
+
+ SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color));
+ if (!image.get()) {
+ continue; // gpu may not be enabled
+ }
+ const void* addr = image->peekPixels(&info, &rowBytes);
+ bool success = (NULL != addr);
+ REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
+ if (success) {
+ REPORTER_ASSERT(reporter, 10 == info.fWidth);
+ REPORTER_ASSERT(reporter, 10 == info.fHeight);
+ REPORTER_ASSERT(reporter, kPMColor_SkColorType == info.fColorType);
+ REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.fAlphaType ||
+ kOpaque_SkAlphaType == info.fAlphaType);
+ REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
+ REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
+ }
+ }
+}
+
static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
GrContext* context) {
// Verify that the right canvas commands trigger a copy on write
@@ -257,6 +336,7 @@ DEF_GPUTEST(Surface, reporter, factory) {
TestSurfaceWritableAfterSnapshotRelease(reporter, kPicture_SurfaceType, NULL);
TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
+ test_imagepeek(reporter);
#if SK_SUPPORT_GPU
TestGetTexture(reporter, kRaster_SurfaceType, NULL);
TestGetTexture(reporter, kPicture_SurfaceType, NULL);