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
This commit is contained in:
reed@google.com 2014-02-06 14:11:56 +00:00
parent 999da9c5e4
commit 4f7c61583b
5 changed files with 260 additions and 17 deletions

View File

@ -75,6 +75,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

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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);