Reland "Add async rescale and read APIs to SkImage."

This reverts commit 1caf3789f8.

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>
This commit is contained in:
Brian Salomon 2020-06-26 13:32:09 -04:00 committed by Skia Commit-Bot
parent 56079c4c3e
commit 63a0a758ce
22 changed files with 1381 additions and 820 deletions

View File

@ -1410,6 +1410,8 @@ if (skia_enable_tools) {
deps = []
public_deps = []
sources = [
"tools/gpu/BackendTextureImageFactory.cpp",
"tools/gpu/BackendTextureImageFactory.h",
"tools/gpu/GrContextFactory.cpp",
"tools/gpu/GrTest.cpp",
"tools/gpu/MemoryCache.cpp",

View File

@ -16,6 +16,9 @@ Milestone 86
* Enable BackendSemaphores for the Direct3D backend.
https://review.skia.org/298752
* Added SkImage:asyncRescaleAndReadPixels and SkImage::asyncRescaleAndReadPixelsYUV420
https://review.skia.org/299281
* * *
Milestone 85

View File

@ -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 @@ static void async_callback(void* c, std::unique_ptr<const SkSurface::AsyncReadRe
// 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 @@ static sk_sp<SkImage> do_read_and_scale_yuv(SkSurface* surface, SkYUVColorSpace
{-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 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
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 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
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 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
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 @@ static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas, const char* im
*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);
}
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;
}
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 @@ DEF_SIMPLE_GM_CAN_FAIL(async_yuv_no_scale, canvas, errorMsg, 400, 300) {
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 @@ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_no_bleed, canvas, errorMsg, 60, 60
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;
}

View File

@ -418,6 +418,8 @@ skia_core_sources = [
"$_src/image/SkImage_Lazy.cpp",
"$_src/image/SkImage_Lazy.h",
"$_src/image/SkImage_Raster.cpp",
"$_src/image/SkRescaleAndReadPixels.cpp",
"$_src/image/SkRescaleAndReadPixels.h",
"$_src/image/SkSurface.cpp",
"$_src/image/SkSurface_Base.h",

View File

@ -958,6 +958,108 @@ public:
bool readPixels(const SkPixmap& dst, int srcX, int srcY,
CachingHint cachingHint = kAllow_CachingHint) const;
/** The result from asyncRescaleAndReadPixels() or asyncRescaleAndReadPixelsYUV420(). */
class AsyncReadResult {
public:
AsyncReadResult(const AsyncReadResult&) = delete;
AsyncReadResult(AsyncReadResult&&) = delete;
AsyncReadResult& operator=(const AsyncReadResult&) = delete;
AsyncReadResult& operator=(AsyncReadResult&&) = delete;
virtual ~AsyncReadResult() = default;
virtual int count() const = 0;
virtual const void* data(int i) const = 0;
virtual size_t rowBytes(int i) const = 0;
protected:
AsyncReadResult() = default;
};
/** Client-provided context that is passed to client-provided ReadPixelsContext. */
using ReadPixelsContext = void*;
/** Client-provided callback to asyncRescaleAndReadPixels() or
asyncRescaleAndReadPixelsYUV420() that is called when read result is ready or on failure.
*/
using ReadPixelsCallback = void(ReadPixelsContext, std::unique_ptr<const AsyncReadResult>);
enum class RescaleGamma : bool { kSrc, kLinear };
/** Makes image pixel data available to caller, possibly asynchronously. It can also rescale
the image pixels.
Currently asynchronous reads are only supported on the GPU backend and only when the
underlying 3D API supports transfer buffers and CPU/GPU synchronization primitives. In all
other cases this operates synchronously.
Data is read from the source sub-rectangle, is optionally converted to a linear gamma, is
rescaled to the size indicated by 'info', is then converted to the color space, color type,
and alpha type of 'info'. A 'srcRect' that is not contained by the bounds of the image
causes failure.
When the pixel data is ready the caller's ReadPixelsCallback is called with a
AsyncReadResult containing pixel data in the requested color type, alpha type, and color
space. The AsyncReadResult will have count() == 1. Upon failure the callback is called with
nullptr for AsyncReadResult. For a GPU image this flushes work but a submit must occur to
guarantee a finite time before the callback is called.
The data is valid for the lifetime of AsyncReadResult with the exception that if the SkImage
is GPU-backed the data is immediately invalidated if the GrContext is abandoned or
destroyed.
@param info info of the requested pixels
@param srcRect subrectangle of image to read
@param rescaleGamma controls whether rescaling is done in the image's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with result of the read
@param context passed to callback
*/
void asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
/**
Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The
RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three
planes ordered y, u, v. The u and v planes are half the width and height of the resized
rectangle. The y, u, and v values are single bytes. Currently this fails if 'dstSize'
width and height are not even. A 'srcRect' that is not contained by the bounds of the
image causes failure.
When the pixel data is ready the caller's ReadPixelsCallback is called with a
AsyncReadResult containing the planar data. The AsyncReadResult will have count() == 3.
Upon failure the callback is called with nullptr for AsyncReadResult. For a GPU image this
flushes work but a submit must occur to guarantee a finite time before the callback is
called.
The data is valid for the lifetime of AsyncReadResult with the exception that if the SkImage
is GPU-backed the data is immediately invalidated if the GrContext is abandoned or
destroyed.
@param yuvColorSpace The transformation from RGB to YUV. Applied to the resized image
after it is converted to dstColorSpace.
@param dstColorSpace The color space to convert the resized image to, after rescaling.
@param srcRect The portion of the image to rescale and convert to YUV planes.
@param dstSize The size to rescale srcRect to
@param rescaleGamma controls whether rescaling is done in the image's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with the planar read result
@param context passed to callback
*/
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
/** Copies SkImage to dst, scaling pixels to fit dst.width() and dst.height(), and
converting pixels to match dst.colorType() and dst.alphaType(). Returns true if
pixels are copied. Returns false if dst.addr() is nullptr, or dst.rowBytes() is

View File

@ -769,22 +769,7 @@ public:
*/
bool readPixels(const SkBitmap& dst, int srcX, int srcY);
/** The result from asyncRescaleAndReadPixels() or asyncRescaleAndReadPixelsYUV420(). */
class AsyncReadResult {
public:
AsyncReadResult(const AsyncReadResult&) = delete;
AsyncReadResult(AsyncReadResult&&) = delete;
AsyncReadResult& operator=(const AsyncReadResult&) = delete;
AsyncReadResult& operator=(AsyncReadResult&&) = delete;
virtual ~AsyncReadResult() = default;
virtual int count() const = 0;
virtual const void* data(int i) const = 0;
virtual size_t rowBytes(int i) const = 0;
protected:
AsyncReadResult() = default;
};
using AsyncReadResult = SkImage::AsyncReadResult;
/** Client-provided context that is passed to client-provided ReadPixelsContext. */
using ReadPixelsContext = void*;
@ -797,7 +782,7 @@ public:
/** Controls the gamma that rescaling occurs in for asyncRescaleAndReadPixels() and
asyncRescaleAndReadPixelsYUV420().
*/
enum RescaleGamma : bool { kSrc, kLinear };
using RescaleGamma = SkImage::RescaleGamma;
/** Makes surface pixel data available to caller, possibly asynchronously. It can also rescale
the surface pixels.
@ -829,9 +814,12 @@ public:
@param callback function to call with result of the read
@param context passed to callback
*/
void asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallback callback, ReadPixelsContext context);
void asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
/**
Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The
@ -869,7 +857,7 @@ public:
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext);
ReadPixelsContext context);
/** Copies SkRect of pixels from the src SkPixmap to the SkSurface.

View File

@ -956,7 +956,7 @@ func (b *taskBuilder) dmFlags(internalHardwareLabel string) {
match = append(match, "~WritePixelsNonTextureMSAA_Gpu")
}
if b.extraConfig("Direct3D") {
if b.extraConfig("Direct3D") {
// skia:9935
match = append(match, "~^ColorTypeBackendAllocationTest$")
match = append(match, "~^CompressedBackendAllocationTest$")
@ -971,10 +971,13 @@ func (b *taskBuilder) dmFlags(internalHardwareLabel string) {
match = append(match, "~^TextureIdleStateTest$")
match = append(match, "~^TextureProxyTest$")
}
if b.extraConfig("Direct3D") && b.gpu("RadeonHD7770") && b.matchOs("Win") {
if b.extraConfig("Direct3D") && b.matchOs("Win") {
// skia:9935
match = append(match, "~^AsyncReadPixels$")
match = append(match, "~^ImageAsyncReadPixels$")
}
if b.extraConfig("Direct3D") && b.gpu("RadeonHD7770") && b.matchOs("Win") {
// skia:9935
match = append(match, "~^SurfaceAsyncReadPixels$")
match = append(match, "~^MorphologyFilterRadiusWithMirrorCTM_Gpu$")
match = append(match, "~^ReadPixels_Gpu$")
match = append(match, "~^ReadPixels_Texture$")

View File

@ -49064,7 +49064,7 @@
"skia/infra/bots/run_recipe.py",
"${ISOLATED_OUTDIR}",
"test",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Debug-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"Clang\\\",\\\"configuration\\\",\\\"Debug\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"RadeonHD7770\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"ShuttleA\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^AsyncReadPixels$\\\",\\\"~^MorphologyFilterRadiusWithMirrorCTM_Gpu$\\\",\\\"~^ReadPixels_Gpu$\\\",\\\"~^ReadPixels_Texture$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Debug-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Debug-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"Clang\\\",\\\"configuration\\\",\\\"Debug\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"RadeonHD7770\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"ShuttleA\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^ImageAsyncReadPixels$\\\",\\\"~^SurfaceAsyncReadPixels$\\\",\\\"~^MorphologyFilterRadiusWithMirrorCTM_Gpu$\\\",\\\"~^ReadPixels_Gpu$\\\",\\\"~^ReadPixels_Texture$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Debug-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"skia"
],
"dependencies": [
@ -49396,7 +49396,7 @@
"skia/infra/bots/run_recipe.py",
"${ISOLATED_OUTDIR}",
"test",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"Clang\\\",\\\"configuration\\\",\\\"Release\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"RadeonHD7770\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"ShuttleA\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^AsyncReadPixels$\\\",\\\"~^MorphologyFilterRadiusWithMirrorCTM_Gpu$\\\",\\\"~^ReadPixels_Gpu$\\\",\\\"~^ReadPixels_Texture$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"Clang\\\",\\\"configuration\\\",\\\"Release\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"RadeonHD7770\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"ShuttleA\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^ImageAsyncReadPixels$\\\",\\\"~^SurfaceAsyncReadPixels$\\\",\\\"~^MorphologyFilterRadiusWithMirrorCTM_Gpu$\\\",\\\"~^ReadPixels_Gpu$\\\",\\\"~^ReadPixels_Texture$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"skia"
],
"dependencies": [
@ -50143,7 +50143,7 @@
"skia/infra/bots/run_recipe.py",
"${ISOLATED_OUTDIR}",
"test",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Debug-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"MSVC\\\",\\\"configuration\\\",\\\"Debug\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"QuadroP400\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"Golo\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Debug-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Debug-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"MSVC\\\",\\\"configuration\\\",\\\"Debug\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"QuadroP400\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"Golo\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^ImageAsyncReadPixels$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Debug-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"skia"
],
"dependencies": [
@ -50392,7 +50392,7 @@
"skia/infra/bots/run_recipe.py",
"${ISOLATED_OUTDIR}",
"test",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Release-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"MSVC\\\",\\\"configuration\\\",\\\"Release\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"QuadroP400\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"Golo\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Release-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Release-All-Direct3D\",\"dm_flags\":\"[\\\"dm\\\",\\\"--nameByHash\\\",\\\"--key\\\",\\\"arch\\\",\\\"x86_64\\\",\\\"compiler\\\",\\\"MSVC\\\",\\\"configuration\\\",\\\"Release\\\",\\\"cpu_or_gpu\\\",\\\"GPU\\\",\\\"cpu_or_gpu_value\\\",\\\"QuadroP400\\\",\\\"extra_config\\\",\\\"Direct3D\\\",\\\"model\\\",\\\"Golo\\\",\\\"os\\\",\\\"Win10\\\",\\\"style\\\",\\\"default\\\",\\\"--randomProcessorTest\\\",\\\"--nocpu\\\",\\\"--config\\\",\\\"d3d\\\",\\\"--src\\\",\\\"tests\\\",\\\"gm\\\",\\\"image\\\",\\\"colorImage\\\",\\\"svg\\\",\\\"--blacklist\\\",\\\"_\\\",\\\"svg\\\",\\\"_\\\",\\\"svgparse_\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"pal8os2v2-16.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgba32abf.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24prof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rgb24lprof.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"8bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"4bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"32bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"24bpp-pixeldata-cropped.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"testimgari.jpg\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle8-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"rle4-height-negative.bmp\\\",\\\"_\\\",\\\"image\\\",\\\"gen_platf\\\",\\\"error\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced1.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced2.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\"interlaced3.png\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".arw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".cr2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".dng\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".nrw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".orf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".raf\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".rw2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".pef\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".srw\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ARW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".CR2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".DNG\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".NRW\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".ORF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RAF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".RW2\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".PEF\\\",\\\"_\\\",\\\"image\\\",\\\"_\\\",\\\".SRW\\\",\\\"--match\\\",\\\"~^ColorTypeBackendAllocationTest$\\\",\\\"~^CompressedBackendAllocationTest$\\\",\\\"~^DDLSkSurfaceFlush$\\\",\\\"~^GrBackendTextureImageMipMappedTest$\\\",\\\"~^GrMeshTest$\\\",\\\"~^GrSurfaceRenderability$\\\",\\\"~^GrTextureMipMapInvalidationTest$\\\",\\\"~^PremulAlphaRoundTrip_Gpu$\\\",\\\"~^ReplaceSurfaceBackendTexture$\\\",\\\"~^SkImage_makeTextureImage$\\\",\\\"~^TextureIdleStateTest$\\\",\\\"~^TextureProxyTest$\\\",\\\"~^ImageAsyncReadPixels$\\\",\\\"--nonativeFonts\\\",\\\"--verbose\\\"]\",\"dm_properties\":\"{\\\"buildbucket_build_id\\\":\\\"<(BUILDBUCKET_BUILD_ID)\\\",\\\"builder\\\":\\\"Test-Win10-MSVC-Golo-GPU-QuadroP400-x86_64-Release-All-Direct3D\\\",\\\"gitHash\\\":\\\"<(REVISION)\\\",\\\"issue\\\":\\\"<(ISSUE)\\\",\\\"patch_storage\\\":\\\"<(PATCH_STORAGE)\\\",\\\"patchset\\\":\\\"<(PATCHSET)\\\",\\\"swarming_bot_id\\\":\\\"${SWARMING_BOT_ID}\\\",\\\"swarming_task_id\\\":\\\"${SWARMING_TASK_ID}\\\",\\\"task_id\\\":\\\"<(TASK_ID)\\\"}\",\"do_upload\":\"true\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"images\":\"true\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"resources\":\"true\",\"revision\":\"<(REVISION)\",\"skps\":\"true\",\"svgs\":\"true\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
"skia"
],
"dependencies": [

View File

@ -23,12 +23,10 @@
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRRectPriv.h"
#include "src/core/SkSurfacePriv.h"
#include "src/core/SkYUVMath.h"
#include "src/gpu/GrAppliedClip.h"
#include "src/gpu/GrAuditTrail.h"
#include "src/gpu/GrBlurUtils.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrClientMappedBufferManager.h"
#include "src/gpu/GrClip.h"
#include "src/gpu/GrColor.h"
#include "src/gpu/GrContextPriv.h"
@ -50,7 +48,6 @@
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
#include "src/gpu/effects/GrRRectEffect.h"
#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/geometry/GrQuadUtils.h"
#include "src/gpu/geometry/GrStyledShape.h"
@ -1716,526 +1713,6 @@ void GrRenderTargetContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHand
this->addOp(std::move(op));
}
void GrRenderTargetContext::asyncRescaleAndReadPixels(
const SkImageInfo& info, const SkIRect& srcRect, SkSurface::RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality, ReadPixelsCallback callback, ReadPixelsContext context) {
auto direct = fContext->priv().asDirectContext();
if (!direct) {
callback(context, nullptr);
return;
}
if (this->asRenderTargetProxy()->wrapsVkSecondaryCB()) {
callback(context, nullptr);
return;
}
if (this->asRenderTargetProxy()->framebufferOnly()) {
callback(context, nullptr);
return;
}
auto dstCT = SkColorTypeToGrColorType(info.colorType());
if (dstCT == GrColorType::kUnknown) {
callback(context, nullptr);
return;
}
bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
auto colorTypeOfFinalContext = this->colorInfo().colorType();
auto backendFormatOfFinalContext = this->asSurfaceProxy()->backendFormat();
if (needsRescale) {
colorTypeOfFinalContext = dstCT;
backendFormatOfFinalContext = this->caps()->getDefaultBackendFormat(dstCT,
GrRenderable::kYes);
}
auto readInfo = this->caps()->supportedReadPixelsColorType(colorTypeOfFinalContext,
backendFormatOfFinalContext, dstCT);
// Fail if we can't read from the source surface's color type.
if (readInfo.fColorType == GrColorType::kUnknown) {
callback(context, nullptr);
return;
}
// Fail if read color type does not have all of dstCT's color channels and those missing color
// channels are in the src.
uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType);
uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
if ((~legalReadChannels & dstChannels) & srcChannels) {
callback(context, nullptr);
return;
}
std::unique_ptr<GrRenderTargetContext> tempRTC;
int x = srcRect.fLeft;
int y = srcRect.fTop;
if (needsRescale) {
tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma,
rescaleQuality);
if (!tempRTC) {
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin);
x = y = 0;
} else {
sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(),
this->colorInfo().alphaType(),
info.colorSpace(),
info.alphaType());
// Insert a draw to a temporary surface if we need to do a y-flip or color space conversion.
if (this->origin() == kBottomLeft_GrSurfaceOrigin || xform) {
// We flip or color convert by drawing and we don't currently support drawing to
// kPremul.
if (info.alphaType() == kUnpremul_SkAlphaType) {
callback(context, nullptr);
return;
}
GrSurfaceProxyView texProxyView = this->readSurfaceView();
SkRect srcRectToDraw = SkRect::Make(srcRect);
// If the src is not texturable first try to make a copy to a texture.
if (!texProxyView.asTextureProxy()) {
texProxyView =
GrSurfaceProxyView::Copy(fContext, texProxyView, GrMipMapped::kNo, srcRect,
SkBackingFit::kApprox, SkBudgeted::kNo);
if (!texProxyView) {
callback(context, nullptr);
return;
}
SkASSERT(texProxyView.asTextureProxy());
srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
}
tempRTC = GrRenderTargetContext::Make(
direct, this->colorInfo().colorType(), info.refColorSpace(),
SkBackingFit::kApprox, srcRect.size(), 1, GrMipMapped::kNo, GrProtected::kNo,
kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr);
return;
}
tempRTC->drawTexture(nullptr, std::move(texProxyView), this->colorInfo().alphaType(),
GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
SK_PMColor4fWHITE, srcRectToDraw,
SkRect::MakeWH(srcRect.width(), srcRect.height()), GrAA::kNo,
GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
SkMatrix::I(), std::move(xform));
x = y = 0;
}
}
auto rtc = tempRTC ? tempRTC.get() : this;
return rtc->asyncReadPixels(SkIRect::MakeXYWH(x, y, info.width(), info.height()),
info.colorType(), callback, context);
}
class GrRenderTargetContext::AsyncReadResult : public SkSurface::AsyncReadResult {
public:
AsyncReadResult(uint32_t inboxID) : fInboxID(inboxID) {}
~AsyncReadResult() override {
for (int i = 0; i < fPlanes.count(); ++i) {
if (!fPlanes[i].fMappedBuffer) {
delete[] static_cast<const char*>(fPlanes[i].fData);
} else {
GrClientMappedBufferManager::BufferFinishedMessageBus::Post(
{std::move(fPlanes[i].fMappedBuffer), fInboxID});
}
}
}
int count() const override { return fPlanes.count(); }
const void* data(int i) const override { return fPlanes[i].fData; }
size_t rowBytes(int i) const override { return fPlanes[i].fRowBytes; }
bool addTransferResult(const PixelTransferResult& result,
SkISize dimensions,
size_t rowBytes,
GrClientMappedBufferManager* manager) {
SkASSERT(!result.fTransferBuffer->isMapped());
const void* mappedData = result.fTransferBuffer->map();
if (!mappedData) {
return false;
}
if (result.fPixelConverter) {
std::unique_ptr<char[]> convertedData(new char[rowBytes * dimensions.height()]);
result.fPixelConverter(convertedData.get(), mappedData);
this->addCpuPlane(std::move(convertedData), rowBytes);
result.fTransferBuffer->unmap();
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
}
return true;
}
void addCpuPlane(std::unique_ptr<const char[]> data, size_t rowBytes) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
fPlanes.emplace_back(data.release(), rowBytes, nullptr);
}
private:
void addMappedPlane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> mappedBuffer) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
SkASSERT(mappedBuffer);
SkASSERT(mappedBuffer->isMapped());
fPlanes.emplace_back(data, rowBytes, std::move(mappedBuffer));
}
struct Plane {
Plane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> buffer)
: fData(data), fRowBytes(rowBytes), fMappedBuffer(std::move(buffer)) {}
const void* fData;
size_t fRowBytes;
// If this is null then fData is heap alloc and must be delete[]ed as const char[].
sk_sp<GrGpuBuffer> fMappedBuffer;
};
SkSTArray<3, Plane> fPlanes;
uint32_t fInboxID;
};
void GrRenderTargetContext::asyncReadPixels(const SkIRect& rect, SkColorType colorType,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
callback(context, nullptr);
return;
}
auto directContext = fContext->priv().asDirectContext();
SkASSERT(directContext);
auto mappedBufferManager = directContext->priv().clientMappedBufferManager();
auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
if (!transferResult.fTransferBuffer) {
auto ii = SkImageInfo::Make(rect.size(), colorType,
this->colorInfo().alphaType(),
this->colorInfo().refColorSpace());
auto result = std::make_unique<AsyncReadResult>(0);
std::unique_ptr<char[]> data(new char[ii.computeMinByteSize()]);
SkPixmap pm(ii, data.get(), ii.minRowBytes());
result->addCpuPlane(std::move(data), pm.rowBytes());
if (!this->readPixels(ii, pm.writable_addr(), pm.rowBytes(), {rect.fLeft, rect.fTop})) {
callback(context, nullptr);
return;
}
callback(context, std::move(result));
return;
}
struct FinishContext {
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
SkISize fSize;
SkColorType fColorType;
GrClientMappedBufferManager* fMappedBufferManager;
PixelTransferResult fTransferResult;
};
// Assumption is that the caller would like to flush. We could take a parameter or require an
// explicit flush from the caller. We'd have to have a way to defer attaching the finish
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback,
context,
rect.size(),
colorType,
mappedBufferManager,
std::move(transferResult)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
size_t rowBytes = context->fSize.width() * SkColorTypeBytesPerPixel(context->fColorType);
if (!result->addTransferResult(context->fTransferResult, context->fSize, rowBytes,
context->fMappedBufferManager)) {
result.reset();
}
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;
flushInfo.fFinishedContext = finishContext;
flushInfo.fFinishedProc = finishCallback;
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr);
}
void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
SkISize dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
SkASSERT(!dstSize.isZero());
SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
auto direct = fContext->priv().asDirectContext();
if (!direct) {
callback(context, nullptr);
return;
}
if (this->asRenderTargetProxy()->wrapsVkSecondaryCB()) {
callback(context, nullptr);
return;
}
if (this->asRenderTargetProxy()->framebufferOnly()) {
callback(context, nullptr);
return;
}
if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
callback(context, nullptr);
return;
}
int x = srcRect.fLeft;
int y = srcRect.fTop;
bool needsRescale = srcRect.size() != dstSize;
GrSurfaceProxyView srcView;
if (needsRescale) {
// We assume the caller wants kPremul. There is no way to indicate a preference.
auto info = SkImageInfo::Make(dstSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
dstColorSpace);
// TODO: Incorporate the YUV conversion into last pass of rescaling.
auto tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma,
rescaleQuality);
if (!tempRTC) {
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin);
x = y = 0;
srcView = tempRTC->readSurfaceView();
} else {
srcView = this->readSurfaceView();
if (!srcView.asTextureProxy()) {
srcView = GrSurfaceProxyView::Copy(fContext, std::move(srcView), GrMipMapped::kNo,
srcRect, SkBackingFit::kApprox, SkBudgeted::kYes);
if (!srcView) {
// If we can't get a texture copy of the contents then give up.
callback(context, nullptr);
return;
}
SkASSERT(srcView.asTextureProxy());
x = y = 0;
}
// We assume the caller wants kPremul. There is no way to indicate a preference.
sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make(
this->colorInfo().colorSpace(), this->colorInfo().alphaType(), dstColorSpace.get(),
kPremul_SkAlphaType);
if (xform) {
SkRect srcRectToDraw = SkRect::MakeXYWH(x, y, srcRect.width(), srcRect.height());
auto tempRTC = GrRenderTargetContext::Make(
direct, this->colorInfo().colorType(), dstColorSpace, SkBackingFit::kApprox,
dstSize, 1, GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr);
return;
}
tempRTC->drawTexture(nullptr, std::move(srcView), this->colorInfo().alphaType(),
GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
SK_PMColor4fWHITE, srcRectToDraw, SkRect::Make(srcRect.size()),
GrAA::kNo, GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
SkMatrix::I(), std::move(xform));
srcView = tempRTC->readSurfaceView();
SkASSERT(srcView.asTextureProxy());
x = y = 0;
}
}
auto yRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, dstSize, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
int halfW = dstSize.width()/2;
int halfH = dstSize.height()/2;
auto uRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
auto vRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!yRTC || !uRTC || !vRTC) {
callback(context, nullptr);
return;
}
float baseM[20];
SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
// TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
auto texMatrix = SkMatrix::Translate(x, y);
SkRect dstRectY = SkRect::Make(dstSize);
SkRect dstRectUV = SkRect::MakeWH(halfW, halfH);
bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport();
PixelTransferResult yTransfer, uTransfer, vTransfer;
// This matrix generates (r,g,b,a) = (0, 0, 0, y)
float yM[20];
std::fill_n(yM, 15, 0.f);
std::copy_n(baseM + 0, 5, yM + 15);
GrPaint yPaint;
auto yTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
auto yColFP = GrColorMatrixFragmentProcessor::Make(std::move(yTexFP), yM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
yPaint.addColorFragmentProcessor(std::move(yColFP));
yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
yRTC->fillRectToRect(nullptr, std::move(yPaint), GrAA::kNo, SkMatrix::I(),
dstRectY, dstRectY);
if (!doSynchronousRead) {
yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(yRTC->width(), yRTC->height()));
if (!yTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
texMatrix.preScale(2.f, 2.f);
// This matrix generates (r,g,b,a) = (0, 0, 0, u)
float uM[20];
std::fill_n(uM, 15, 0.f);
std::copy_n(baseM + 5, 5, uM + 15);
GrPaint uPaint;
auto uTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix,
GrSamplerState::Filter::kBilerp);
auto uColFP = GrColorMatrixFragmentProcessor::Make(std::move(uTexFP), uM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
uPaint.addColorFragmentProcessor(std::move(uColFP));
uPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
uRTC->fillRectToRect(nullptr, std::move(uPaint), GrAA::kNo, SkMatrix::I(),
dstRectUV, dstRectUV);
if (!doSynchronousRead) {
uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(uRTC->width(), uRTC->height()));
if (!uTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
// This matrix generates (r,g,b,a) = (0, 0, 0, v)
float vM[20];
std::fill_n(vM, 15, 0.f);
std::copy_n(baseM + 10, 5, vM + 15);
GrPaint vPaint;
auto vTexFP = GrTextureEffect::Make(std::move(srcView), this->colorInfo().alphaType(),
texMatrix, GrSamplerState::Filter::kBilerp);
auto vColFP = GrColorMatrixFragmentProcessor::Make(std::move(vTexFP), vM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
vPaint.addColorFragmentProcessor(std::move(vColFP));
vPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
vRTC->fillRectToRect(nullptr, std::move(vPaint), GrAA::kNo, SkMatrix::I(),
dstRectUV, dstRectUV);
if (!doSynchronousRead) {
vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(vRTC->width(), vRTC->height()));
if (!vTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
if (doSynchronousRead) {
GrImageInfo yInfo(GrColorType::kAlpha_8, kPremul_SkAlphaType, nullptr, dstSize);
GrImageInfo uvInfo = yInfo.makeWH(halfW, halfH);
size_t yRB = yInfo.minRowBytes();
size_t uvRB = uvInfo.minRowBytes();
std::unique_ptr<char[]> y(new char[yRB * yInfo.height()]);
std::unique_ptr<char[]> u(new char[uvRB*uvInfo.height()]);
std::unique_ptr<char[]> v(new char[uvRB*uvInfo.height()]);
if (!yRTC->readPixels(yInfo, y.get(), yRB, {0, 0}, direct) ||
!uRTC->readPixels(uvInfo, u.get(), uvRB, {0, 0}, direct) ||
!vRTC->readPixels(uvInfo, v.get(), uvRB, {0, 0}, direct)) {
callback(context, nullptr);
return;
}
auto result = std::make_unique<AsyncReadResult>(direct->priv().contextID());
result->addCpuPlane(std::move(y), yRB );
result->addCpuPlane(std::move(u), uvRB);
result->addCpuPlane(std::move(v), uvRB);
callback(context, std::move(result));
return;
}
struct FinishContext {
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
GrClientMappedBufferManager* fMappedBufferManager;
SkISize fSize;
PixelTransferResult fYTransfer;
PixelTransferResult fUTransfer;
PixelTransferResult fVTransfer;
};
// Assumption is that the caller would like to flush. We could take a parameter or require an
// explicit flush from the caller. We'd have to have a way to defer attaching the finish
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback,
context,
direct->priv().clientMappedBufferManager(),
dstSize,
std::move(yTransfer),
std::move(uTransfer),
std::move(vTransfer)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
auto manager = context->fMappedBufferManager;
size_t rowBytes = SkToSizeT(context->fSize.width());
if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
rowBytes /= 2;
SkISize uvSize = {context->fSize.width()/2, context->fSize.height()/2};
if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;
flushInfo.fFinishedContext = finishContext;
flushInfo.fFinishedProc = finishCallback;
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr);
}
GrSemaphoresSubmitted GrRenderTargetContext::flush(SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
ASSERT_SINGLE_OWNER
if (fContext->priv().abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}
SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "flush", fContext);
return this->drawingManager()->flushSurface(this->asSurfaceProxy(), access, info, newState);
}
bool GrRenderTargetContext::waitOnSemaphores(int numSemaphores,
const GrBackendSemaphore waitSemaphores[]) {
ASSERT_SINGLE_OWNER

View File

@ -524,31 +524,6 @@ public:
*/
void drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
using ReadPixelsCallback = SkSurface::ReadPixelsCallback;
using ReadPixelsContext = SkSurface::ReadPixelsContext;
using RescaleGamma = SkSurface::RescaleGamma;
// GPU implementation for SkSurface::asyncRescaleAndReadPixels.
void asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallback callback, ReadPixelsContext context);
// GPU implementation for SkSurface::asyncRescaleAndReadPixelsYUV420.
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
SkISize dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
/**
* After this returns any pending surface IO will be issued to the backend 3D API and
* if the surface has MSAA it will be resolved.
*/
GrSemaphoresSubmitted flush(SkSurface::BackendSurfaceAccess access, const GrFlushInfo&,
const GrBackendSurfaceMutableState*);
/**
* The next time this GrRenderTargetContext is flushed, the gpu will wait on the passed in
* semaphores before executing any commands.
@ -694,12 +669,6 @@ private:
bool SK_WARN_UNUSED_RESULT setupDstProxyView(const GrOp& op,
GrXferProcessor::DstProxyView* result);
class AsyncReadResult;
// The async read step of asyncRescaleAndReadPixels()
void asyncReadPixels(const SkIRect& rect, SkColorType colorType, ReadPixelsCallback callback,
ReadPixelsContext context);
GrOpsTask* getOpsTask();
SkGlyphRunListPainter* glyphPainter() { return &fGlyphPainter; }

View File

@ -9,6 +9,7 @@
#include "include/private/GrRecordingContext.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkYUVMath.h"
#include "src/gpu/GrAuditTrail.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrDataUtils.h"
@ -22,6 +23,7 @@
#include "src/gpu/GrSurfacePriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
#define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner())
#define RETURN_FALSE_IF_ABANDONED if (this->fContext->priv().abandoned()) { return false; }
@ -474,6 +476,511 @@ bool GrSurfaceContext::writePixels(const GrImageInfo& origSrcInfo, const void* s
srcColorType, src, rowBytes);
}
void GrSurfaceContext::asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
auto direct = fContext->priv().asDirectContext();
// We implement this by rendering and we don't currently support rendering kUnpremul.
if (info.alphaType() == kUnpremul_SkAlphaType) {
callback(context, nullptr);
return;
}
if (!direct) {
callback(context, nullptr);
return;
}
auto rt = this->asRenderTargetProxy();
if (rt && rt->wrapsVkSecondaryCB()) {
callback(context, nullptr);
return;
}
if (rt && rt->framebufferOnly()) {
callback(context, nullptr);
return;
}
auto dstCT = SkColorTypeToGrColorType(info.colorType());
if (dstCT == GrColorType::kUnknown) {
callback(context, nullptr);
return;
}
bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
auto colorTypeOfFinalContext = this->colorInfo().colorType();
auto backendFormatOfFinalContext = this->asSurfaceProxy()->backendFormat();
if (needsRescale) {
colorTypeOfFinalContext = dstCT;
backendFormatOfFinalContext =
this->caps()->getDefaultBackendFormat(dstCT, GrRenderable::kYes);
}
auto readInfo = this->caps()->supportedReadPixelsColorType(colorTypeOfFinalContext,
backendFormatOfFinalContext, dstCT);
// Fail if we can't read from the source surface's color type.
if (readInfo.fColorType == GrColorType::kUnknown) {
callback(context, nullptr);
return;
}
// Fail if read color type does not have all of dstCT's color channels and those missing color
// channels are in the src.
uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType);
uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
if ((~legalReadChannels & dstChannels) & srcChannels) {
callback(context, nullptr);
return;
}
std::unique_ptr<GrRenderTargetContext> tempRTC;
int x = srcRect.fLeft;
int y = srcRect.fTop;
if (needsRescale) {
tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma,
rescaleQuality);
if (!tempRTC) {
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin);
x = y = 0;
} else {
sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(),
this->colorInfo().alphaType(),
info.colorSpace(),
info.alphaType());
// Insert a draw to a temporary surface if we need to do a y-flip or color space conversion.
if (this->origin() == kBottomLeft_GrSurfaceOrigin || xform) {
GrSurfaceProxyView texProxyView = this->readSurfaceView();
SkRect srcRectToDraw = SkRect::Make(srcRect);
// If the src is not texturable first try to make a copy to a texture.
if (!texProxyView.asTextureProxy()) {
texProxyView =
GrSurfaceProxyView::Copy(fContext, texProxyView, GrMipMapped::kNo, srcRect,
SkBackingFit::kApprox, SkBudgeted::kNo);
if (!texProxyView) {
callback(context, nullptr);
return;
}
SkASSERT(texProxyView.asTextureProxy());
srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
}
tempRTC = GrRenderTargetContext::Make(direct, this->colorInfo().colorType(),
info.refColorSpace(), SkBackingFit::kApprox,
srcRect.size(), 1, GrMipMapped::kNo,
GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr);
return;
}
tempRTC->drawTexture(nullptr, std::move(texProxyView), this->colorInfo().alphaType(),
GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
SK_PMColor4fWHITE, srcRectToDraw,
SkRect::MakeWH(srcRect.width(), srcRect.height()), GrAA::kNo,
GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
SkMatrix::I(), std::move(xform));
x = y = 0;
}
}
auto rtc = tempRTC ? tempRTC.get() : this;
return rtc->asyncReadPixels(SkIRect::MakeXYWH(x, y, info.width(), info.height()),
info.colorType(), callback, context);
}
class GrSurfaceContext::AsyncReadResult : public SkImage::AsyncReadResult {
public:
AsyncReadResult(uint32_t inboxID) : fInboxID(inboxID) {}
~AsyncReadResult() override {
for (int i = 0; i < fPlanes.count(); ++i) {
if (!fPlanes[i].fMappedBuffer) {
delete[] static_cast<const char*>(fPlanes[i].fData);
} else {
GrClientMappedBufferManager::BufferFinishedMessageBus::Post(
{std::move(fPlanes[i].fMappedBuffer), fInboxID});
}
}
}
int count() const override { return fPlanes.count(); }
const void* data(int i) const override { return fPlanes[i].fData; }
size_t rowBytes(int i) const override { return fPlanes[i].fRowBytes; }
bool addTransferResult(const PixelTransferResult& result,
SkISize dimensions,
size_t rowBytes,
GrClientMappedBufferManager* manager) {
SkASSERT(!result.fTransferBuffer->isMapped());
const void* mappedData = result.fTransferBuffer->map();
if (!mappedData) {
return false;
}
if (result.fPixelConverter) {
std::unique_ptr<char[]> convertedData(new char[rowBytes * dimensions.height()]);
result.fPixelConverter(convertedData.get(), mappedData);
this->addCpuPlane(std::move(convertedData), rowBytes);
result.fTransferBuffer->unmap();
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
}
return true;
}
void addCpuPlane(std::unique_ptr<const char[]> data, size_t rowBytes) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
fPlanes.emplace_back(data.release(), rowBytes, nullptr);
}
private:
void addMappedPlane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> mappedBuffer) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
SkASSERT(mappedBuffer);
SkASSERT(mappedBuffer->isMapped());
fPlanes.emplace_back(data, rowBytes, std::move(mappedBuffer));
}
struct Plane {
Plane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> buffer)
: fData(data), fRowBytes(rowBytes), fMappedBuffer(std::move(buffer)) {}
const void* fData;
size_t fRowBytes;
// If this is null then fData is heap alloc and must be delete[]ed as const char[].
sk_sp<GrGpuBuffer> fMappedBuffer;
};
SkSTArray<3, Plane> fPlanes;
uint32_t fInboxID;
};
void GrSurfaceContext::asyncReadPixels(const SkIRect& rect,
SkColorType colorType,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
callback(context, nullptr);
return;
}
auto directContext = fContext->priv().asDirectContext();
SkASSERT(directContext);
auto mappedBufferManager = directContext->priv().clientMappedBufferManager();
auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
if (!transferResult.fTransferBuffer) {
auto ii = SkImageInfo::Make(rect.size(), colorType, this->colorInfo().alphaType(),
this->colorInfo().refColorSpace());
auto result = std::make_unique<AsyncReadResult>(0);
std::unique_ptr<char[]> data(new char[ii.computeMinByteSize()]);
SkPixmap pm(ii, data.get(), ii.minRowBytes());
result->addCpuPlane(std::move(data), pm.rowBytes());
if (!this->readPixels(ii, pm.writable_addr(), pm.rowBytes(), {rect.fLeft, rect.fTop})) {
callback(context, nullptr);
return;
}
callback(context, std::move(result));
return;
}
struct FinishContext {
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
SkISize fSize;
SkColorType fColorType;
GrClientMappedBufferManager* fMappedBufferManager;
PixelTransferResult fTransferResult;
};
// Assumption is that the caller would like to flush. We could take a parameter or require an
// explicit flush from the caller. We'd have to have a way to defer attaching the finish
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback,
context,
rect.size(),
colorType,
mappedBufferManager,
std::move(transferResult)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
size_t rowBytes = context->fSize.width() * SkColorTypeBytesPerPixel(context->fColorType);
if (!result->addTransferResult(context->fTransferResult, context->fSize, rowBytes,
context->fMappedBufferManager)) {
result.reset();
}
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;
flushInfo.fFinishedContext = finishContext;
flushInfo.fFinishedProc = finishCallback;
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr);
}
void GrSurfaceContext::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
SkISize dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
SkASSERT(!dstSize.isZero());
SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
auto direct = fContext->priv().asDirectContext();
if (!direct) {
callback(context, nullptr);
return;
}
auto rt = this->asRenderTargetProxy();
if (rt && rt->wrapsVkSecondaryCB()) {
callback(context, nullptr);
return;
}
if (rt && rt->framebufferOnly()) {
callback(context, nullptr);
return;
}
if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
callback(context, nullptr);
return;
}
int x = srcRect.fLeft;
int y = srcRect.fTop;
bool needsRescale = srcRect.size() != dstSize;
GrSurfaceProxyView srcView;
if (needsRescale) {
// We assume the caller wants kPremul. There is no way to indicate a preference.
auto info = SkImageInfo::Make(dstSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
dstColorSpace);
// TODO: Incorporate the YUV conversion into last pass of rescaling.
auto tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma,
rescaleQuality);
if (!tempRTC) {
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin);
x = y = 0;
srcView = tempRTC->readSurfaceView();
} else {
srcView = this->readSurfaceView();
if (!srcView.asTextureProxy()) {
srcView = GrSurfaceProxyView::Copy(fContext, std::move(srcView), GrMipMapped::kNo,
srcRect, SkBackingFit::kApprox, SkBudgeted::kYes);
if (!srcView) {
// If we can't get a texture copy of the contents then give up.
callback(context, nullptr);
return;
}
SkASSERT(srcView.asTextureProxy());
x = y = 0;
}
// We assume the caller wants kPremul. There is no way to indicate a preference.
sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make(
this->colorInfo().colorSpace(), this->colorInfo().alphaType(), dstColorSpace.get(),
kPremul_SkAlphaType);
if (xform) {
SkRect srcRectToDraw = SkRect::MakeXYWH(x, y, srcRect.width(), srcRect.height());
auto tempRTC = GrRenderTargetContext::Make(
direct, this->colorInfo().colorType(), dstColorSpace, SkBackingFit::kApprox,
dstSize, 1, GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr);
return;
}
tempRTC->drawTexture(nullptr, std::move(srcView), this->colorInfo().alphaType(),
GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
SK_PMColor4fWHITE, srcRectToDraw, SkRect::Make(srcRect.size()),
GrAA::kNo, GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
SkMatrix::I(), std::move(xform));
srcView = tempRTC->readSurfaceView();
SkASSERT(srcView.asTextureProxy());
x = y = 0;
}
}
auto yRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, dstSize, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
int halfW = dstSize.width() /2;
int halfH = dstSize.height()/2;
auto uRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
auto vRTC = GrRenderTargetContext::MakeWithFallback(
direct, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, 1,
GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!yRTC || !uRTC || !vRTC) {
callback(context, nullptr);
return;
}
float baseM[20];
SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
// TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
auto texMatrix = SkMatrix::Translate(x, y);
SkRect dstRectY = SkRect::Make(dstSize);
SkRect dstRectUV = SkRect::MakeWH(halfW, halfH);
bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport();
PixelTransferResult yTransfer, uTransfer, vTransfer;
// This matrix generates (r,g,b,a) = (0, 0, 0, y)
float yM[20];
std::fill_n(yM, 15, 0.f);
std::copy_n(baseM + 0, 5, yM + 15);
GrPaint yPaint;
auto yTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
auto yColFP = GrColorMatrixFragmentProcessor::Make(std::move(yTexFP), yM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
yPaint.addColorFragmentProcessor(std::move(yColFP));
yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
yRTC->fillRectToRect(nullptr, std::move(yPaint), GrAA::kNo, SkMatrix::I(), dstRectY, dstRectY);
if (!doSynchronousRead) {
yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(yRTC->width(), yRTC->height()));
if (!yTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
texMatrix.preScale(2.f, 2.f);
// This matrix generates (r,g,b,a) = (0, 0, 0, u)
float uM[20];
std::fill_n(uM, 15, 0.f);
std::copy_n(baseM + 5, 5, uM + 15);
GrPaint uPaint;
auto uTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix,
GrSamplerState::Filter::kBilerp);
auto uColFP = GrColorMatrixFragmentProcessor::Make(std::move(uTexFP), uM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
uPaint.addColorFragmentProcessor(std::move(uColFP));
uPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
uRTC->fillRectToRect(nullptr, std::move(uPaint), GrAA::kNo, SkMatrix::I(), dstRectUV,
dstRectUV);
if (!doSynchronousRead) {
uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(uRTC->width(), uRTC->height()));
if (!uTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
// This matrix generates (r,g,b,a) = (0, 0, 0, v)
float vM[20];
std::fill_n(vM, 15, 0.f);
std::copy_n(baseM + 10, 5, vM + 15);
GrPaint vPaint;
auto vTexFP = GrTextureEffect::Make(std::move(srcView), this->colorInfo().alphaType(),
texMatrix, GrSamplerState::Filter::kBilerp);
auto vColFP = GrColorMatrixFragmentProcessor::Make(std::move(vTexFP), vM,
/*unpremulInput=*/false,
/*clampRGBOutput=*/true,
/*premulOutput=*/false);
vPaint.addColorFragmentProcessor(std::move(vColFP));
vPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
vRTC->fillRectToRect(nullptr, std::move(vPaint), GrAA::kNo, SkMatrix::I(), dstRectUV,
dstRectUV);
if (!doSynchronousRead) {
vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(vRTC->width(), vRTC->height()));
if (!vTransfer.fTransferBuffer) {
callback(context, nullptr);
return;
}
}
if (doSynchronousRead) {
GrImageInfo yInfo(GrColorType::kAlpha_8, kPremul_SkAlphaType, nullptr, dstSize);
GrImageInfo uvInfo = yInfo.makeWH(halfW, halfH);
size_t yRB = yInfo.minRowBytes();
size_t uvRB = uvInfo.minRowBytes();
std::unique_ptr<char[]> y(new char[yRB * yInfo.height()]);
std::unique_ptr<char[]> u(new char[uvRB*uvInfo.height()]);
std::unique_ptr<char[]> v(new char[uvRB*uvInfo.height()]);
if (!yRTC->readPixels(yInfo, y.get(), yRB, {0, 0}, direct) ||
!uRTC->readPixels(uvInfo, u.get(), uvRB, {0, 0}, direct) ||
!vRTC->readPixels(uvInfo, v.get(), uvRB, {0, 0}, direct)) {
callback(context, nullptr);
return;
}
auto result = std::make_unique<AsyncReadResult>(direct->priv().contextID());
result->addCpuPlane(std::move(y), yRB );
result->addCpuPlane(std::move(u), uvRB);
result->addCpuPlane(std::move(v), uvRB);
callback(context, std::move(result));
return;
}
struct FinishContext {
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
GrClientMappedBufferManager* fMappedBufferManager;
SkISize fSize;
PixelTransferResult fYTransfer;
PixelTransferResult fUTransfer;
PixelTransferResult fVTransfer;
};
// Assumption is that the caller would like to flush. We could take a parameter or require an
// explicit flush from the caller. We'd have to have a way to defer attaching the finish
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback,
context,
direct->priv().clientMappedBufferManager(),
dstSize,
std::move(yTransfer),
std::move(uTransfer),
std::move(vTransfer)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
auto manager = context->fMappedBufferManager;
size_t rowBytes = SkToSizeT(context->fSize.width());
if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
rowBytes /= 2;
SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2};
if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;
flushInfo.fFinishedContext = finishContext;
flushInfo.fFinishedProc = finishCallback;
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr);
}
bool GrSurfaceContext::copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) {
ASSERT_SINGLE_OWNER
RETURN_FALSE_IF_ABANDONED
@ -499,12 +1006,11 @@ bool GrSurfaceContext::copy(GrSurfaceProxy* src, const SkIRect& srcRect, const S
this->readSurfaceView(), dstPoint);
}
std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(
const GrImageInfo& info,
GrSurfaceOrigin origin,
SkIRect srcRect,
SkSurface::RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality) {
std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(const GrImageInfo& info,
GrSurfaceOrigin origin,
SkIRect srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality) {
auto rtProxy = this->asRenderTargetProxy();
if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
return nullptr;
@ -538,7 +1044,7 @@ std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(
// Assume we should ignore the rescale linear request if the surface has no color space since
// it's unclear how we'd linearize from an unknown color space.
if (rescaleGamma == SkSurface::kLinear && this->colorInfo().colorSpace() &&
if (rescaleGamma == RescaleGamma::kLinear && this->colorInfo().colorSpace() &&
!this->colorInfo().colorSpace()->gammaIsLinear()) {
auto cs = this->colorInfo().colorSpace()->makeLinearGamma();
auto xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(), srcAlphaType, cs.get(),
@ -640,6 +1146,25 @@ std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(
return tempA;
}
GrSemaphoresSubmitted GrSurfaceContext::flush(SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
ASSERT_SINGLE_OWNER
if (fContext->priv().abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}
SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "flush", fContext);
return this->drawingManager()->flushSurface(this->asSurfaceProxy(), access, info, newState);
}
GrSurfaceContext::PixelTransferResult GrSurfaceContext::transferPixels(GrColorType dstCT,
const SkIRect& rect) {
SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());

View File

@ -9,9 +9,11 @@
#define GrSurfaceContext_DEFINED
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSurface.h"
#include "src/gpu/GrClientMappedBufferManager.h"
#include "src/gpu/GrColorInfo.h"
#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrImageInfo.h"
@ -83,6 +85,28 @@ public:
bool readPixels(const GrImageInfo& dstInfo, void* dst, size_t rowBytes, SkIPoint srcPt,
GrContext* direct = nullptr);
using ReadPixelsCallback = SkImage::ReadPixelsCallback;
using ReadPixelsContext = SkImage::ReadPixelsContext;
using RescaleGamma = SkImage::RescaleGamma;
// GPU implementation for SkImage:: and SkSurface::asyncRescaleAndReadPixels.
void asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
// GPU implementation for SkImage:: and SkSurface::asyncRescaleAndReadPixelsYUV420.
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
SkISize dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
/**
* Writes a rectangle of pixels [srcInfo, srcBuffer, srcRowbytes] into the
* renderTargetContext at the specified position.
@ -124,9 +148,17 @@ public:
std::unique_ptr<GrRenderTargetContext> rescale(const GrImageInfo& info,
GrSurfaceOrigin,
SkIRect srcRect,
SkSurface::RescaleGamma,
SkImage::RescaleGamma,
SkFilterQuality);
/**
* After this returns any pending surface IO will be issued to the backend 3D API and
* if the surface has MSAA it will be resolved.
*/
GrSemaphoresSubmitted flush(SkSurface::BackendSurfaceAccess access,
const GrFlushInfo&,
const GrBackendSurfaceMutableState*);
GrAuditTrail* auditTrail();
// Provides access to functions that aren't part of the public API.
@ -171,6 +203,12 @@ protected:
};
PixelTransferResult transferPixels(GrColorType colorType, const SkIRect& rect);
// The async read step of asyncRescaleAndReadPixels()
void asyncReadPixels(const SkIRect& rect,
SkColorType colorType,
ReadPixelsCallback callback,
ReadPixelsContext context);
private:
friend class GrSurfaceProxy; // for copy
@ -192,6 +230,8 @@ private:
*/
bool copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint);
class AsyncReadResult;
GrColorInfo fColorInfo;
typedef SkRefCnt INHERITED;

View File

@ -24,6 +24,7 @@
#include "src/core/SkSpecialImage.h"
#include "src/image/SkImage_Base.h"
#include "src/image/SkReadPixelsRec.h"
#include "src/image/SkRescaleAndReadPixels.h"
#include "src/shaders/SkImageShader.h"
#if SK_SUPPORT_GPU
@ -52,6 +53,44 @@ bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dst
return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
}
void SkImage::asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
!SkImageInfoIsValid(info)) {
callback(context, nullptr);
return;
}
as_IB(this)->onAsyncRescaleAndReadPixels(
info, srcRect, rescaleGamma, rescaleQuality, callback, context);
}
void SkImage::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
(dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
callback(context, nullptr);
return;
}
as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
std::move(dstColorSpace),
srcRect,
dstSize,
rescaleGamma,
rescaleQuality,
callback,
context);
}
bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
if (this->width() == dst.width() && this->height() == dst.height()) {
return this->readPixels(dst, 0, 0, chint);
@ -207,6 +246,44 @@ SkImage_Base::~SkImage_Base() {
}
}
void SkImage_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& origSrcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkBitmap src;
SkPixmap peek;
SkIRect srcRect;
if (this->peekPixels(&peek)) {
src.installPixels(peek);
srcRect = origSrcRect;
} else {
src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
src.allocPixels();
if (!this->readPixels(src.pixmap(), origSrcRect.x(), origSrcRect.y())) {
callback(context, nullptr);
return;
}
srcRect = SkIRect::MakeSize(src.dimensions());
}
return SkRescaleAndReadPixels(
src, info, srcRect, rescaleGamma, rescaleQuality, callback, context);
}
void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
// TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
// call client's callback.
callback(context, nullptr);
}
GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
GrSurfaceOrigin* origin) const {
return GrBackendTexture(); // invalid

View File

@ -46,6 +46,27 @@ public:
virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
int srcX, int srcY, CachingHint) const = 0;
/**
* Default implementation does a rescale/read and then calls the callback.
*/
virtual void onAsyncRescaleAndReadPixels(const SkImageInfo&,
const SkIRect& srcRect,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext);
/**
* Default implementation does a rescale/read/yuv conversion and then calls the callback.
*/
virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext);
virtual GrContext* context() const { return nullptr; }
#if SK_SUPPORT_GPU

View File

@ -123,6 +123,47 @@ sk_sp<SkImage> SkImage_Gpu::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) c
this->alphaType(), std::move(newCS));
}
void SkImage_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
GrColorType ct = SkColorTypeToGrColorType(this->colorType());
auto ctx = GrSurfaceContext::Make(fContext.get(), fView, ct, this->alphaType(),
this->refColorSpace());
if (!ctx) {
callback(context, nullptr);
return;
}
ctx->asyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback, context);
}
void SkImage_Gpu::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
GrColorType ct = SkColorTypeToGrColorType(this->colorType());
auto ctx = GrSurfaceContext::Make(fContext.get(), fView, ct, this->alphaType(),
this->refColorSpace());
if (!ctx) {
callback(context, nullptr);
return;
}
ctx->asyncRescaleAndReadPixelsYUV420(yuvColorSpace,
std::move(dstColorSpace),
srcRect,
dstSize,
rescaleGamma,
rescaleQuality,
callback,
context);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx,

View File

@ -50,6 +50,22 @@ public:
sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const final;
void onAsyncRescaleAndReadPixels(const SkImageInfo&,
const SkIRect& srcRect,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext) override;
void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
sk_sp<SkColorSpace>,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext) override;
/**
* This is the implementation of SkDeferredDisplayListRecorder::makePromiseImage.
*/

View File

@ -0,0 +1,139 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSurface.h"
#include <cmath>
void SkRescaleAndReadPixels(SkBitmap bmp,
const SkImageInfo& resultInfo,
const SkIRect& srcRect,
SkImage::RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
SkImage::ReadPixelsCallback callback,
SkImage::ReadPixelsContext context) {
int srcW = srcRect.width();
int srcH = srcRect.height();
float sx = (float)resultInfo.width() / srcW;
float sy = (float)resultInfo.height() / srcH;
// How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling.
int stepsX;
int stepsY;
if (rescaleQuality > kNone_SkFilterQuality) {
stepsX = static_cast<int>((sx > 1.f) ? std::ceil(std::log2f(sx))
: std::floor(std::log2f(sx)));
stepsY = static_cast<int>((sy > 1.f) ? std::ceil(std::log2f(sy))
: std::floor(std::log2f(sy)));
} else {
stepsX = sx != 1.f;
stepsY = sy != 1.f;
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
if (stepsX < 0 || stepsY < 0) {
// Don't trigger MIP generation. We don't currently have a way to trigger bicubic for
// downscaling draws.
rescaleQuality = std::min(rescaleQuality, kLow_SkFilterQuality);
}
paint.setFilterQuality(rescaleQuality);
sk_sp<SkSurface> tempSurf;
sk_sp<SkImage> srcImage;
int srcX = srcRect.fLeft;
int srcY = srcRect.fTop;
SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
// Assume we should ignore the rescale linear request if the surface has no color space since
// it's unclear how we'd linearize from an unknown color space.
if (rescaleGamma == SkSurface::RescaleGamma::kLinear && bmp.info().colorSpace() &&
!bmp.info().colorSpace()->gammaIsLinear()) {
auto cs = bmp.info().colorSpace()->makeLinearGamma();
// Promote to F16 color type to preserve precision.
auto ii = SkImageInfo::Make(srcW, srcH, kRGBA_F16_SkColorType, bmp.info().alphaType(),
std::move(cs));
auto linearSurf = SkSurface::MakeRaster(ii);
if (!linearSurf) {
callback(context, nullptr);
return;
}
linearSurf->getCanvas()->drawBitmap(bmp, -srcX, -srcY, &paint);
tempSurf = std::move(linearSurf);
srcImage = tempSurf->makeImageSnapshot();
srcX = 0;
srcY = 0;
constraint = SkCanvas::kFast_SrcRectConstraint;
} else {
// MakeFromBitmap would trigger a copy if bmp is mutable.
srcImage = SkImage::MakeFromRaster(bmp.pixmap(), nullptr, nullptr);
}
while (stepsX || stepsY) {
int nextW = resultInfo.width();
int nextH = resultInfo.height();
if (stepsX < 0) {
nextW = resultInfo.width() << (-stepsX - 1);
stepsX++;
} else if (stepsX != 0) {
if (stepsX > 1) {
nextW = srcW * 2;
}
--stepsX;
}
if (stepsY < 0) {
nextH = resultInfo.height() << (-stepsY - 1);
stepsY++;
} else if (stepsY != 0) {
if (stepsY > 1) {
nextH = srcH * 2;
}
--stepsY;
}
auto ii = srcImage->imageInfo().makeWH(nextW, nextH);
if (!stepsX && !stepsY) {
// Might as well fold conversion to final info in the last step.
ii = resultInfo;
}
auto next = SkSurface::MakeRaster(ii);
if (!next) {
callback(context, nullptr);
return;
}
next->getCanvas()->drawImageRect(
std::move(srcImage), SkIRect::MakeXYWH(srcX, srcY, srcW, srcH),
SkRect::MakeWH((float)nextW, (float)nextH), &paint, constraint);
tempSurf = std::move(next);
srcImage = tempSurf->makeImageSnapshot();
srcX = srcY = 0;
srcW = nextW;
srcH = nextH;
constraint = SkCanvas::kFast_SrcRectConstraint;
}
size_t rowBytes = resultInfo.minRowBytes();
std::unique_ptr<char[]> data(new char[resultInfo.height() * rowBytes]);
SkPixmap pm(resultInfo, data.get(), rowBytes);
if (srcImage->readPixels(pm, srcX, srcY)) {
class Result : public SkImage::AsyncReadResult {
public:
Result(std::unique_ptr<const char[]> data, size_t rowBytes)
: fData(std::move(data)), fRowBytes(rowBytes) {}
int count() const override { return 1; }
const void* data(int i) const override { return fData.get(); }
size_t rowBytes(int i) const override { return fRowBytes; }
private:
std::unique_ptr<const char[]> fData;
size_t fRowBytes;
};
callback(context, std::make_unique<Result>(std::move(data), rowBytes));
} else {
callback(context, nullptr);
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkBitmap.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h"
struct SkImageInfo;
struct SkIRect;
/** Generic/synchronous implementation for SkImage:: and SkSurface::asyncRescaleAndReadPixels. */
void SkRescaleAndReadPixels(SkBitmap src,
const SkImageInfo& resultInfo,
const SkIRect& srcRect,
SkImage::RescaleGamma,
SkFilterQuality,
SkImage::ReadPixelsCallback,
SkImage::ReadPixelsContext);

View File

@ -12,6 +12,7 @@
#include "include/gpu/GrBackendSurface.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkImagePriv.h"
#include "src/image/SkRescaleAndReadPixels.h"
#include "src/image/SkSurface_Base.h"
static SkPixelGeometry compute_default_geometry() {
@ -97,126 +98,29 @@ void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPa
}
}
void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& origSrcRect,
SkSurface::RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
SkSurface::ReadPixelsCallback callback,
SkSurface::ReadPixelsContext context) {
int srcW = srcRect.width();
int srcH = srcRect.height();
float sx = (float)info.width() / srcW;
float sy = (float)info.height() / srcH;
// How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling.
int stepsX;
int stepsY;
if (rescaleQuality > kNone_SkFilterQuality) {
stepsX = static_cast<int>((sx > 1.f) ? std::ceil(std::log2f(sx))
: std::floor(std::log2f(sx)));
stepsY = static_cast<int>((sy > 1.f) ? std::ceil(std::log2f(sy))
: std::floor(std::log2f(sy)));
SkBitmap src;
SkPixmap peek;
SkIRect srcRect;
if (this->peekPixels(&peek)) {
src.installPixels(peek);
srcRect = origSrcRect;
} else {
stepsX = sx != 1.f;
stepsY = sy != 1.f;
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
if (stepsX < 0 || stepsY < 0) {
// Don't trigger MIP generation. We don't currently have a way to trigger bicubic for
// downscaling draws.
rescaleQuality = std::min(rescaleQuality, kLow_SkFilterQuality);
}
paint.setFilterQuality(rescaleQuality);
sk_sp<SkSurface> src(SkRef(this));
int srcX = srcRect.fLeft;
int srcY = srcRect.fTop;
SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
// Assume we should ignore the rescale linear request if the surface has no color space since
// it's unclear how we'd linearize from an unknown color space.
if (rescaleGamma == SkSurface::RescaleGamma::kLinear &&
this->getCanvas()->imageInfo().colorSpace() &&
!this->getCanvas()->imageInfo().colorSpace()->gammaIsLinear()) {
auto cs = this->getCanvas()->imageInfo().colorSpace()->makeLinearGamma();
// Promote to F16 color type to preserve precision.
auto ii = SkImageInfo::Make(srcW, srcH, kRGBA_F16_SkColorType,
this->getCanvas()->imageInfo().alphaType(), std::move(cs));
auto linearSurf = this->makeSurface(ii);
if (!linearSurf) {
// Maybe F16 isn't supported? Try again with original color type.
ii = ii.makeColorType(this->getCanvas()->imageInfo().colorType());
linearSurf = this->makeSurface(ii);
if (!linearSurf) {
callback(context, nullptr);
return;
}
}
this->draw(linearSurf->getCanvas(), -srcX, -srcY, &paint);
src = std::move(linearSurf);
srcX = 0;
srcY = 0;
constraint = SkCanvas::kFast_SrcRectConstraint;
}
while (stepsX || stepsY) {
int nextW = info.width();
int nextH = info.height();
if (stepsX < 0) {
nextW = info.width() << (-stepsX - 1);
stepsX++;
} else if (stepsX != 0) {
if (stepsX > 1) {
nextW = srcW * 2;
}
--stepsX;
}
if (stepsY < 0) {
nextH = info.height() << (-stepsY - 1);
stepsY++;
} else if (stepsY != 0) {
if (stepsY > 1) {
nextH = srcH * 2;
}
--stepsY;
}
auto ii = src->getCanvas()->imageInfo().makeWH(nextW, nextH);
if (!stepsX && !stepsY) {
// Might as well fold conversion to final info in the last step.
ii = info;
}
auto next = this->makeSurface(ii);
if (!next) {
src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
src.allocPixels();
if (!this->readPixels(src, origSrcRect.x(), origSrcRect.y())) {
callback(context, nullptr);
return;
}
next->getCanvas()->drawImageRect(
src->makeImageSnapshot(), SkIRect::MakeXYWH(srcX, srcY, srcW, srcH),
SkRect::MakeWH((float)nextW, (float)nextH), &paint, constraint);
src = std::move(next);
srcX = srcY = 0;
srcW = nextW;
srcH = nextH;
constraint = SkCanvas::kFast_SrcRectConstraint;
}
size_t rowBytes = info.minRowBytes();
std::unique_ptr<char[]> data(new char[info.height() * rowBytes]);
SkPixmap pm(info, data.get(), rowBytes);
if (src->readPixels(pm, srcX, srcY)) {
class Result : public AsyncReadResult {
public:
Result(std::unique_ptr<const char[]> data, size_t rowBytes)
: fData(std::move(data)), fRowBytes(rowBytes) {}
int count() const override { return 1; }
const void* data(int i) const override { return fData.get(); }
size_t rowBytes(int i) const override { return fRowBytes; }
private:
std::unique_ptr<const char[]> fData;
size_t fRowBytes;
};
callback(context, std::make_unique<Result>(std::move(data), rowBytes));
} else {
callback(context, nullptr);
srcRect = SkIRect::MakeSize(src.dimensions());
}
return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleQuality, callback,
context);
}
void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(

View File

@ -5,8 +5,8 @@
* found in the LICENSE file.
*/
#include <initializer_list>
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GrContext.h"
@ -23,9 +23,12 @@
#include "tests/Test.h"
#include "tests/TestUtils.h"
#include "tools/ToolUtils.h"
#include "tools/gpu/BackendTextureImageFactory.h"
#include "tools/gpu/GrContextFactory.h"
#include "tools/gpu/ProxyUtils.h"
#include <initializer_list>
static const int DEV_W = 100, DEV_H = 100;
static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
@ -647,8 +650,15 @@ struct GpuReadPixelTestRules {
// the pixmap.
template <typename T> using GpuSrcFactory = T(SkPixmap&);
enum class GpuReadResult {
kFail,
kSuccess,
kExcusedFailure,
};
// Does a read from the T into the pixmap.
template <typename T> using GpuReadSrcFn = bool(const T&, const SkIVector& offset, const SkPixmap&);
template <typename T>
using GpuReadSrcFn = GpuReadResult(const T&, const SkIVector& offset, const SkPixmap&);
} // anonymous namespace
@ -685,19 +695,19 @@ static void gpu_read_pixels_test_driver(skiatest::Reporter* reporter,
dstPixels.computeByteSize(),
kInitialByte);
const bool success = read(src, offset, dstPixels);
const GpuReadResult result = read(src, offset, dstPixels);
if (!SkIRect::Intersects(rect, surfBounds)) {
REPORTER_ASSERT(reporter, !success);
REPORTER_ASSERT(reporter, result != GpuReadResult::kSuccess);
} else if (readCT == kUnknown_SkColorType) {
REPORTER_ASSERT(reporter, !success);
REPORTER_ASSERT(reporter, result != GpuReadResult::kSuccess);
} else if (readAT == kUnknown_SkAlphaType) {
REPORTER_ASSERT(reporter, !success);
REPORTER_ASSERT(reporter, result != GpuReadResult::kSuccess);
} else if (!rules.fUncontainedRectSucceeds && !surfBounds.contains(rect)) {
REPORTER_ASSERT(reporter, !success);
REPORTER_ASSERT(reporter, result != GpuReadResult::kSuccess);
} else if (!rules.fAllowUnpremulRead && readAT == kUnpremul_SkAlphaType) {
REPORTER_ASSERT(reporter, !success);
} else if (!success) {
REPORTER_ASSERT(reporter, result != GpuReadResult::kSuccess);
} else if (result == GpuReadResult::kFail) {
// TODO: Support RGB/BGR 101010x, BGRA 1010102 on the GPU.
if (SkColorTypeToGrColorType(readCT) != GrColorType::kUnknown) {
ERRORF(reporter,
@ -716,7 +726,7 @@ static void gpu_read_pixels_test_driver(skiatest::Reporter* reporter,
// Considering the rect we tried to read and the surface bounds figure out which pixels in
// both src and dst space should actually have been read and written.
SkIRect srcReadRect;
if (success && srcReadRect.intersect(surfBounds, rect)) {
if (result == GpuReadResult::kSuccess && srcReadRect.intersect(surfBounds, rect)) {
SkIRect dstWriteRect = srcReadRect.makeOffset(-rect.fLeft, -rect.fTop);
const bool lumConversion =
@ -724,18 +734,19 @@ static void gpu_read_pixels_test_driver(skiatest::Reporter* reporter,
(SkColorTypeChannelFlags(readCT) & kGray_SkColorChannelFlag);
// A CS or luminance conversion allows a 3 value difference and otherwise a 2 value
// difference. Note that sometimes read back on GPU can be lossy even when there no
// conversion at allbecause GPU->CPU read may go to a lower bit depth format and then be
// promoted back to the original type. For example, GL ES cannot read to 1010102, so we
// go through 8888.
// conversion at all because GPU->CPU read may go to a lower bit depth format and then
// be promoted back to the original type. For example, GL ES cannot read to 1010102, so
// we go through 8888.
const float numer = (lumConversion || csConversion) ? 3.f : 2.f;
int rgbBits = std::min({min_rgb_channel_bits(readCT),
min_rgb_channel_bits(srcCT),
8});
int rgbBits = std::min(
{min_rgb_channel_bits(readCT), min_rgb_channel_bits(srcCT), 8});
float tol = numer / (1 << rgbBits);
float alphaTol = 0;
if (readAT != kOpaque_SkAlphaType && srcAT != kOpaque_SkAlphaType) {
const int alphaBits = std::min(alpha_channel_bits(readCT),
alpha_channel_bits(srcCT));
// Alpha can also get squashed down to 8 bits going through an intermediate
// color format.
const int alphaBits =
std::min({alpha_channel_bits(readCT), alpha_channel_bits(srcCT), 8});
alphaTol = 2.f / (1 << alphaBits);
}
@ -924,20 +935,20 @@ static void gpu_read_pixels_test_driver(skiatest::Reporter* reporter,
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;
};
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceAsyncReadPixels, reporter, ctxInfo) {
using Surface = sk_sp<SkSurface>;
auto reader = std::function<GpuReadSrcFn<Surface>>([](const Surface& surface,
const SkIVector& offset,
@ -946,18 +957,18 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset);
// Rescale quality and linearity don't matter since we're doing a non-scaling readback.
surface->asyncRescaleAndReadPixels(pixels.info(), rect, SkSurface::RescaleGamma::kSrc,
surface->asyncRescaleAndReadPixels(pixels.info(), rect, SkImage::RescaleGamma::kSrc,
kNone_SkFilterQuality, async_callback, &context);
surface->getContext()->submit();
while (!context.fCalled) {
surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
}
if (!context.fResult) {
return false;
return GpuReadResult::kFail;
}
SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), context.fResult->data(0),
context.fResult->rowBytes(0), pixels.info().minRowBytes(), pixels.height());
return true;
return GpuReadResult::kSuccess;
});
GpuReadPixelTestRules rules;
rules.fAllowUnpremulSrc = false;
@ -981,11 +992,65 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
}
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageAsyncReadPixels, reporter, ctxInfo) {
using Image = sk_sp<SkImage>;
GrContext* context = ctxInfo.grContext();
auto reader = std::function<GpuReadSrcFn<Image>>([context](const Image& image,
const SkIVector& offset,
const SkPixmap& pixels) {
AsyncContext asyncContext;
auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset);
// The GPU implementation is based on rendering and will fail for non-renderable color
// types.
auto ct = SkColorTypeToGrColorType(image->colorType());
auto format = context->priv().caps()->getDefaultBackendFormat(ct, GrRenderable::kYes);
if (!context->priv().caps()->isFormatAsColorTypeRenderable(ct, format)) {
return GpuReadResult::kExcusedFailure;
}
// Rescale quality and linearity don't matter since we're doing a non-scaling readback.
image->asyncRescaleAndReadPixels(pixels.info(), rect, SkImage::RescaleGamma::kSrc,
kNone_SkFilterQuality, async_callback, &asyncContext);
context->submit();
while (!asyncContext.fCalled) {
context->checkAsyncWorkCompletion();
}
if (!asyncContext.fResult) {
return GpuReadResult::kFail;
}
SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), asyncContext.fResult->data(0),
asyncContext.fResult->rowBytes(0), pixels.info().minRowBytes(),
pixels.height());
return GpuReadResult::kSuccess;
});
GpuReadPixelTestRules rules;
rules.fAllowUnpremulSrc = true;
// GPU doesn't support reading to kUnpremul because the rescaling works by rendering and now
// we only support premul rendering.
rules.fAllowUnpremulRead = false;
rules.fUncontainedRectSucceeds = false;
for (auto origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
auto factory = std::function<GpuSrcFactory<Image>>([&](const SkPixmap& src) {
if (src.colorType() == kRGB_888x_SkColorType) {
return Image();
}
return sk_gpu_test::MakeBackendTextureImage(ctxInfo.grContext(), src, renderable,
origin);
});
gpu_read_pixels_test_driver(reporter, rules, factory, reader);
}
}
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Gpu, reporter, ctxInfo) {
using Surface = sk_sp<SkSurface>;
auto reader = std::function<GpuReadSrcFn<Surface>>(
[](const Surface& surface, const SkIVector& offset, const SkPixmap& pixels) {
return surface->readPixels(pixels, offset.fX, offset.fY);
return surface->readPixels(pixels, offset.fX, offset.fY) ? GpuReadResult::kSuccess
: GpuReadResult::kFail;
});
GpuReadPixelTestRules rules;
rules.fAllowUnpremulSrc = false;
@ -1062,10 +1127,10 @@ DEF_GPUTEST(AsyncReadPixelsContextShutdown, reporter, options) {
if (yuv) {
surf->asyncRescaleAndReadPixelsYUV420(
kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
ii.dimensions(), SkSurface::RescaleGamma::kSrc, kNone_SkFilterQuality,
ii.dimensions(), SkImage::RescaleGamma::kSrc, kNone_SkFilterQuality,
&async_callback, &cbContext);
} else {
surf->asyncRescaleAndReadPixels(ii, ii.bounds(), SkSurface::RescaleGamma::kSrc,
surf->asyncRescaleAndReadPixels(ii, ii.bounds(), SkImage::RescaleGamma::kSrc,
kNone_SkFilterQuality, &async_callback,
&cbContext);
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tools/gpu/BackendTextureImageFactory.h"
#include "include/core/SkImage.h"
#include "include/core/SkPixmap.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContext.h"
#include "src/core/SkAutoPixmapStorage.h"
namespace {
class ManagedBackendTexture : public SkNVRefCnt<ManagedBackendTexture> {
public:
~ManagedBackendTexture() {
if (fContext && fTexture.isValid()) {
fContext->submit(true);
fContext->deleteBackendTexture(fTexture);
}
}
static void Proc(void* context) { static_cast<ManagedBackendTexture*>(context)->unref(); }
template <typename... Args>
static sk_sp<ManagedBackendTexture> Make(GrContext* context, Args&&... args) {
sk_sp<ManagedBackendTexture> mbet(new ManagedBackendTexture);
mbet->fContext = context;
mbet->fTexture = context->createBackendTexture(std::forward<Args>(args)...,
Proc,
mbet->refAndPassAsContext());
return mbet;
}
const GrBackendTexture& texture() { return fTexture; }
void* refAndPassAsContext() {
this->ref();
return static_cast<void*>(this);
}
private:
ManagedBackendTexture() = default;
GrContext* fContext = nullptr;
GrBackendTexture fTexture;
};
} // namespace
namespace sk_gpu_test {
sk_sp<SkImage> MakeBackendTextureImage(GrContext* context,
const SkPixmap& pixmap,
GrRenderable renderable,
GrSurfaceOrigin origin) {
const SkPixmap* src = &pixmap;
SkAutoPixmapStorage temp;
if (origin == kBottomLeft_GrSurfaceOrigin) {
temp.alloc(src->info());
auto s = static_cast<const char*>(src->addr(0, pixmap.height() - 1));
auto d = static_cast<char*>(temp.writable_addr(0, 0));
for (int y = 0; y < temp.height(); ++y, s -= pixmap.rowBytes(), d += temp.rowBytes()) {
std::copy_n(s, temp.info().minRowBytes(), d);
}
src = &temp;
}
auto mbet = ManagedBackendTexture::Make(context, src, 1, renderable, GrProtected::kNo);
auto image = SkImage::MakeFromTexture(context, mbet->texture(), origin, src->colorType(),
src->alphaType(), src->refColorSpace(),
ManagedBackendTexture::Proc, mbet->refAndPassAsContext());
// We currently have an issue where depending on how MakeFromTexture fails it may not
// call the release proc (to be fixed soon, crbug.com/1097484). For now we use this hack
// to see if it failed without calling Proc.
if (!image) {
context->submit(true);
if (!mbet->unique()) {
ManagedBackendTexture::Proc(mbet.get());
}
}
return image;
}
} // namespace sk_gpu_test

View File

@ -0,0 +1,24 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkRefCnt.h"
#include "include/gpu/GrTypes.h"
class GrContext;
class SkImage;
class SkPixmap;
namespace sk_gpu_test {
/**
* Creates a backend texture with pixmap contents and wraps it in a SkImage that safely deletes
* the texture when it goes away. Unlike using makeTextureImage() on a non-GPU image, this will
* fail rather than fallback if the pixmaps's color type doesn't map to a supported texture format.
* For testing purposes the texture can be made renderable to exercise different code paths for
* renderable textures/formats.
*/
sk_sp<SkImage> MakeBackendTextureImage(GrContext*, const SkPixmap&, GrRenderable, GrSurfaceOrigin);
} // namespace sk_gpu_test