Reland DeferredTextureImageData low-bit-depth/dithering support

Cause DeferredTextureImageData functionality to support low bit depth
(4444, 565) image formats (with dithering).

Updated to handle colorspace + 4444 colortype correctly.

Bug: 720105
Change-Id: Ib7e14d937849f4f6b08fda6992a240bb203d0089
Reviewed-on: https://skia-review.googlesource.com/19094
Commit-Queue: Eric Karl <ericrk@chromium.org>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Eric Karl 2017-06-12 10:05:49 -07:00 committed by Skia Commit-Bot
parent a8d45e5220
commit 7a8c84c6c9
7 changed files with 125 additions and 44 deletions

View File

@ -17,7 +17,9 @@
// Helper function that uploads the given SkImage using MakeFromDeferredTextureImageData and then
// draws the uploaded version at the specified coordinates.
static void DrawDeferredTextureImageData(SkCanvas* canvas,
SkImage::DeferredTextureImageUsageParams* params) {
const char* resourceName,
SkImage::DeferredTextureImageUsageParams* params,
SkColorType dstColorType) {
GrContext* context = canvas->getGrContext();
if (!context) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
@ -27,14 +29,14 @@ static void DrawDeferredTextureImageData(SkCanvas* canvas,
sk_sp<SkImage> encodedImage = GetResourceAsImage("mandrill_512.png");
sk_sp<SkImage> encodedImage = GetResourceAsImage(resourceName);
if (!encodedImage) {
SkDebugf("\nCould not load resource.\n");
return;
}
size_t requiredMemoryInBytes = encodedImage->getDeferredTextureImageData(
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
@ -43,7 +45,7 @@ static void DrawDeferredTextureImageData(SkCanvas* canvas,
std::vector<uint8_t> memory;
memory.resize(requiredMemoryInBytes);
encodedImage->getDeferredTextureImageData(
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedEncodedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
@ -52,14 +54,14 @@ static void DrawDeferredTextureImageData(SkCanvas* canvas,
SkBitmap bitmap;
if (!GetResourceAsBitmap("mandrill_512.png", &bitmap)) {
if (!GetResourceAsBitmap(resourceName, &bitmap)) {
SkDebugf("\nCould not decode resource.\n");
return;
}
sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
requiredMemoryInBytes = decodedImage->getDeferredTextureImageData(
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
@ -67,15 +69,16 @@ static void DrawDeferredTextureImageData(SkCanvas* canvas,
memory.resize(requiredMemoryInBytes);
decodedImage->getDeferredTextureImageData(
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedDecodedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
canvas->drawImage(uploadedDecodedImage, 512 + 20, 10);
canvas->drawImage(uploadedDecodedImage, encodedImage->width() + 20, 10);
}
static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
SkImage::DeferredTextureImageUsageParams* params) {
SkImage::DeferredTextureImageUsageParams* params,
SkColorType dstColorType) {
GrContext* context = canvas->getGrContext();
if (!context) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
@ -88,7 +91,7 @@ static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
int mipLevelCount = SkMipMap::ComputeLevelCount(image->width(), image->height());
size_t requiredMemoryInBytes = image->getDeferredTextureImageData(
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
*proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
@ -97,7 +100,7 @@ static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
std::vector<uint8_t> memory;
memory.resize(requiredMemoryInBytes);
image->getDeferredTextureImageData(
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
@ -145,13 +148,19 @@ static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
DEF_SIMPLE_GM(deferred_texture_image_none, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kNone_SkFilterQuality, 0);
DrawDeferredTextureImageData(canvas, &params);
DrawDeferredTextureImageData(canvas, "mandrill_512.png", &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_low, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kLow_SkFilterQuality, 0);
DrawDeferredTextureImageData(canvas, &params);
DrawDeferredTextureImageData(canvas, "mandrill_512.png", &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_low_dithered, canvas, 180 + 180 + 30, 180 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kLow_SkFilterQuality, 0);
DrawDeferredTextureImageData(canvas, "dog.jpg", &params, kARGB_4444_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_encoded, canvas, 512 + 512 + 30, 1110) {
@ -163,7 +172,7 @@ DEF_SIMPLE_GM(deferred_texture_image_medium_encoded, canvas, 512 + 512 + 30, 111
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), &params);
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded, canvas, 512 + 512 + 30, 1110) {
@ -176,13 +185,13 @@ DEF_SIMPLE_GM(deferred_texture_image_medium_decoded, canvas, 512 + 512 + 30, 111
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), &params);
DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_high, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kHigh_SkFilterQuality, 0);
DrawDeferredTextureImageData(canvas, &params);
DrawDeferredTextureImageData(canvas, "mandrill_512.png", &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_encoded_indexed, canvas, 128 + 128 + 30, 340) {
@ -194,7 +203,7 @@ DEF_SIMPLE_GM(deferred_texture_image_medium_encoded_indexed, canvas, 128 + 128 +
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), &params);
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), &params, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded_indexed, canvas, 128 + 128 + 30, 340) {
@ -207,7 +216,7 @@ DEF_SIMPLE_GM(deferred_texture_image_medium_decoded_indexed, canvas, 128 + 128 +
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), &params);
DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), &params, kN32_SkColorType);
}
#endif

View File

@ -477,12 +477,18 @@ public:
* dstColorSpace is the color space of the surface where this texture will ultimately be used.
* If the method determines that mip-maps are needed, this helps determine the correct strategy
* for building them (gamma-correct or not).
*
* dstColorType is the color type of the surface where this texture will ultimately be used.
* This determines the format with which the image will be uploaded to the GPU. If dstColorType
* does not support color spaces (low bit depth types such as ARGB_4444), then dstColorSpace
* must be null.
*/
size_t getDeferredTextureImageData(const GrContextThreadSafeProxy&,
const DeferredTextureImageUsageParams[],
int paramCnt,
void* buffer,
SkColorSpace* dstColorSpace = nullptr) const;
SkColorSpace* dstColorSpace = nullptr,
SkColorType dstColorType = kN32_SkColorType) const;
/**
* Returns a texture-backed image from data produced in SkImage::getDeferredTextureImageData.

View File

@ -14,6 +14,16 @@ SkAutoPixmapStorage::~SkAutoPixmapStorage() {
this->freeStorage();
}
SkAutoPixmapStorage& SkAutoPixmapStorage::operator=(SkAutoPixmapStorage&& other) {
this->fStorage = other.fStorage;
this->INHERITED::reset(other.info(), this->fStorage, other.rowBytes(), other.ctable());
other.fStorage = nullptr;
other.INHERITED::reset();
return *this;
}
size_t SkAutoPixmapStorage::AllocSize(const SkImageInfo& info, size_t* rowBytes) {
size_t rb = info.minRowBytes();
if (rowBytes) {

View File

@ -16,6 +16,11 @@ public:
SkAutoPixmapStorage();
~SkAutoPixmapStorage();
/**
* Leave the moved-from object in a free-but-valid state.
*/
SkAutoPixmapStorage& operator=(SkAutoPixmapStorage&& other);
/**
* Try to allocate memory for the pixels needed to match the specified Info. On success
* return true and fill out the pixmap to point to that memory. The storage will be freed

View File

@ -347,7 +347,8 @@ sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
const DeferredTextureImageUsageParams[],
int paramCnt, void* buffer,
SkColorSpace* dstColorSpace) const {
SkColorSpace* dstColorSpace,
SkColorType dstColorType) const {
return 0;
}

View File

@ -597,10 +597,22 @@ private:
offsetof(DeferredTextureImage, member), \
sizeof(DeferredTextureImage::member));
static bool SupportsColorSpace(SkColorType colorType) {
switch (colorType) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
case kRGBA_F16_SkColorType:
return true;
default:
return false;
}
}
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
const DeferredTextureImageUsageParams params[],
int paramCnt, void* buffer,
SkColorSpace* dstColorSpace) const {
SkColorSpace* dstColorSpace,
SkColorType dstColorType) const {
// Some quick-rejects where is makes no sense to return CPU data
// e.g.
// - texture backed
@ -613,6 +625,12 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
return 0;
}
bool supportsColorSpace = SupportsColorSpace(dstColorType);
// Quick reject if the caller requests a color space with an unsupported color type.
if (SkToBool(dstColorSpace) && !supportsColorSpace) {
return 0;
}
// Extract relevant min/max values from the params array.
int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
SkFilterQuality highestFilterQuality = params[0].fQuality;
@ -657,7 +675,8 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
SkAutoPixmapStorage pixmap;
SkImageInfo info;
size_t pixelSize = 0;
if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) {
if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable() &&
pixmap.info().colorType() == dstColorType) {
info = pixmap.info();
pixelSize = SkAlign8(pixmap.getSafeSize());
if (!dstColorSpace) {
@ -680,24 +699,35 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
info = info.makeColorSpace(nullptr);
}
}
if (kIndex_8_SkColorType == info.colorType()) {
// Force Index8 to be N32 instead. Index8 is unsupported in Ganesh.
info = info.makeColorType(kN32_SkColorType);
}
// Force color type to be the requested type.
info = info.makeColorType(dstColorType);
pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
if (fillMode) {
pixmap.alloc(info);
// Always decode to N32 and convert to the requested type if necessary.
SkImageInfo decodeInfo = info.makeColorType(kN32_SkColorType);
SkAutoPixmapStorage decodePixmap;
decodePixmap.alloc(decodeInfo);
if (isScaled) {
if (!this->scalePixels(pixmap, scaleFilterQuality,
if (!this->scalePixels(decodePixmap, scaleFilterQuality,
SkImage::kDisallow_CachingHint)) {
return 0;
}
} else {
if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
if (!this->readPixels(decodePixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
return 0;
}
}
SkASSERT(!pixmap.ctable());
SkASSERT(!decodePixmap.ctable());
if (decodeInfo.colorType() != info.colorType()) {
pixmap.alloc(info);
// Convert and copy the decoded pixmap to the target pixmap.
decodePixmap.readPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes(), 0,
0);
} else {
pixmap = std::move(decodePixmap);
}
}
}
int mipMapLevelCount = 1;
@ -735,10 +765,11 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
SkColorSpaceTransferFn fn;
if (info.colorSpace()) {
SkASSERT(dstColorSpace);
SkASSERT(supportsColorSpace);
colorSpaceOffset = size;
colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
size += colorSpaceSize;
} else if (this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
} else if (supportsColorSpace && this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
// In legacy mode, preserve the color space tag on the SkImage. This is only
// supported if the color space has a parametric transfer function.
SkASSERT(!dstColorSpace);
@ -762,6 +793,7 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
SkASSERT(supportsColorSpace);
colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
}

View File

@ -1024,21 +1024,23 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
struct {
std::function<sk_sp<SkImage> ()> fImageFactory;
std::vector<SkImage::DeferredTextureImageUsageParams> fParams;
sk_sp<SkColorSpace> fColorSpace;
SkColorType fColorType;
SkFilterQuality fExpectedQuality;
int fExpectedScaleFactor;
bool fExpectation;
} testCases[] = {
{ create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, true },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, true },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, true },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_picture_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, false },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
{ [context] { return create_gpu_image(context); },
{{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, false },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create a texture image in a another GrContext.
{ [testContext, otherContextInfo] {
otherContextInfo.testContext()->makeCurrent();
@ -1046,21 +1048,34 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
testContext->makeCurrent();
return otherContextImage;
}, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, false },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create an image that is too large to upload.
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
kNone_SkFilterQuality, 1, false },
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create an image that is too large, but is scaled to an acceptable size.
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
kMedium_SkFilterQuality, 16, true},
nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
// Create an image with multiple low filter qualities, make sure we round up.
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 4},
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
kMedium_SkFilterQuality, 16, true},
nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
// Create an image with multiple prescale levels, make sure we chose the minimum scale.
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 5},
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
kMedium_SkFilterQuality, 16, true},
nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
// Create a images which are decoded to a 4444 backing.
{ create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
// Valid SkColorSpace and SkColorType.
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
SkColorSpace::MakeSRGB(), kN32_SkColorType, kNone_SkFilterQuality, 1, true },
// Invalid SkColorSpace and SkColorType.
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
SkColorSpace::MakeSRGB(), kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, false },
};
@ -1073,7 +1088,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
nullptr, nullptr);
nullptr, testCase.fColorSpace.get(),
testCase.fColorType);
static const char *const kFS[] = { "fail", "succeed" };
if (SkToBool(size) != testCase.fExpectation) {
ERRORF(reporter, "This image was expected to %s but did not.",
@ -1084,12 +1100,14 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
misaligned, nullptr)) {
misaligned, testCase.fColorSpace.get(),
testCase.fColorType)) {
ERRORF(reporter, "Should fail when buffer is misaligned.");
}
if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
buffer, nullptr)) {
buffer, testCase.fColorSpace.get(),
testCase.fColorType)) {
ERRORF(reporter, "deferred image size succeeded but creation failed.");
} else {
for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {