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:
parent
999da9c5e4
commit
4f7c61583b
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user