Index: src/images/SkImageDecoder_libpng.cpp |
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp |
index d59a67bf8ef8090e4268de40a788c04086e7d087..4c44b9109caadc6deba3cbea737c15239dbf7aff 100644 |
--- a/src/images/SkImageDecoder_libpng.cpp |
+++ b/src/images/SkImageDecoder_libpng.cpp |
@@ -42,18 +42,24 @@ extern "C" { |
class SkPNGImageIndex { |
public: |
- SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) { |
- this->png_ptr = png_ptr; |
- this->info_ptr = info_ptr; |
+ SkPNGImageIndex(SkStream* stream, png_structp png_ptr, png_infop info_ptr) |
+ : fStream(stream) |
+ , fPng_ptr(png_ptr) |
+ , fInfo_ptr(info_ptr) |
+ , fConfig(SkBitmap::kNo_Config) { |
+ SkASSERT(stream != NULL); |
+ stream->ref(); |
} |
~SkPNGImageIndex() { |
- if (NULL != png_ptr) { |
- png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); |
+ if (NULL != fPng_ptr) { |
+ png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); |
} |
} |
- png_structp png_ptr; |
- png_infop info_ptr; |
+ SkAutoTUnref<SkStream> fStream; |
+ png_structp fPng_ptr; |
+ png_infop fInfo_ptr; |
+ SkBitmap::Config fConfig; |
}; |
class SkPNGImageDecoder : public SkImageDecoder { |
@@ -261,10 +267,6 @@ bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp, |
png_set_expand_gray_1_2_4_to_8(png_ptr); |
} |
- /* Make a grayscale image into RGB. */ |
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { |
- png_set_gray_to_rgb(png_ptr); |
- } |
return true; |
} |
@@ -326,11 +328,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
SkAutoLockPixels alp(*decodedBitmap); |
- /* Add filler (or alpha) byte (before/after each RGB triplet) */ |
- if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) { |
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); |
- } |
- |
/* Turn on interlace handling. REQUIRED if you are not using |
* png_read_image(). To see how to handle interlacing passes, |
* see the png_read_row() method below: |
@@ -344,7 +341,11 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
*/ |
png_read_update_info(png_ptr, info_ptr); |
- if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { |
+ if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config) |
+ && 1 == sampleSize) { |
+ // A8 is only allowed if the original was GRAY. |
+ SkASSERT(config != SkBitmap::kA8_Config |
+ || 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); |
@@ -358,6 +359,11 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
if (colorTable != NULL) { |
sc = SkScaledBitmapSampler::kIndex; |
srcBytesPerPixel = 1; |
+ } else if (SkBitmap::kA8_Config == config) { |
+ // A8 is only allowed if the original was GRAY. |
+ SkASSERT(PNG_COLOR_TYPE_GRAY == colorType); |
+ sc = SkScaledBitmapSampler::kGray; |
+ srcBytesPerPixel = 1; |
} else if (hasAlpha) { |
sc = SkScaledBitmapSampler::kRGBA; |
} else { |
@@ -436,8 +442,10 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
- SkBitmap::Config *configp, bool * SK_RESTRICT hasAlphap, |
- bool *doDitherp, SkPMColor *theTranspColorp) { |
+ SkBitmap::Config* SK_RESTRICT configp, |
+ bool* SK_RESTRICT hasAlphap, |
+ bool* SK_RESTRICT doDitherp, |
+ SkPMColor* SK_RESTRICT theTranspColorp) { |
png_uint_32 origWidth, origHeight; |
int bitDepth, colorType; |
png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, |
@@ -510,7 +518,14 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
PNG_COLOR_TYPE_GRAY_ALPHA == colorType) { |
*hasAlphap = true; |
} |
- *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap); |
+ |
+ SrcDepth srcDepth = k32Bit_SrcDepth; |
+ if (PNG_COLOR_TYPE_GRAY == colorType) { |
+ srcDepth = k8BitGray_SrcDepth; |
+ SkASSERT(!*hasAlphap); |
+ } |
+ |
+ *configp = this->getPrefConfig(srcDepth, *hasAlphap); |
// now match the request against our capabilities |
if (*hasAlphap) { |
if (*configp != SkBitmap::kARGB_4444_Config) { |
@@ -518,7 +533,8 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
} |
} else { |
if (*configp != SkBitmap::kRGB_565_Config && |
- *configp != SkBitmap::kARGB_4444_Config) { |
+ *configp != SkBitmap::kARGB_4444_Config && |
+ *configp != SkBitmap::kA8_Config) { |
*configp = SkBitmap::kARGB_8888_Config; |
} |
} |
@@ -546,6 +562,33 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
if (this->getRequireUnpremultipliedColors() && *hasAlphap) { |
*configp = SkBitmap::kARGB_8888_Config; |
} |
+ |
+ if (fImageIndex != NULL) { |
+ if (SkBitmap::kNo_Config == fImageIndex->fConfig) { |
+ // This is the first time for this subset decode. From now on, |
+ // all decodes must be in the same config. |
+ fImageIndex->fConfig = *configp; |
+ } else if (fImageIndex->fConfig != *configp) { |
+ // Requesting a different config for a subsequent decode is not |
+ // supported. Report failure before we make changes to png_ptr. |
+ return false; |
+ } |
+ } |
+ |
+ bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType |
+ && *configp != SkBitmap::kA8_Config; |
+ |
+ // Unless the user is requesting A8, convert a grayscale image into RGB. |
+ // GRAY_ALPHA will always be converted to RGB |
+ if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { |
+ png_set_gray_to_rgb(png_ptr); |
+ } |
+ |
+ // Add filler (or alpha) byte (after each RGB triplet) if necessary. |
+ if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) { |
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); |
+ } |
+ |
return true; |
} |
@@ -647,7 +690,7 @@ bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *h |
if (fImageIndex) { |
SkDELETE(fImageIndex); |
} |
- fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr)); |
+ fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (sk_stream, png_ptr, info_ptr)); |
return true; |
} |
@@ -657,8 +700,8 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { |
return false; |
} |
- png_structp png_ptr = fImageIndex->png_ptr; |
- png_infop info_ptr = fImageIndex->info_ptr; |
+ png_structp png_ptr = fImageIndex->fPng_ptr; |
+ png_infop info_ptr = fImageIndex->fInfo_ptr; |
if (setjmp(png_jmpbuf(png_ptr))) { |
return false; |
} |
@@ -724,11 +767,6 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { |
} |
SkAutoLockPixels alp(decodedBitmap); |
- /* Add filler (or alpha) byte (before/after each RGB triplet) */ |
- if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) { |
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); |
- } |
- |
/* Turn on interlace handling. REQUIRED if you are not using |
* png_read_image(). To see how to handle interlacing passes, |
* see the png_read_row() method below: |
@@ -752,7 +790,12 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { |
int actualTop = rect.fTop; |
- if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { |
+ if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config) |
+ && 1 == sampleSize) { |
+ // A8 is only allowed if the original was GRAY. |
+ SkASSERT(config != SkBitmap::kA8_Config |
+ || PNG_COLOR_TYPE_GRAY == colorType); |
+ |
for (int i = 0; i < number_passes; i++) { |
png_configure_decoder(png_ptr, &actualTop, i); |
for (int j = 0; j < rect.fTop - actualTop; j++) { |
@@ -772,6 +815,11 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { |
if (colorTable != NULL) { |
sc = SkScaledBitmapSampler::kIndex; |
srcBytesPerPixel = 1; |
+ } else if (SkBitmap::kA8_Config == config) { |
+ // A8 is only allowed if the original was GRAY. |
+ SkASSERT(PNG_COLOR_TYPE_GRAY == colorType); |
+ sc = SkScaledBitmapSampler::kGray; |
+ srcBytesPerPixel = 1; |
} else if (hasAlpha) { |
sc = SkScaledBitmapSampler::kRGBA; |
} else { |