Image decoder fixes (mostly) around A8.

Opaquness:
When decoding Gray to A8 in libpng, set reallyHasAlpha to true
and add a comment why we do not check for the real answer.

Add comments in jpeg decoder explaining why A8 is not opaque.

Fix a bug where an A8 subset is considered to be opaque.

Other fixes:
In SkJPEGImageDecoder, only allocate as much memory as
needed for each source row, based on the input config.
Also pull out common code into a static function.

When performing the check for requiring unpremultiplied colors,
allow A8 to succeed, since that setting should have no effect on
A8.

Add the check for requiring unpremultiplied colors to subset
decoding.

Fix a bug where attempting to sample gray to A8 does not sample.

R=reed@google.com

Review URL: https://codereview.chromium.org/26210007

git-svn-id: http://skia.googlecode.com/svn/trunk@11897 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
scroggo@google.com 2013-10-21 20:47:31 +00:00
parent 114038a2e2
commit 5ee18dd201
3 changed files with 109 additions and 53 deletions

View File

@ -495,6 +495,39 @@ static void fill_below_level(int y, SkBitmap* bitmap) {
canvas.drawColor(SK_ColorWHITE);
}
/**
* Get the config and bytes per pixel of the source data. Return
* whether the data is supported.
*/
static bool get_src_config(const jpeg_decompress_struct& cinfo,
SkScaledBitmapSampler::SrcConfig* sc,
int* srcBytesPerPixel) {
SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
if (JCS_CMYK == cinfo.out_color_space) {
// In this case we will manually convert the CMYK values to RGB
*sc = SkScaledBitmapSampler::kRGBX;
// The CMYK work-around relies on 4 components per pixel here
*srcBytesPerPixel = 4;
} else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
*sc = SkScaledBitmapSampler::kRGB;
*srcBytesPerPixel = 3;
#ifdef ANDROID_RGB
} else if (JCS_RGBA_8888 == cinfo.out_color_space) {
*sc = SkScaledBitmapSampler::kRGBX;
*srcBytesPerPixel = 4;
} else if (JCS_RGB_565 == cinfo.out_color_space) {
*sc = SkScaledBitmapSampler::kRGB_565;
*srcBytesPerPixel = 2;
#endif
} else if (1 == cinfo.out_color_components &&
JCS_GRAYSCALE == cinfo.out_color_space) {
*sc = SkScaledBitmapSampler::kGray;
*srcBytesPerPixel = 1;
} else {
return false;
}
return true;
}
bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
#ifdef TIME_DECODE
@ -543,6 +576,10 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
#endif
if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
// Assume an A8 bitmap is not opaque to avoid the check of each
// individual pixel. It is very unlikely to be opaque, since
// an opaque A8 bitmap would not be very interesting.
// Otherwise, a jpeg image is opaque.
return bm->setConfig(config, cinfo.image_width, cinfo.image_height, 0,
SkBitmap::kA8_Config == config ?
kPremul_SkAlphaType : kOpaque_SkAlphaType);
@ -565,6 +602,10 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
recompute_sampleSize(sampleSize, cinfo));
// Assume an A8 bitmap is not opaque to avoid the check of each
// individual pixel. It is very unlikely to be opaque, since
// an opaque A8 bitmap would not be very interesting.
// Otherwise, a jpeg image is opaque.
return bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight(),
0, SkBitmap::kA8_Config == config ?
kPremul_SkAlphaType : kOpaque_SkAlphaType);
@ -580,6 +621,10 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
}
SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
// Assume an A8 bitmap is not opaque to avoid the check of each
// individual pixel. It is very unlikely to be opaque, since
// an opaque A8 bitmap would not be very interesting.
// Otherwise, a jpeg image is opaque.
bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
SkBitmap::kA8_Config != config ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
@ -625,21 +670,9 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
// check for supported formats
SkScaledBitmapSampler::SrcConfig sc;
if (JCS_CMYK == cinfo.out_color_space) {
// In this case we will manually convert the CMYK values to RGB
sc = SkScaledBitmapSampler::kRGBX;
} else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
sc = SkScaledBitmapSampler::kRGB;
#ifdef ANDROID_RGB
} else if (JCS_RGBA_8888 == cinfo.out_color_space) {
sc = SkScaledBitmapSampler::kRGBX;
} else if (JCS_RGB_565 == cinfo.out_color_space) {
sc = SkScaledBitmapSampler::kRGB_565;
#endif
} else if (1 == cinfo.out_color_components &&
JCS_GRAYSCALE == cinfo.out_color_space) {
sc = SkScaledBitmapSampler::kGray;
} else {
int srcBytesPerPixel;
if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
return return_false(cinfo, *bm, "jpeg colorspace");
}
@ -647,8 +680,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
return return_false(cinfo, *bm, "sampler.begin");
}
// The CMYK work-around relies on 4 components per pixel here
SkAutoMalloc srcStorage(cinfo.output_width * 4);
SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
uint8_t* srcRow = (uint8_t*)srcStorage.get();
// Possibly skip initial rows [sampler.srcY0]
@ -801,7 +833,13 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
SkBitmap bitmap;
bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
// Assume an A8 bitmap is not opaque to avoid the check of each
// individual pixel. It is very unlikely to be opaque, since
// an opaque A8 bitmap would not be very interesting.
// Otherwise, a jpeg image is opaque.
bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
config == SkBitmap::kA8_Config ? kPremul_SkAlphaType :
kOpaque_SkAlphaType);
// Check ahead of time if the swap(dest, src) is possible or not.
@ -869,21 +907,9 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
// check for supported formats
SkScaledBitmapSampler::SrcConfig sc;
if (JCS_CMYK == cinfo->out_color_space) {
// In this case we will manually convert the CMYK values to RGB
sc = SkScaledBitmapSampler::kRGBX;
} else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
sc = SkScaledBitmapSampler::kRGB;
#ifdef ANDROID_RGB
} else if (JCS_RGBA_8888 == cinfo->out_color_space) {
sc = SkScaledBitmapSampler::kRGBX;
} else if (JCS_RGB_565 == cinfo->out_color_space) {
sc = SkScaledBitmapSampler::kRGB_565;
#endif
} else if (1 == cinfo->out_color_components &&
JCS_GRAYSCALE == cinfo->out_color_space) {
sc = SkScaledBitmapSampler::kGray;
} else {
int srcBytesPerPixel;
if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
return return_false(*cinfo, *bm, "jpeg colorspace");
}
@ -891,8 +917,7 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
return return_false(*cinfo, bitmap, "sampler.begin");
}
// The CMYK work-around relies on 4 components per pixel here
SkAutoMalloc srcStorage(width * 4);
SkAutoMalloc srcStorage(width * srcBytesPerPixel);
uint8_t* srcRow = (uint8_t*)srcStorage.get();
// Possibly skip initial rows [sampler.srcY0]

View File

@ -372,9 +372,14 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
&& 1 == sampleSize) {
if (SkBitmap::kA8_Config == config) {
// For an A8 bitmap, we assume there is an alpha for speed. It is
// possible the bitmap is opaque, but that is an unlikely use case
// since it would not be very interesting.
reallyHasAlpha = true;
// A8 is only allowed if the original was GRAY.
SkASSERT(config != SkBitmap::kA8_Config
|| PNG_COLOR_TYPE_GRAY == colorType);
SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
}
for (int i = 0; i < number_passes; i++) {
for (png_uint_32 y = 0; y < origHeight; y++) {
uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
@ -456,15 +461,20 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
if (0 != theTranspColor) {
reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
}
if (reallyHasAlpha && this->getRequireUnpremultipliedColors() &&
SkBitmap::kARGB_8888_Config != decodedBitmap->config()) {
// If the caller wants an unpremultiplied bitmap, and we let them get
// away with a config other than 8888, and it has alpha after all,
// return false, since the result will have premultiplied colors.
if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
switch (decodedBitmap->config()) {
case SkBitmap::kIndex8_Config:
// Fall through.
case SkBitmap::kARGB_4444_Config:
// We have chosen not to support unpremul for these configs.
return false;
default: {
// Fall through to finish the decode. This config either
// supports unpremul or it is irrelevant because it has no
// alpha (or only alpha).
// These brackets prevent a warning.
}
}
if (SkBitmap::kA8_Config == decodedBitmap->config()) {
reallyHasAlpha = true;
}
SkAlphaType alphaType = kOpaque_SkAlphaType;
@ -852,9 +862,14 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
&& 1 == sampleSize) {
if (SkBitmap::kA8_Config == config) {
// For an A8 bitmap, we assume there is an alpha for speed. It is
// possible the bitmap is opaque, but that is an unlikely use case
// since it would not be very interesting.
reallyHasAlpha = true;
// A8 is only allowed if the original was GRAY.
SkASSERT(config != SkBitmap::kA8_Config
|| PNG_COLOR_TYPE_GRAY == colorType);
SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
}
for (int i = 0; i < number_passes; i++) {
png_configure_decoder(png_ptr, &actualTop, i);
@ -945,8 +960,20 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
if (0 != theTranspColor) {
reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
}
if (SkBitmap::kA8_Config == decodedBitmap.config()) {
reallyHasAlpha = true;
if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
switch (decodedBitmap.config()) {
case SkBitmap::kIndex8_Config:
// Fall through.
case SkBitmap::kARGB_4444_Config:
// We have chosen not to support unpremul for these configs.
return false;
default: {
// Fall through to finish the decode. This config either
// supports unpremul or it is irrelevant because it has no
// alpha (or only alpha).
// These brackets prevent a warning.
}
}
}
SkAlphaType alphaType = kOpaque_SkAlphaType;
if (reallyHasAlpha) {

View File

@ -549,7 +549,11 @@ static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor[]) {
memcpy(dstRow, src, width);
// Sampling Gray to A8 uses the same function as Index to Index8,
// except we assume that there is alpha for speed, since an A8
// bitmap with no alpha is not interesting.
(void) Sample_Index_DI(dstRow, src, width, deltaSrc, /* y unused */ 0,
/* ctable unused */ NULL);
return true;
}