diff options
-rw-r--r-- | src/images/SkImageDecoder_libpng.cpp | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp index d59a67bf8e..4c44b9109c 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 { |