Make BitmapRegionDecoder succeed on invalid requests

If the client requests a color type or alpha type that is
not supported, we should decode to the default/appropriate
color and alpha types to match existing behavior in Android.

BUG=skia:

Review URL: https://codereview.chromium.org/1513023002
This commit is contained in:
msarett 2015-12-11 07:38:50 -08:00 committed by Commit bot
parent e804292e80
commit 9a0e3467da
5 changed files with 78 additions and 9 deletions

View File

@ -52,6 +52,24 @@ public:
*/
SkEncodedFormat getEncodedFormat() const { return fCodec->getEncodedFormat(); }
/**
* @param requestedColorType Color type requested by the client
*
* If it is possible to decode to requestedColorType, this returns
* requestedColorType. Otherwise, this returns whichever color type
* is suggested by the codec as the best match for the encoded data.
*/
SkColorType computeOutputColorType(SkColorType requestedColorType);
/**
* @param requestedUnpremul Indicates if the client requested
* unpremultiplied output
*
* Returns the appropriate alpha type to decode to. If the image
* has alpha, the value of requestedUnpremul will be honored.
*/
SkAlphaType computeOutputAlphaType(bool requestedUnpremul);
/**
* Returns the dimensions of the scaled output image, for an input
* sampleSize.

View File

@ -18,6 +18,7 @@ SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator,
const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
bool requireUnpremul) {
// Reject color types not supported by this method
if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) {
SkCodecPrintf("Error: Color type not supported.\n");
@ -34,8 +35,6 @@ bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* alloca
dstAlphaType = kPremul_SkAlphaType;
}
// FIXME: Can we add checks and support kIndex8 or unpremultiplied alpha in special cases?
// Fix the input sampleSize if necessary.
if (sampleSize < 1) {
sampleSize = 1;

View File

@ -14,8 +14,16 @@
* an SkCanvas. It uses the scanline decoder to subset the height. It then
* will subset the width and scale by drawing to an SkCanvas.
*/
// FIXME (msarett): This implementation does not support WEBP, because WEBP
// does not have a scanline decoder.
// FIXME: This class works well as a performance/quality comparison for
// SkBitmapRegionCodec, but it lacks several capabilities that are
// required by BitmapRegionDecoder in Android.
// (1) WEBP decodes - because SkWebpCodec does not have a scanline
// decoder.
// (2) Decodes to kGray8 and kIndex8.
// (3) Decodes to kUnpremul.
// (4) Correcting an invalid dstColorType. For example, if the
// client requests kRGB_565 for a non-opaque image, rather than
// fail, we need to go ahead and decode to kN32.
class SkBitmapRegionCanvas : public SkBitmapRegionDecoder {
public:

View File

@ -17,7 +17,7 @@ SkBitmapRegionCodec::SkBitmapRegionCodec(SkAndroidCodec* codec)
{}
bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator,
const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
const SkIRect& desiredSubset, int sampleSize, SkColorType prefColorType,
bool requireUnpremul) {
// Fix the input sampleSize if necessary.
@ -50,10 +50,8 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset);
// Create the image info for the decode
SkAlphaType dstAlphaType = fCodec->getInfo().alphaType();
if (kOpaque_SkAlphaType != dstAlphaType) {
dstAlphaType = requireUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
}
SkColorType dstColorType = fCodec->computeOutputColorType(prefColorType);
SkAlphaType dstAlphaType = fCodec->computeOutputAlphaType(requireUnpremul);
SkImageInfo decodeInfo = SkImageInfo::Make(scaledSize.width(), scaledSize.height(),
dstColorType, dstAlphaType);
@ -94,6 +92,13 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
scaledOutHeight += scaledOutY + scaledExtraY;
}
SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight);
if (kGray_8_SkColorType == dstColorType) {
// The legacy implementations of BitmapFactory and BitmapRegionDecoder
// used kAlpha8 for grayscale images (before kGray8 existed). While
// the codec recognizes kGray8, we need to decode into a kAlpha8
// bitmap in order to avoid a behavior change.
outInfo = SkImageInfo::MakeA8(scaledOutWidth, scaledOutHeight);
}
bitmap->setInfo(outInfo);
if (!bitmap->tryAllocPixels(allocator, colorTable.get())) {
SkCodecPrintf("Error: Could not allocate pixels.\n");

View File

@ -50,6 +50,45 @@ SkAndroidCodec* SkAndroidCodec::NewFromData(SkData* data, SkPngChunkReader* chun
return NewFromStream(new SkMemoryStream(data), chunkReader);
}
SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
SkColorType suggestedColorType = this->getInfo().colorType();
switch (requestedColorType) {
case kARGB_4444_SkColorType:
case kN32_SkColorType:
return kN32_SkColorType;
case kIndex_8_SkColorType:
if (kIndex_8_SkColorType == suggestedColorType) {
return kIndex_8_SkColorType;
}
break;
case kAlpha_8_SkColorType:
// Fall through to kGray_8. Before kGray_8_SkColorType existed,
// we allowed clients to request kAlpha_8 when they wanted a
// grayscale decode.
case kGray_8_SkColorType:
if (kGray_8_SkColorType == suggestedColorType) {
return kGray_8_SkColorType;
}
break;
case kRGB_565_SkColorType:
if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
return kRGB_565_SkColorType;
}
break;
default:
break;
}
return suggestedColorType;
}
SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
return kOpaque_SkAlphaType;
}
return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
}
SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
if (!is_valid_sample_size(sampleSize)) {
return SkISize::Make(0, 0);