Reland "Add async rescale and read APIs to SkImage."
This reverts commit 1caf3789f8c6e7e8d50f08cd56a5b725922aa044.
Makes the image GMs detect an abandoned context just like the surface
GMs.
Bug: skia:10431
Change-Id: I56a3631a75e6b0383f96a73f461cfa314ee29afa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299379
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/asyncrescaleandread.cpp b/gm/asyncrescaleandread.cpp
index f119144..ad825e3 100644
--- a/gm/asyncrescaleandread.cpp
+++ b/gm/asyncrescaleandread.cpp
@@ -23,14 +23,14 @@
namespace {
struct AsyncContext {
bool fCalled = false;
- std::unique_ptr<const SkSurface::AsyncReadResult> fResult;
+ std::unique_ptr<const SkImage::AsyncReadResult> fResult;
};
} // anonymous namespace
// Making this a lambda in the test functions caused:
// "error: cannot compile this forwarded non-trivially copyable parameter yet"
// on x86/Win/Clang bot, referring to 'result'.
-static void async_callback(void* c, std::unique_ptr<const SkSurface::AsyncReadResult> result) {
+static void async_callback(void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) {
auto context = static_cast<AsyncContext*>(c);
context->fResult = std::move(result);
context->fCalled = true;
@@ -38,61 +38,70 @@
// Draws the image to a surface, does a asyncRescaleAndReadPixels of the image, and then sticks
// the result in a raster image.
-static sk_sp<SkImage> do_read_and_scale(SkSurface* surface, const SkIRect& srcRect,
- const SkImageInfo& ii, SkSurface::RescaleGamma rescaleGamma,
+template <typename Src>
+static sk_sp<SkImage> do_read_and_scale(Src* src,
+ GrContext* context,
+ const SkIRect& srcRect,
+ const SkImageInfo& ii,
+ SkImage::RescaleGamma rescaleGamma,
SkFilterQuality quality) {
- auto* context = new AsyncContext();
- surface->asyncRescaleAndReadPixels(ii, srcRect, rescaleGamma, quality, async_callback, context);
- if (auto ctx = surface->getContext()) {
- ctx->submit();
+ auto* asyncContext = new AsyncContext();
+ src->asyncRescaleAndReadPixels(ii, srcRect, rescaleGamma, quality, async_callback,
+ asyncContext);
+ if (context) {
+ context->submit();
}
- while (!context->fCalled) {
+ while (!asyncContext->fCalled) {
// Only GPU should actually be asynchronous.
- SkASSERT(surface->getCanvas()->getGrContext());
- surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
+ SkASSERT(context);
+ context->checkAsyncWorkCompletion();
}
- if (!context->fResult) {
+ if (!asyncContext->fResult) {
return nullptr;
}
- SkPixmap pixmap(ii, context->fResult->data(0), context->fResult->rowBytes(0));
+ SkPixmap pixmap(ii, asyncContext->fResult->data(0), asyncContext->fResult->rowBytes(0));
auto releasePixels = [](const void*, void* c) { delete static_cast<AsyncContext*>(c); };
- return SkImage::MakeFromRaster(pixmap, releasePixels, context);
+ return SkImage::MakeFromRaster(pixmap, releasePixels, asyncContext);
}
-static sk_sp<SkImage> do_read_and_scale_yuv(SkSurface* surface, SkYUVColorSpace yuvCS,
- const SkIRect& srcRect, SkISize size,
- SkSurface::RescaleGamma rescaleGamma,
- SkFilterQuality quality, SkScopeExit* cleanup) {
+template <typename Src>
+static sk_sp<SkImage> do_read_and_scale_yuv(Src* src,
+ GrContext* context,
+ SkYUVColorSpace yuvCS,
+ const SkIRect& srcRect,
+ SkISize size,
+ SkImage::RescaleGamma rescaleGamma,
+ SkFilterQuality quality,
+ SkScopeExit* cleanup) {
SkASSERT(!(size.width() & 0b1) && !(size.height() & 0b1));
SkISize uvSize = {size.width()/2, size.height()/2};
SkImageInfo yII = SkImageInfo::Make(size, kGray_8_SkColorType, kPremul_SkAlphaType);
SkImageInfo uvII = SkImageInfo::Make(uvSize, kGray_8_SkColorType, kPremul_SkAlphaType);
- AsyncContext context;
- surface->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(), srcRect, size,
- rescaleGamma, quality, async_callback, &context);
- if (auto ctx = surface->getContext()) {
- ctx->submit();
+ AsyncContext asyncContext;
+ src->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(), srcRect, size,
+ rescaleGamma, quality, async_callback, &asyncContext);
+ if (context) {
+ context->submit();
}
- while (!context.fCalled) {
+ while (!asyncContext.fCalled) {
// Only GPU should actually be asynchronous.
- SkASSERT(surface->getCanvas()->getGrContext());
- surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
+ SkASSERT(context);
+ context->checkAsyncWorkCompletion();
}
- if (!context.fResult) {
+ if (!asyncContext.fResult) {
return nullptr;
}
- auto* gr = surface->getCanvas()->getGrContext();
GrBackendTexture backendTextures[3];
- SkPixmap yPM(yII, context.fResult->data(0), context.fResult->rowBytes(0));
- SkPixmap uPM(uvII, context.fResult->data(1), context.fResult->rowBytes(1));
- SkPixmap vPM(uvII, context.fResult->data(2), context.fResult->rowBytes(2));
+ SkPixmap yPM(yII, asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0));
+ SkPixmap uPM(uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1));
+ SkPixmap vPM(uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2));
- backendTextures[0] = gr->createBackendTexture(yPM, GrRenderable::kNo, GrProtected::kNo);
- backendTextures[1] = gr->createBackendTexture(uPM, GrRenderable::kNo, GrProtected::kNo);
- backendTextures[2] = gr->createBackendTexture(vPM, GrRenderable::kNo, GrProtected::kNo);
+ backendTextures[0] = context->createBackendTexture(yPM, GrRenderable::kNo, GrProtected::kNo);
+ backendTextures[1] = context->createBackendTexture(uPM, GrRenderable::kNo, GrProtected::kNo);
+ backendTextures[2] = context->createBackendTexture(vPM, GrRenderable::kNo, GrProtected::kNo);
SkYUVAIndex indices[4] = {
{ 0, SkColorChannel::kR},
@@ -101,25 +110,31 @@
{-1, SkColorChannel::kR}
};
- *cleanup = {[gr, backendTextures] {
+ *cleanup = {[context, backendTextures] {
GrFlushInfo flushInfo;
flushInfo.fFlags = kSyncCpu_GrFlushFlag;
- gr->flush(flushInfo);
- gr->submit(true);
- gr->deleteBackendTexture(backendTextures[0]);
- gr->deleteBackendTexture(backendTextures[1]);
- gr->deleteBackendTexture(backendTextures[2]);
+ context->flush(flushInfo);
+ context->submit(true);
+ context->deleteBackendTexture(backendTextures[0]);
+ context->deleteBackendTexture(backendTextures[1]);
+ context->deleteBackendTexture(backendTextures[2]);
}};
- return SkImage::MakeFromYUVATextures(gr, yuvCS, backendTextures, indices, size,
+ return SkImage::MakeFromYUVATextures(context, yuvCS, backendTextures, indices, size,
kTopLeft_GrSurfaceOrigin, SkColorSpace::MakeSRGB());
}
// Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are
// rescale in src gamma and rescale in linear gamma.
-static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
- const SkIRect& srcRect, SkISize newSize, bool doYUV420,
- SkString* errorMsg, int pad = 0) {
+template <typename Src>
+static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas,
+ Src* src,
+ GrContext* context,
+ const SkIRect& srcRect,
+ SkISize newSize,
+ bool doYUV420,
+ SkString* errorMsg,
+ int pad = 0) {
if (doYUV420) {
if (!canvas->getGrContext() || !canvas->getGrContext()->priv().asDirectContext()) {
errorMsg->printf("YUV420 only supported on direct GPU for now.");
@@ -134,13 +149,13 @@
SkYUVColorSpace yuvColorSpace = kRec601_SkYUVColorSpace;
canvas->save();
- for (auto gamma : {SkSurface::RescaleGamma::kSrc, SkSurface::RescaleGamma::kLinear}) {
+ for (auto gamma : {SkImage::RescaleGamma::kSrc, SkImage::RescaleGamma::kLinear}) {
canvas->save();
for (auto quality : {kNone_SkFilterQuality, kLow_SkFilterQuality, kHigh_SkFilterQuality}) {
SkScopeExit cleanup;
sk_sp<SkImage> result;
if (doYUV420) {
- result = do_read_and_scale_yuv(surface, yuvColorSpace, srcRect, newSize, gamma,
+ result = do_read_and_scale_yuv(src, context, yuvColorSpace, srcRect, newSize, gamma,
quality, &cleanup);
if (!result) {
errorMsg->printf("YUV420 async call failed. Allowed for now.");
@@ -149,7 +164,7 @@
int nextCS = static_cast<int>(yuvColorSpace + 1) % (kLastEnum_SkYUVColorSpace + 1);
yuvColorSpace = static_cast<SkYUVColorSpace>(nextCS);
} else {
- result = do_read_and_scale(surface, srcRect, ii, gamma, quality);
+ result = do_read_and_scale(src, context, srcRect, ii, gamma, quality);
if (!result) {
errorMsg->printf("async read call failed.");
return skiagm::DrawResult::kFail;
@@ -165,9 +180,13 @@
return skiagm::DrawResult::kOk;
}
-static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas, const char* imageFile,
- const SkIRect& srcRect, SkISize newSize,
- bool doYUV420, SkString* errorMsg) {
+static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas,
+ const char* imageFile,
+ const SkIRect& srcRect,
+ SkISize newSize,
+ bool doSurface,
+ bool doYUV420,
+ SkString* errorMsg) {
auto image = GetResourceAsImage(imageFile);
if (!image) {
errorMsg->printf("Could not load image file %s.", imageFile);
@@ -177,54 +196,89 @@
*errorMsg = "Not supported on recording/vector backends.";
return skiagm::DrawResult::kSkip;
}
- // Turn the image into a surface in order to call the read and rescale API
- auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
- auto surface = canvas->makeSurface(surfInfo);
- if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
- surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
- surface = canvas->makeSurface(surfInfo);
- }
- if (!surface) {
- *errorMsg = "Could not create surface for image.";
- // When testing abandoned GrContext we expect surface creation to fail.
- if (canvas->getGrContext() && canvas->getGrContext()->abandoned()) {
- return skiagm::DrawResult::kSkip;
+ if (doSurface) {
+ // Turn the image into a surface in order to call the read and rescale API
+ auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
+ auto surface = canvas->makeSurface(surfInfo);
+ if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
+ surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
+ surface = canvas->makeSurface(surfInfo);
}
- return skiagm::DrawResult::kFail;
+ if (!surface) {
+ *errorMsg = "Could not create surface for image.";
+ // When testing abandoned GrContext we expect surface creation to fail.
+ if (canvas->getGrContext() && canvas->getGrContext()->abandoned()) {
+ return skiagm::DrawResult::kSkip;
+ }
+ return skiagm::DrawResult::kFail;
+ }
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawImage(image, 0, 0, &paint);
+ return do_rescale_grid(canvas, surface.get(), canvas->getGrContext(), srcRect, newSize,
+ doYUV420, errorMsg);
+ } else if (auto ctx = canvas->getGrContext()) {
+ image = image->makeTextureImage(ctx);
+ if (!image) {
+ *errorMsg = "Could not create image.";
+ // When testing abandoned GrContext we expect surface creation to fail.
+ if (canvas->getGrContext() && canvas->getGrContext()->abandoned()) {
+ return skiagm::DrawResult::kSkip;
+ }
+ return skiagm::DrawResult::kFail;
+ }
}
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- surface->getCanvas()->drawImage(image, 0, 0, &paint);
- return do_rescale_grid(canvas, surface.get(), srcRect, newSize, doYUV420, errorMsg);
+ return do_rescale_grid(canvas, image.get(), canvas->getGrContext(), srcRect, newSize, doYUV420,
+ errorMsg);
}
-#define DEF_RESCALE_AND_READ_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
- DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
- ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
- return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, false, errorMsg); \
+#define DEF_RESCALE_AND_READ_SURF_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
+ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
+ ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
+ return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, false, \
+ errorMsg); \
}
-#define DEF_RESCALE_AND_READ_YUV_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
- DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
- ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
- return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, errorMsg); \
+#define DEF_RESCALE_AND_READ_YUV_SURF_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
+ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
+ ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
+ return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, true, errorMsg); \
}
-DEF_RESCALE_AND_READ_YUV_GM(images/yellow_rose.webp, rose, SkIRect::MakeXYWH(50, 5, 200, 150),
- 410, 376)
+#define DEF_RESCALE_AND_READ_IMG_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
+ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
+ ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
+ return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, false, false, \
+ errorMsg); \
+ }
-DEF_RESCALE_AND_READ_GM(images/yellow_rose.webp, rose, SkIRect::MakeXYWH(100, 20, 100, 100),
- 410, 410)
+#define DEF_RESCALE_AND_READ_YUV_IMG_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
+ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
+ ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
+ return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, true, errorMsg); \
+ }
-DEF_RESCALE_AND_READ_GM(images/dog.jpg, dog_down, SkIRect::MakeXYWH(0, 10, 180, 150), 45, 45)
-DEF_RESCALE_AND_READ_GM(images/dog.jpg, dog_up, SkIRect::MakeWH(180, 180), 800, 400)
+DEF_RESCALE_AND_READ_YUV_SURF_GM(
+ images/yellow_rose.webp, rose, SkIRect::MakeXYWH(50, 5, 200, 150), 410, 376)
-DEF_RESCALE_AND_READ_GM(images/text.png, text_down, SkIRect::MakeWH(637, 105), (int)(0.7 * 637),
- (int)(0.7 * 105))
-DEF_RESCALE_AND_READ_GM(images/text.png, text_up, SkIRect::MakeWH(637, 105), (int)(1.2 * 637),
- (int)(1.2 * 105))
-DEF_RESCALE_AND_READ_GM(images/text.png, text_up_large, SkIRect::MakeXYWH(300, 0, 300, 105),
- (int)(2.4 * 300), (int)(2.4 * 105))
+DEF_RESCALE_AND_READ_YUV_IMG_GM(
+ images/yellow_rose.webp, rose_down, SkIRect::MakeXYWH(50, 5, 200, 150), 106, 60)
+
+DEF_RESCALE_AND_READ_SURF_GM(
+ images/yellow_rose.webp, rose, SkIRect::MakeXYWH(100, 20, 100, 100), 410, 410)
+
+DEF_RESCALE_AND_READ_SURF_GM(images/dog.jpg, dog_down, SkIRect::MakeXYWH(0, 10, 180, 150), 45, 45)
+DEF_RESCALE_AND_READ_IMG_GM(images/dog.jpg, dog_up, SkIRect::MakeWH(180, 180), 800, 400)
+
+DEF_RESCALE_AND_READ_IMG_GM(
+ images/text.png, text_down, SkIRect::MakeWH(637, 105), (int)(0.7 * 637), (int)(0.7 * 105))
+DEF_RESCALE_AND_READ_SURF_GM(
+ images/text.png, text_up, SkIRect::MakeWH(637, 105), (int)(1.2 * 637), (int)(1.2 * 105))
+DEF_RESCALE_AND_READ_IMG_GM(images/text.png,
+ text_up_large,
+ SkIRect::MakeXYWH(300, 0, 300, 105),
+ (int)(2.4 * 300),
+ (int)(2.4 * 105))
// Exercises non-scaling YUV420. Reads from the original canvas's surface in order to
// exercise case where source surface is not a texture (in glbert config).
@@ -244,8 +298,8 @@
SkScopeExit scopeExit;
auto yuvImage = do_read_and_scale_yuv(
- surface, kRec601_SkYUVColorSpace, SkIRect::MakeWH(400, 300), {400, 300},
- SkSurface::RescaleGamma::kSrc, kNone_SkFilterQuality, &scopeExit);
+ surface, surface->getContext(), kRec601_SkYUVColorSpace, SkIRect::MakeWH(400, 300),
+ {400, 300}, SkImage::RescaleGamma::kSrc, kNone_SkFilterQuality, &scopeExit);
canvas->clear(SK_ColorWHITE);
canvas->drawImage(yuvImage.get(), 0, 0);
@@ -283,13 +337,17 @@
canvas->translate(kPad, kPad);
skiagm::DrawResult result;
SkISize downSize = {static_cast<int>(kInner/2), static_cast<int>(kInner / 2)};
- result = do_rescale_grid(canvas, surface.get(), srcRect, downSize, false, errorMsg, kPad);
+ GrContext* context = canvas->getGrContext();
+ result = do_rescale_grid(canvas, surface.get(), context, srcRect, downSize, false, errorMsg,
+ kPad);
+
if (result != skiagm::DrawResult::kOk) {
return result;
}
canvas->translate(0, 4 * downSize.height());
SkISize upSize = {static_cast<int>(kInner * 3.5), static_cast<int>(kInner * 4.6)};
- result = do_rescale_grid(canvas, surface.get(), srcRect, upSize, false, errorMsg, kPad);
+ result =
+ do_rescale_grid(canvas, surface.get(), context, srcRect, upSize, false, errorMsg, kPad);
if (result != skiagm::DrawResult::kOk) {
return result;
}