add callbacks to Images that wrap client-provided content

BUG=skia:

Review URL: https://codereview.chromium.org/1169553003
This commit is contained in:
reed 2015-06-18 13:41:40 -07:00 committed by Commit bot
parent d7395d82d5
commit de49988bc2
8 changed files with 185 additions and 26 deletions

View File

@ -44,10 +44,23 @@ public:
SK_DECLARE_INST_COUNT(SkImage) SK_DECLARE_INST_COUNT(SkImage)
typedef SkImageInfo Info; typedef SkImageInfo Info;
typedef void* ReleaseContext;
static SkImage* NewRasterCopy(const Info&, const void* pixels, size_t rowBytes); static SkImage* NewRasterCopy(const Info&, const void* pixels, size_t rowBytes);
static SkImage* NewRasterData(const Info&, SkData* pixels, size_t rowBytes); static SkImage* NewRasterData(const Info&, SkData* pixels, size_t rowBytes);
typedef void (*RasterReleaseProc)(const void* pixels, ReleaseContext);
/**
* Return a new Image referencing the specified pixels. These must remain valid and unchanged
* until the specified release-proc is called, indicating that Skia no longer has a reference
* to the pixels.
*
* Returns NULL if the requested Info is unsupported.
*/
static SkImage* NewFromRaster(const Info&, const void* pixels, size_t rowBytes,
RasterReleaseProc, ReleaseContext);
/** /**
* Construct a new SkImage based on the given ImageGenerator. * Construct a new SkImage based on the given ImageGenerator.
* This function will always take ownership of the passed * This function will always take ownership of the passed
@ -70,8 +83,25 @@ public:
* *
* Will return NULL if the specified descriptor is unsupported. * Will return NULL if the specified descriptor is unsupported.
*/ */
static SkImage* NewFromTexture(GrContext*, const GrBackendTextureDesc&, static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc) {
SkAlphaType = kPremul_SkAlphaType); return NewFromTexture(ctx, desc, kPremul_SkAlphaType, NULL, NULL);
}
static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& de, SkAlphaType at) {
return NewFromTexture(ctx, de, at, NULL, NULL);
}
typedef void (*TextureReleaseProc)(ReleaseContext);
/**
* Create a new image from the specified descriptor. The underlying platform texture must stay
* valid and unaltered until the specified release-proc is invoked, indicating that Skia
* nolonger is holding a reference to it.
*
* Will return NULL if the specified descriptor is unsupported.
*/
static SkImage* NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
TextureReleaseProc, ReleaseContext);
/** /**
* Create a new image from the specified descriptor. Note - Skia will delete or recycle the * Create a new image from the specified descriptor. Note - Skia will delete or recycle the

View File

@ -282,7 +282,11 @@ public:
} }
size_t getSafeSize(size_t rowBytes) const { size_t getSafeSize(size_t rowBytes) const {
return (size_t)this->getSafeSize64(rowBytes); int64_t size = this->getSafeSize64(rowBytes);
if (!sk_64_isS32(size)) {
return 0;
}
return sk_64_asS32(size);
} }
bool validRowBytes(size_t rowBytes) const { bool validRowBytes(size_t rowBytes) const {

View File

@ -127,6 +127,14 @@ public:
inline GrSurfacePriv surfacePriv(); inline GrSurfacePriv surfacePriv();
inline const GrSurfacePriv surfacePriv() const; inline const GrSurfacePriv surfacePriv() const;
typedef void* ReleaseCtx;
typedef void (*ReleaseProc)(ReleaseCtx);
void setRelease(ReleaseProc proc, ReleaseCtx ctx) {
fReleaseProc = proc;
fReleaseCtx = ctx;
}
protected: protected:
// Methods made available via GrSurfacePriv // Methods made available via GrSurfacePriv
SkImageInfo info() const; SkImageInfo info() const;
@ -140,12 +148,32 @@ protected:
GrSurface(GrGpu* gpu, LifeCycle lifeCycle, const GrSurfaceDesc& desc) GrSurface(GrGpu* gpu, LifeCycle lifeCycle, const GrSurfaceDesc& desc)
: INHERITED(gpu, lifeCycle) : INHERITED(gpu, lifeCycle)
, fDesc(desc) { , fDesc(desc)
, fReleaseProc(NULL)
, fReleaseCtx(NULL)
{}
~GrSurface() override {
// check that invokeReleaseProc has been called (if needed)
SkASSERT(NULL == fReleaseProc);
} }
GrSurfaceDesc fDesc; GrSurfaceDesc fDesc;
void invokeReleaseProc() {
if (fReleaseProc) {
fReleaseProc(fReleaseCtx);
fReleaseProc = NULL;
}
}
void onRelease() override;
void onAbandon() override;
private: private:
ReleaseProc fReleaseProc;
ReleaseCtx fReleaseCtx;
typedef GrGpuResource INHERITED; typedef GrGpuResource INHERITED;
}; };

View File

@ -125,3 +125,13 @@ bool GrSurface::hasPendingIO() const {
} }
return false; return false;
} }
void GrSurface::onRelease() {
this->invokeReleaseProc();
this->INHERITED::onRelease();
}
void GrSurface::onAbandon() {
this->invokeReleaseProc();
this->INHERITED::onAbandon();
}

View File

@ -205,7 +205,8 @@ SkImage* SkImage_Base::onNewImage(int newWidth, int newHeight, const SkIRect* su
#if !SK_SUPPORT_GPU #if !SK_SUPPORT_GPU
SkImage* SkImage::NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType) { SkImage* SkImage::NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
TextureReleaseProc, ReleaseContext) {
return NULL; return NULL;
} }

View File

@ -115,7 +115,9 @@ bool SkImage_Gpu::onReadPixels(const SkImageInfo& info, void* pixels, size_t row
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc, static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc,
SkAlphaType at, GrWrapOwnership ownership) { SkAlphaType at, GrWrapOwnership ownership,
SkImage::TextureReleaseProc releaseProc,
SkImage::ReleaseContext releaseCtx) {
if (desc.fWidth <= 0 || desc.fHeight <= 0) { if (desc.fWidth <= 0 || desc.fHeight <= 0) {
return NULL; return NULL;
} }
@ -123,18 +125,23 @@ static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextur
if (!tex) { if (!tex) {
return NULL; return NULL;
} }
if (releaseProc) {
tex->setRelease(releaseProc, releaseCtx);
}
const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted; const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
return SkNEW_ARGS(SkImage_Gpu, (desc.fWidth, desc.fHeight, at, tex, 0, budgeted)); return SkNEW_ARGS(SkImage_Gpu, (desc.fWidth, desc.fHeight, at, tex, 0, budgeted));
} }
SkImage* SkImage::NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc, SkAlphaType at) { SkImage* SkImage::NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc, SkAlphaType at,
return new_wrapped_texture_common(ctx, desc, at, kBorrow_GrWrapOwnership); TextureReleaseProc releaseP, ReleaseContext releaseC) {
return new_wrapped_texture_common(ctx, desc, at, kBorrow_GrWrapOwnership, releaseP, releaseC);
} }
SkImage* SkImage::NewFromAdoptedTexture(GrContext* ctx, const GrBackendTextureDesc& desc, SkImage* SkImage::NewFromAdoptedTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
SkAlphaType at) { SkAlphaType at) {
return new_wrapped_texture_common(ctx, desc, at, kAdopt_GrWrapOwnership); return new_wrapped_texture_common(ctx, desc, at, kAdopt_GrWrapOwnership, NULL, NULL);
} }
SkImage* SkImage::NewFromTextureCopy(GrContext* ctx, const GrBackendTextureDesc& srcDesc, SkImage* SkImage::NewFromTextureCopy(GrContext* ctx, const GrBackendTextureDesc& srcDesc,

View File

@ -16,9 +16,8 @@
class SkImage_Raster : public SkImage_Base { class SkImage_Raster : public SkImage_Base {
public: public:
static bool ValidArgs(const Info& info, size_t rowBytes) { static bool ValidArgs(const Info& info, size_t rowBytes, size_t* minSize) {
const int maxDimension = SK_MaxS32 >> 2; const int maxDimension = SK_MaxS32 >> 2;
const size_t kMaxPixelByteSize = SK_MaxS32;
if (info.width() <= 0 || info.height() <= 0) { if (info.width() <= 0 || info.height() <= 0) {
return false; return false;
@ -43,10 +42,14 @@ public:
return false; return false;
} }
int64_t size = (int64_t)info.height() * rowBytes; size_t size = info.getSafeSize(rowBytes);
if (size > (int64_t)kMaxPixelByteSize) { if (0 == size) {
return false; return false;
} }
if (minSize) {
*minSize = size;
}
return true; return true;
} }
@ -146,23 +149,24 @@ bool SkImage_Raster::getROPixels(SkBitmap* dst) const {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
SkImage* SkImage::NewRasterCopy(const SkImageInfo& info, const void* pixels, size_t rowBytes) { SkImage* SkImage::NewRasterCopy(const SkImageInfo& info, const void* pixels, size_t rowBytes) {
if (!SkImage_Raster::ValidArgs(info, rowBytes) || !pixels) { size_t size;
if (!SkImage_Raster::ValidArgs(info, rowBytes, &size) || !pixels) {
return NULL; return NULL;
} }
// Here we actually make a copy of the caller's pixel data // Here we actually make a copy of the caller's pixel data
SkAutoDataUnref data(SkData::NewWithCopy(pixels, info.height() * rowBytes)); SkAutoDataUnref data(SkData::NewWithCopy(pixels, size));
return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL)); return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL));
} }
SkImage* SkImage::NewRasterData(const SkImageInfo& info, SkData* data, size_t rowBytes) { SkImage* SkImage::NewRasterData(const SkImageInfo& info, SkData* data, size_t rowBytes) {
if (!SkImage_Raster::ValidArgs(info, rowBytes) || !data) { size_t size;
if (!SkImage_Raster::ValidArgs(info, rowBytes, &size) || !data) {
return NULL; return NULL;
} }
// did they give us enough data? // did they give us enough data?
size_t size = info.height() * rowBytes;
if (data->size() < size) { if (data->size() < size) {
return NULL; return NULL;
} }
@ -170,6 +174,17 @@ SkImage* SkImage::NewRasterData(const SkImageInfo& info, SkData* data, size_t ro
return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL)); return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL));
} }
SkImage* SkImage::NewFromRaster(const SkImageInfo& info, const void* pixels, size_t rowBytes,
RasterReleaseProc proc, ReleaseContext ctx) {
size_t size;
if (!SkImage_Raster::ValidArgs(info, rowBytes, &size) || !pixels) {
return NULL;
}
SkAutoDataUnref data(SkData::NewWithProc(pixels, size, proc, ctx));
return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL));
}
SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator) { SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator) {
SkBitmap bitmap; SkBitmap bitmap;
if (!SkInstallDiscardablePixelRef(generator, &bitmap)) { if (!SkInstallDiscardablePixelRef(generator, &bitmap)) {
@ -185,7 +200,7 @@ SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator) {
SkImage* SkNewImageFromPixelRef(const SkImageInfo& info, SkPixelRef* pr, SkImage* SkNewImageFromPixelRef(const SkImageInfo& info, SkPixelRef* pr,
const SkIPoint& pixelRefOrigin, size_t rowBytes, const SkIPoint& pixelRefOrigin, size_t rowBytes,
const SkSurfaceProps* props) { const SkSurfaceProps* props) {
if (!SkImage_Raster::ValidArgs(info, rowBytes)) { if (!SkImage_Raster::ValidArgs(info, rowBytes, NULL)) {
return NULL; return NULL;
} }
return SkNEW_ARGS(SkImage_Raster, (info, pr, pixelRefOrigin, rowBytes, props)); return SkNEW_ARGS(SkImage_Raster, (info, pr, pixelRefOrigin, rowBytes, props));
@ -193,7 +208,7 @@ SkImage* SkNewImageFromPixelRef(const SkImageInfo& info, SkPixelRef* pr,
SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef, SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef,
const SkSurfaceProps* props) { const SkSurfaceProps* props) {
if (!SkImage_Raster::ValidArgs(bm.info(), bm.rowBytes())) { if (!SkImage_Raster::ValidArgs(bm.info(), bm.rowBytes(), NULL)) {
return NULL; return NULL;
} }

View File

@ -65,6 +65,7 @@ static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context,
enum ImageType { enum ImageType {
kRasterCopy_ImageType, kRasterCopy_ImageType,
kRasterData_ImageType, kRasterData_ImageType,
kRasterProc_ImageType,
kGpu_ImageType, kGpu_ImageType,
kCodec_ImageType, kCodec_ImageType,
}; };
@ -81,6 +82,7 @@ static void test_empty_image(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterCopy(info, NULL, 0)); REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterCopy(info, NULL, 0));
REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterData(info, NULL, 0)); REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterData(info, NULL, 0));
REPORTER_ASSERT(reporter, NULL == SkImage::NewFromRaster(info, NULL, 0, NULL, NULL));
REPORTER_ASSERT(reporter, NULL == SkImage::NewFromGenerator(SkNEW(EmptyGenerator))); REPORTER_ASSERT(reporter, NULL == SkImage::NewFromGenerator(SkNEW(EmptyGenerator)));
} }
@ -204,7 +206,21 @@ static void test_image(skiatest::Reporter* reporter) {
data->unref(); data->unref();
} }
static SkImage* createImage(ImageType imageType, GrContext* context, SkColor color) { // Want to ensure that our Release is called when the owning image is destroyed
struct ReleaseDataContext {
skiatest::Reporter* fReporter;
SkData* fData;
static void Release(const void* pixels, void* context) {
ReleaseDataContext* state = (ReleaseDataContext*)context;
REPORTER_ASSERT(state->fReporter, state->fData);
state->fData->unref();
state->fData = NULL;
}
};
static SkImage* createImage(ImageType imageType, GrContext* context, SkColor color,
ReleaseDataContext* releaseContext) {
const SkPMColor pmcolor = SkPreMultiplyColor(color); const SkPMColor pmcolor = SkPreMultiplyColor(color);
const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
const size_t rowBytes = info.minRowBytes(); const size_t rowBytes = info.minRowBytes();
@ -219,6 +235,11 @@ static SkImage* createImage(ImageType imageType, GrContext* context, SkColor col
return SkImage::NewRasterCopy(info, addr, rowBytes); return SkImage::NewRasterCopy(info, addr, rowBytes);
case kRasterData_ImageType: case kRasterData_ImageType:
return SkImage::NewRasterData(info, data, rowBytes); return SkImage::NewRasterData(info, data, rowBytes);
case kRasterProc_ImageType:
SkASSERT(releaseContext);
releaseContext->fData = SkRef(data.get());
return SkImage::NewFromRaster(info, addr, rowBytes,
ReleaseDataContext::Release, releaseContext);
case kGpu_ImageType: { case kGpu_ImageType: {
SkAutoTUnref<SkSurface> surf( SkAutoTUnref<SkSurface> surf(
SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0)); SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0));
@ -302,6 +323,7 @@ static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* facto
} gRec[] = { } gRec[] = {
{ kRasterCopy_ImageType, true, "RasterCopy" }, { kRasterCopy_ImageType, true, "RasterCopy" },
{ kRasterData_ImageType, true, "RasterData" }, { kRasterData_ImageType, true, "RasterData" },
{ kRasterProc_ImageType, true, "RasterProc" },
{ kGpu_ImageType, false, "Gpu" }, { kGpu_ImageType, false, "Gpu" },
{ kCodec_ImageType, false, "Codec" }, { kCodec_ImageType, false, "Codec" },
}; };
@ -317,15 +339,25 @@ static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* facto
} }
#endif #endif
ReleaseDataContext releaseCtx;
releaseCtx.fReporter = reporter;
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
SkImageInfo info; SkImageInfo info;
size_t rowBytes; size_t rowBytes;
SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, ctx, color)); releaseCtx.fData = NULL;
SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, ctx, color, &releaseCtx));
if (!image.get()) { if (!image.get()) {
SkDebugf("failed to createImage[%d] %s\n", i, gRec[i].fName); SkDebugf("failed to createImage[%d] %s\n", i, gRec[i].fName);
continue; // gpu may not be enabled continue; // gpu may not be enabled
} }
if (kRasterProc_ImageType == gRec[i].fType) {
REPORTER_ASSERT(reporter, NULL != releaseCtx.fData); // we are tracking the data
} else {
REPORTER_ASSERT(reporter, NULL == releaseCtx.fData); // we ignored the context
}
const void* addr = image->peekPixels(&info, &rowBytes); const void* addr = image->peekPixels(&info, &rowBytes);
bool success = SkToBool(addr); bool success = SkToBool(addr);
REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success); REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
@ -341,6 +373,7 @@ static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* facto
test_image_readpixels(reporter, image, pmcolor); test_image_readpixels(reporter, image, pmcolor);
} }
REPORTER_ASSERT(reporter, NULL == releaseCtx.fData); // we released the data
} }
static void test_canvaspeek(skiatest::Reporter* reporter, static void test_canvaspeek(skiatest::Reporter* reporter,
@ -739,7 +772,28 @@ DEF_GPUTEST(Surface, reporter, factory) {
} }
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject texID, bool doCopy) {
struct ReleaseTextureContext {
ReleaseTextureContext(skiatest::Reporter* reporter) {
fReporter = reporter;
fIsReleased = false;
}
skiatest::Reporter* fReporter;
bool fIsReleased;
void doRelease() {
REPORTER_ASSERT(fReporter, false == fIsReleased);
fIsReleased = true;
}
static void ReleaseProc(void* context) {
((ReleaseTextureContext*)context)->doRelease();
}
};
static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject texID,
ReleaseTextureContext* releaseContext) {
GrBackendTextureDesc desc; GrBackendTextureDesc desc;
desc.fConfig = kSkia8888_GrPixelConfig; desc.fConfig = kSkia8888_GrPixelConfig;
// need to be a rendertarget for now... // need to be a rendertarget for now...
@ -748,7 +802,10 @@ static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject te
desc.fHeight = h; desc.fHeight = h;
desc.fSampleCnt = 0; desc.fSampleCnt = 0;
desc.fTextureHandle = texID; desc.fTextureHandle = texID;
return doCopy ? SkImage::NewFromTextureCopy(ctx, desc) : SkImage::NewFromTexture(ctx, desc); return releaseContext
? SkImage::NewFromTexture(ctx, desc, kPremul_SkAlphaType,
ReleaseTextureContext::ReleaseProc, releaseContext)
: SkImage::NewFromTextureCopy(ctx, desc, kPremul_SkAlphaType);
} }
static void test_image_color(skiatest::Reporter* reporter, SkImage* image, SkPMColor expected) { static void test_image_color(skiatest::Reporter* reporter, SkImage* image, SkPMColor expected) {
@ -787,8 +844,10 @@ DEF_GPUTEST(SkImage_NewFromTexture, reporter, factory) {
} }
GrBackendObject srcTex = tex->getTextureHandle(); GrBackendObject srcTex = tex->getTextureHandle();
SkAutoTUnref<SkImage> refImg(make_desc_image(ctx, w, h, srcTex, false)); ReleaseTextureContext releaseCtx(reporter);
SkAutoTUnref<SkImage> cpyImg(make_desc_image(ctx, w, h, srcTex, true));
SkAutoTUnref<SkImage> refImg(make_desc_image(ctx, w, h, srcTex, &releaseCtx));
SkAutoTUnref<SkImage> cpyImg(make_desc_image(ctx, w, h, srcTex, NULL));
test_image_color(reporter, refImg, expected0); test_image_color(reporter, refImg, expected0);
test_image_color(reporter, cpyImg, expected0); test_image_color(reporter, cpyImg, expected0);
@ -801,5 +860,10 @@ DEF_GPUTEST(SkImage_NewFromTexture, reporter, factory) {
// We expect the ref'd image to see the new color, but cpy'd one should still see the old color // We expect the ref'd image to see the new color, but cpy'd one should still see the old color
test_image_color(reporter, refImg, expected1); test_image_color(reporter, refImg, expected1);
test_image_color(reporter, cpyImg, expected0); test_image_color(reporter, cpyImg, expected0);
// Now exercise the release proc
REPORTER_ASSERT(reporter, !releaseCtx.fIsReleased);
refImg.reset(NULL); // force a release of the image
REPORTER_ASSERT(reporter, releaseCtx.fIsReleased);
} }
#endif #endif