Chromium Code Reviews| Index: src/images/SkImageDecoder_libpng.cpp |
| diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp |
| index cca69fac1049a2b0e31073075629fb7fb5a89b89..bacb2cf5e0848279af6054fdd98f33c0add86296 100644 |
| --- a/src/images/SkImageDecoder_libpng.cpp |
| +++ b/src/images/SkImageDecoder_libpng.cpp |
| @@ -23,14 +23,50 @@ extern "C" { |
| #include "png.h" |
| } |
|
robertphillips
2013/03/11 18:25:55
comment?
Can this be nested in SkPNGImageDecoder?
|
| +class SkPNGImageIndex { |
| +public: |
| + SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) { |
| + this->png_ptr = png_ptr; |
| + this->info_ptr = info_ptr; |
| + } |
| + ~SkPNGImageIndex() { |
|
robertphillips
2013/03/11 18:25:55
NULL !=
djsollen
2013/03/11 19:43:24
Done.
|
| + if (png_ptr) { |
| + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); |
| + } |
| + } |
| + |
| + png_structp png_ptr; |
| + png_infop info_ptr; |
| +}; |
| + |
| class SkPNGImageDecoder : public SkImageDecoder { |
| public: |
| + SkPNGImageDecoder() { |
| + fImageIndex = NULL; |
| + } |
| virtual Format getFormat() const { |
| return kPNG_Format; |
| } |
| + virtual ~SkPNGImageDecoder() { |
| + SkDELETE(fImageIndex); |
| + } |
| protected: |
| +#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) |
|
robertphillips
2013/03/11 18:25:55
OVERRIDES?
djsollen
2013/03/11 19:43:24
Done.
|
| + virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height); |
|
robertphillips
2013/03/11 18:25:55
_const_ SkIRect& region?
djsollen
2013/03/11 19:43:24
Done.
|
| + virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect& region); |
| +#endif |
| virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); |
| + |
| +private: |
| + bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp); |
| + bool decodePalette(png_structp png_ptr, png_infop info_ptr, bool *hasAlphap, |
| + bool *reallyHasAlphap, SkColorTable **colorTablep); |
| + bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
| + SkBitmap::Config *config, bool *hasAlpha, |
| + bool *doDither, SkPMColor *theTranspColor); |
| + |
| + SkPNGImageIndex* fImageIndex; |
|
robertphillips
2013/03/11 18:25:55
INHERITED?
|
| }; |
| #ifndef png_jmpbuf |
| @@ -43,7 +79,7 @@ protected: |
| struct PNGAutoClean { |
| PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {} |
| ~PNGAutoClean() { |
| - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
| + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); |
| } |
| private: |
| png_structp png_ptr; |
| @@ -51,13 +87,21 @@ private: |
| }; |
| static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { |
| - SkStream* sk_stream = (SkStream*)png_get_io_ptr(png_ptr); |
| + SkStream* sk_stream = (SkStream*) png_ptr->io_ptr; |
| size_t bytes = sk_stream->read(data, length); |
| if (bytes != length) { |
| png_error(png_ptr, "Read Error!"); |
| } |
| } |
| +#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) |
| +static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) { |
| + SkStream* sk_stream = (SkStream*) png_ptr->io_ptr; |
| + sk_stream->rewind(); |
| + (void)sk_stream->skip(offset); |
| +} |
| +#endif |
| + |
| static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { |
| SkImageDecoder::Peeker* peeker = |
| (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr); |
| @@ -76,7 +120,7 @@ static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { |
| static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) { |
| for (int i = 0; i < count; i++) { |
| uint8_t* tmp = storage; |
| - png_read_rows(png_ptr, &tmp, NULL, 1); |
| + png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); |
| } |
| } |
| @@ -128,10 +172,8 @@ static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) { |
| return false; |
| } |
| -bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| - Mode mode) { |
| -// SkAutoTrace apr("SkPNGImageDecoder::onDecode"); |
| - |
| +bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp, |
| + png_infop *info_ptrp) { |
| /* Create and initialize the png_struct with the desired error handler |
| * functions. If you want to use the default stderr and longjump method, |
| * you can supply NULL for the last three parameters. We also supply the |
| @@ -143,15 +185,15 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| if (png_ptr == NULL) { |
| return false; |
| } |
| + *png_ptrp = png_ptr; |
| /* Allocate/initialize the memory for image information. */ |
| png_infop info_ptr = png_create_info_struct(png_ptr); |
| if (info_ptr == NULL) { |
| - png_destroy_read_struct(&png_ptr, NULL, NULL); |
| + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); |
| return false; |
| } |
| - |
| - PNGAutoClean autoClean(png_ptr, info_ptr); |
| + *info_ptrp = info_ptr; |
| /* Set error handling if you are using the setjmp/longjmp method (this is |
| * the normal method of doing things with libpng). REQUIRED unless you |
| @@ -165,6 +207,9 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| * png_init_io() here you would call: |
| */ |
| png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); |
| +#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) |
| + png_set_seek_fn(png_ptr, sk_seek_fn); |
| +#endif |
| /* where user_io_ptr is a structure you want available to the callbacks */ |
| /* If we have already read some of the signature */ |
| // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); |
| @@ -180,8 +225,8 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| png_read_info(png_ptr, info_ptr); |
| png_uint_32 origWidth, origHeight; |
| int bit_depth, color_type, interlace_type; |
| - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, |
| - &interlace_type, NULL, NULL); |
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, |
| + &color_type, &interlace_type, int_p_NULL, int_p_NULL); |
| /* tell libpng to strip 16 bit/color files down to 8 bits/color */ |
| if (bit_depth == 16) { |
| @@ -194,7 +239,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| } |
| /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ |
| if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { |
| - png_set_expand_gray_1_2_4_to_8(png_ptr); |
| + png_set_gray_1_2_4_to_8(png_ptr); |
| } |
| /* Make a grayscale image into RGB. */ |
| @@ -202,38 +247,218 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { |
| png_set_gray_to_rgb(png_ptr); |
| } |
| + return true; |
| +} |
| + |
| +bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| + Mode mode) { |
| + png_structp png_ptr; |
| + png_infop info_ptr; |
| + |
|
robertphillips
2013/03/11 18:25:55
!onDecodeInit?
djsollen
2013/03/11 19:43:24
Done.
|
| + if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) { |
| + return false; |
| + } |
| + |
| + if (setjmp(png_jmpbuf(png_ptr))) { |
| + return false; |
| + } |
| + |
| + PNGAutoClean autoClean(png_ptr, info_ptr); |
| + |
| + png_uint_32 origWidth, origHeight; |
| + int bit_depth, color_type, interlace_type; |
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, |
| + &color_type, &interlace_type, int_p_NULL, int_p_NULL); |
| SkBitmap::Config config; |
| bool hasAlpha = false; |
| bool doDither = this->getDitherImage(); |
| SkPMColor theTranspColor = 0; // 0 tells us not to try to match |
|
robertphillips
2013/03/11 18:25:55
!getBitmapConfig?
djsollen
2013/03/11 19:43:24
Done.
|
| + if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, |
| + &theTranspColor) == false) { |
| + return false; |
| + } |
| + |
| + const int sampleSize = this->getSampleSize(); |
| + SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); |
| + |
| + decodedBitmap->lockPixels(); |
| + void* rowptr = (void*) decodedBitmap->getPixels(); |
| + bool reuseBitmap = (rowptr != NULL); |
| + decodedBitmap->unlockPixels(); |
| + if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() || |
|
robertphillips
2013/03/11 18:25:55
intend the next line some more?
djsollen
2013/03/11 19:43:24
Done.
|
| + sampler.scaledHeight() != decodedBitmap->height())) { |
| + // Dimensions must match |
| + return false; |
| + } |
| + |
| + if (!reuseBitmap) { |
| + decodedBitmap->setConfig(config, sampler.scaledWidth(), |
| + sampler.scaledHeight(), 0); |
| + } |
| + if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
| + return true; |
| + } |
| + |
| + // from here down we are concerned with colortables and pixels |
| + |
| + // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype |
| + // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we |
| + // draw lots faster if we can flag the bitmap has being opaque |
| + bool reallyHasAlpha = false; |
| + SkColorTable* colorTable = NULL; |
| + |
| + if (color_type == PNG_COLOR_TYPE_PALETTE) { |
| + decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable); |
| + } |
| + |
| + SkAutoUnref aur(colorTable); |
| + |
| + if (!reuseBitmap) { |
| + if (!this->allocPixelRef(decodedBitmap, |
| + SkBitmap::kIndex8_Config == config ? |
|
robertphillips
2013/03/11 18:25:55
move this next line up to the end of the prior lin
djsollen
2013/03/11 19:43:24
Done.
|
| + colorTable : NULL)) { |
| + return false; |
| + } |
| + } |
| + |
| + SkAutoLockPixels alp(*decodedBitmap); |
| + |
| + /* Add filler (or alpha) byte (before/after each RGB triplet) */ |
| + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { |
| + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); |
| + } |
| + |
| + /* Turn on interlace handling. REQUIRED if you are not using |
|
robertphillips
2013/03/11 18:25:55
indent these next lines 1 char?
djsollen
2013/03/11 19:43:24
Done.
|
| + * png_read_image(). To see how to handle interlacing passes, |
| + * see the png_read_row() method below: |
| + */ |
| + const int number_passes = (interlace_type != PNG_INTERLACE_NONE) ? |
| + png_set_interlace_handling(png_ptr) : 1; |
| + |
| + /* Optional call to gamma correct and add the background to the palette |
| + * and update info structure. REQUIRED if you are expecting libpng to |
| + * update the palette for you (ie you selected such a transform above). |
| + */ |
| + png_read_update_info(png_ptr, info_ptr); |
| + |
| + if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { |
| + for (int i = 0; i < number_passes; i++) { |
| + for (png_uint_32 y = 0; y < origHeight; y++) { |
| + uint8_t* bmRow = decodedBitmap->getAddr8(0, y); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| + } |
| + } |
| + } else { |
| + SkScaledBitmapSampler::SrcConfig sc; |
| + int srcBytesPerPixel = 4; |
| + |
| + if (colorTable != NULL) { |
| + sc = SkScaledBitmapSampler::kIndex; |
| + srcBytesPerPixel = 1; |
| + } else if (hasAlpha) { |
| + sc = SkScaledBitmapSampler::kRGBA; |
| + } else { |
| + sc = SkScaledBitmapSampler::kRGBX; |
| + } |
| + |
| + /* We have to pass the colortable explicitly, since we may have one |
| + even if our decodedBitmap doesn't, due to the request that we |
| + upscale png's palette to a direct model |
| + */ |
| + SkAutoLockColors ctLock(colorTable); |
| + if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) { |
| + return false; |
| + } |
| + const int height = decodedBitmap->height(); |
| + |
| + if (number_passes > 1) { |
| + SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel); |
| + uint8_t* base = (uint8_t*)storage.get(); |
| + size_t rb = origWidth * srcBytesPerPixel; |
| + |
| + for (int i = 0; i < number_passes; i++) { |
| + uint8_t* row = base; |
| + for (png_uint_32 y = 0; y < origHeight; y++) { |
| + uint8_t* bmRow = row; |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| + row += rb; |
| + } |
| + } |
| + // now sample it |
| + base += sampler.srcY0() * rb; |
| + for (int y = 0; y < height; y++) { |
| + reallyHasAlpha |= sampler.next(base); |
| + base += sampler.srcDY() * rb; |
| + } |
| + } else { |
| + SkAutoMalloc storage(origWidth * srcBytesPerPixel); |
| + uint8_t* srcRow = (uint8_t*)storage.get(); |
| + skip_src_rows(png_ptr, srcRow, sampler.srcY0()); |
| + |
| + for (int y = 0; y < height; y++) { |
| + uint8_t* tmp = srcRow; |
| + png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); |
| + reallyHasAlpha |= sampler.next(srcRow); |
| + if (y < height - 1) { |
| + skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); |
| + } |
| + } |
| + |
| + // skip the rest of the rows (if any) |
| + png_uint_32 read = (height - 1) * sampler.srcDY() + |
| + sampler.srcY0() + 1; |
| + SkASSERT(read <= origHeight); |
| + skip_src_rows(png_ptr, srcRow, origHeight - read); |
| + } |
| + } |
| + |
| + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ |
| + png_read_end(png_ptr, info_ptr); |
| + |
| + if (0 != theTranspColor) { |
| + reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); |
| + } |
| + decodedBitmap->setIsOpaque(!reallyHasAlpha); |
| + if (reuseBitmap) { |
| + decodedBitmap->notifyPixelsChanged(); |
| + } |
| + return true; |
| +} |
| + |
| + |
| + |
| +bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, |
| + SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp, |
| + SkPMColor *theTranspColorp) { |
| + png_uint_32 origWidth, origHeight; |
| + int bit_depth, color_type, interlace_type; |
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, |
| + &color_type, &interlace_type, int_p_NULL, int_p_NULL); |
| + |
| // check for sBIT chunk data, in case we should disable dithering because |
| // our data is not truely 8bits per component |
| - if (doDither) { |
| - png_color_8p sig_bit = NULL; |
| - bool has_sbit = PNG_INFO_sBIT == png_get_sBIT(png_ptr, info_ptr, |
| - &sig_bit); |
| + if (*doDitherp) { |
| #if 0 |
| - if (has_sbit) { |
| - SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green, |
| - sig_bit->blue, sig_bit->alpha); |
| - } |
| + SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red, |
| + info_ptr->sig_bit.green, info_ptr->sig_bit.blue, |
| + info_ptr->sig_bit.alpha); |
| #endif |
| // 0 seems to indicate no information available |
| - if (has_sbit && pos_le(sig_bit->red, SK_R16_BITS) && |
| - pos_le(sig_bit->green, SK_G16_BITS) && |
| - pos_le(sig_bit->blue, SK_B16_BITS)) { |
| - doDither = false; |
| + if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) && |
| + pos_le(info_ptr->sig_bit.green, SK_G16_BITS) && |
| + pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) { |
| + *doDitherp = false; |
| } |
| } |
| if (color_type == PNG_COLOR_TYPE_PALETTE) { |
| bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr); |
| - config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha); |
| + *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha); |
| // now see if we can upscale to their requested config |
| - if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) { |
| - config = SkBitmap::kIndex8_Config; |
| + if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) { |
| + *configp = SkBitmap::kIndex8_Config; |
| } |
| } else { |
| png_color_16p transpColor = NULL; |
| @@ -253,19 +478,23 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| */ |
| if (color_type & PNG_COLOR_MASK_COLOR) { |
| if (16 == bit_depth) { |
| - theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8, |
| - transpColor->green >> 8, transpColor->blue >> 8); |
| + *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8, |
| + transpColor->green >> 8, |
| + transpColor->blue >> 8); |
| } else { |
| - theTranspColor = SkPackARGB32(0xFF, transpColor->red, |
| - transpColor->green, transpColor->blue); |
| + *theTranspColorp = SkPackARGB32(0xFF, transpColor->red, |
| + transpColor->green, |
| + transpColor->blue); |
| } |
| } else { // gray |
| if (16 == bit_depth) { |
| - theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8, |
| - transpColor->gray >> 8, transpColor->gray >> 8); |
| + *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8, |
| + transpColor->gray >> 8, |
| + transpColor->gray >> 8); |
| } else { |
| - theTranspColor = SkPackARGB32(0xFF, transpColor->gray, |
| - transpColor->gray, transpColor->gray); |
| + *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray, |
| + transpColor->gray, |
| + transpColor->gray); |
| } |
| } |
| } |
| @@ -273,18 +502,18 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| if (valid || |
| PNG_COLOR_TYPE_RGB_ALPHA == color_type || |
| PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { |
| - hasAlpha = true; |
| + *hasAlphap = true; |
| } |
| - config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha); |
| + *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap); |
| // now match the request against our capabilities |
| - if (hasAlpha) { |
| - if (config != SkBitmap::kARGB_4444_Config) { |
| - config = SkBitmap::kARGB_8888_Config; |
| + if (*hasAlphap) { |
| + if (*configp != SkBitmap::kARGB_4444_Config) { |
| + *configp = SkBitmap::kARGB_8888_Config; |
| } |
| } else { |
| - if (config != SkBitmap::kRGB_565_Config && |
| - config != SkBitmap::kARGB_4444_Config) { |
| - config = SkBitmap::kARGB_8888_Config; |
| + if (*configp != SkBitmap::kRGB_565_Config && |
| + *configp != SkBitmap::kARGB_4444_Config) { |
| + *configp = SkBitmap::kARGB_8888_Config; |
| } |
| } |
| } |
| @@ -302,96 +531,175 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| } |
| } |
|
robertphillips
2013/03/11 18:25:55
just "return this->chooseFromOneChoise ..."?
djsollen
2013/03/11 19:43:24
Done.
|
| - if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { |
| + if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) { |
| return false; |
| } |
| + return true; |
| +} |
| - const int sampleSize = this->getSampleSize(); |
| - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); |
| +bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr, |
| + bool *hasAlphap, bool *reallyHasAlphap, |
| + SkColorTable **colorTablep) { |
| + int num_palette; |
| + png_colorp palette; |
| + png_bytep trans; |
| + int num_trans; |
| + bool reallyHasAlpha = false; |
| + SkColorTable* colorTable = NULL; |
| - decodedBitmap->setConfig(config, sampler.scaledWidth(), |
| - sampler.scaledHeight(), 0); |
| - if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
| - return true; |
| + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
| + |
| + /* BUGGY IMAGE WORKAROUND |
| + |
| + We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count |
| + which is a problem since we use the byte as an index. To work around this we grow |
| + the colortable by 1 (if its < 256) and duplicate the last color into that slot. |
| + */ |
| + int colorCount = num_palette + (num_palette < 256); |
| + |
| + colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); |
| + |
| + SkPMColor* colorPtr = colorTable->lockColors(); |
| + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
| + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
| + *hasAlphap = (num_trans > 0); |
| + } else { |
| + num_trans = 0; |
| + colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); |
| + } |
| + // check for bad images that might make us crash |
| + if (num_trans > num_palette) { |
| + num_trans = num_palette; |
| } |
| - // from here down we are concerned with colortables and pixels |
| + int index = 0; |
| + int transLessThanFF = 0; |
| - // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype |
| - // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we |
| - // draw lots faster if we can flag the bitmap has being opaque |
| - bool reallyHasAlpha = false; |
| - SkColorTable* colorTable = NULL; |
| + for (; index < num_trans; index++) { |
| + transLessThanFF |= (int)*trans - 0xFF; |
| + *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue); |
| + palette++; |
| + } |
| + reallyHasAlpha |= (transLessThanFF < 0); |
| - if (color_type == PNG_COLOR_TYPE_PALETTE) { |
| - int num_palette; |
| - png_colorp palette; |
| - png_bytep trans; |
| - int num_trans; |
| + for (; index < num_palette; index++) { |
| + *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); |
| + palette++; |
| + } |
| + |
| + // see BUGGY IMAGE WORKAROUND comment above |
| + if (num_palette < 256) { |
| + *colorPtr = colorPtr[-1]; |
| + } |
| + colorTable->unlockColors(true); |
| + *colorTablep = colorTable; |
| + *reallyHasAlphap = reallyHasAlpha; |
| + return true; |
| +} |
| - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
| +#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) |
| - /* BUGGY IMAGE WORKAROUND |
| +bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *height) { |
| + png_structp png_ptr; |
| + png_infop info_ptr; |
| - We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count |
| - which is a problem since we use the byte as an index. To work around this we grow |
| - the colortable by 1 (if its < 256) and duplicate the last color into that slot. |
| - */ |
| - int colorCount = num_palette + (num_palette < 256); |
| + if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) { |
| + return false; |
| + } |
| - colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); |
| + if (setjmp(png_jmpbuf(png_ptr)) != 0) { |
| + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); |
| + return false; |
| + } |
| - SkPMColor* colorPtr = colorTable->lockColors(); |
| - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
| - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
| - hasAlpha = (num_trans > 0); |
| - } else { |
| - num_trans = 0; |
| - colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); |
| - } |
| - // check for bad images that might make us crash |
| - if (num_trans > num_palette) { |
| - num_trans = num_palette; |
| - } |
| + int bit_depth, color_type, interlace_type; |
| + png_uint_32 origWidth, origHeight; |
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, |
| + &color_type, &interlace_type, int_p_NULL, int_p_NULL); |
| - int index = 0; |
| - int transLessThanFF = 0; |
| + *width = origWidth; |
| + *height = origHeight; |
| - for (; index < num_trans; index++) { |
| - transLessThanFF |= (int)*trans - 0xFF; |
| - *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue); |
| - palette++; |
| - } |
| - reallyHasAlpha |= (transLessThanFF < 0); |
| + png_build_index(png_ptr); |
| + this->fImageIndex = new SkPNGImageIndex(png_ptr, info_ptr); |
| - for (; index < num_palette; index++) { |
| - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); |
| - palette++; |
| - } |
| + return true; |
| +} |
| - // see BUGGY IMAGE WORKAROUND comment above |
| - if (num_palette < 256) { |
| - *colorPtr = colorPtr[-1]; |
| - } |
| - colorTable->unlockColors(true); |
| +bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect& region) { |
| + png_structp png_ptr = this->fImageIndex->png_ptr; |
| + png_infop info_ptr = this->fImageIndex->info_ptr; |
| + if (setjmp(png_jmpbuf(png_ptr))) { |
| + return false; |
| } |
| - SkAutoUnref aur(colorTable); |
| + png_uint_32 origWidth, origHeight; |
| + int bit_depth, color_type, interlace_type; |
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, |
| + &color_type, &interlace_type, int_p_NULL, int_p_NULL); |
| + |
| + SkIRect rect = SkIRect::MakeWH(origWidth, origHeight); |
| - if (!this->allocPixelRef(decodedBitmap, |
| - SkBitmap::kIndex8_Config == config ? |
| - colorTable : NULL)) { |
| + if (!rect.intersect(region)) { |
| + // If the requested region is entirely outsides the image, just |
| + // returns false |
| return false; |
| } |
| - SkAutoLockPixels alp(*decodedBitmap); |
| + SkBitmap::Config config; |
| + bool hasAlpha = false; |
| + bool doDither = this->getDitherImage(); |
| + SkPMColor theTranspColor = 0; // 0 tells us not to try to match |
| + |
| + if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, |
| + &theTranspColor) == false) { |
| + return false; |
| + } |
| + |
| + const int sampleSize = this->getSampleSize(); |
| + SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize); |
| - /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ |
| -// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) |
| -// ; // png_set_swap_alpha(png_ptr); |
| + SkBitmap *decodedBitmap = new SkBitmap; |
| + SkAutoTDelete<SkBitmap> adb(decodedBitmap); |
| - /* swap bytes of 16 bit files to least significant byte first */ |
| - // png_set_swap(png_ptr); |
| + decodedBitmap->setConfig(config, sampler.scaledWidth(), |
| + sampler.scaledHeight(), 0); |
| + |
| + // from here down we are concerned with colortables and pixels |
| + |
| + // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype |
| + // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we |
| + // draw lots faster if we can flag the bitmap has being opaque |
| + bool reallyHasAlpha = false; |
| + SkColorTable* colorTable = NULL; |
| + |
| + if (color_type == PNG_COLOR_TYPE_PALETTE) { |
| + decodePalette(png_ptr, info_ptr, &hasAlpha, |
| + &reallyHasAlpha, &colorTable); |
| + } |
| + |
| + SkAutoUnref aur(colorTable); |
| + |
| + // Check ahead of time if the swap(dest, src) is possible in crop or not. |
| + // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening. |
| + // If no, then we will use alloc to allocate pixels to prevent garbage collection. |
| + int w = rect.width() / sampleSize; |
| + int h = rect.height() / sampleSize; |
| + bool swapOnly = (rect == region) && (w == decodedBitmap->width()) && |
| + (h == decodedBitmap->height()) && |
| + ((0 - rect.x()) / sampleSize == 0) && bm->isNull(); |
| + if (swapOnly) { |
| + if (!this->allocPixelRef(decodedBitmap, |
| + SkBitmap::kIndex8_Config == config ? colorTable : NULL)) { |
| + return false; |
| + } |
| + } else { |
| + if (!decodedBitmap->allocPixels( |
| + NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) { |
| + return false; |
| + } |
| + } |
| + SkAutoLockPixels alp(*decodedBitmap); |
| /* Add filler (or alpha) byte (before/after each RGB triplet) */ |
| if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { |
| @@ -409,13 +717,21 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| * and update info structure. REQUIRED if you are expecting libpng to |
| * update the palette for you (ie you selected such a transform above). |
| */ |
| + png_ptr->pass = 0; |
| png_read_update_info(png_ptr, info_ptr); |
| + int actualTop = rect.fTop; |
| + |
| if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { |
| for (int i = 0; i < number_passes; i++) { |
| + png_configure_decoder(png_ptr, &actualTop, i); |
| + for (int j = 0; j < rect.fTop - actualTop; j++) { |
| + uint8_t* bmRow = decodedBitmap->getAddr8(0, 0); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| + } |
| for (png_uint_32 y = 0; y < origHeight; y++) { |
| uint8_t* bmRow = decodedBitmap->getAddr8(0, y); |
| - png_read_rows(png_ptr, &bmRow, NULL, 1); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| } |
| } |
| } else { |
| @@ -447,10 +763,15 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| size_t rb = origWidth * srcBytesPerPixel; |
| for (int i = 0; i < number_passes; i++) { |
| + png_configure_decoder(png_ptr, &actualTop, i); |
| + for (int j = 0; j < rect.fTop - actualTop; j++) { |
| + uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels(); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| + } |
| uint8_t* row = base; |
| - for (png_uint_32 y = 0; y < origHeight; y++) { |
| + for (int32_t y = 0; y < rect.height(); y++) { |
| uint8_t* bmRow = row; |
| - png_read_rows(png_ptr, &bmRow, NULL, 1); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| row += rb; |
| } |
| } |
| @@ -463,27 +784,30 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| } else { |
| SkAutoMalloc storage(origWidth * srcBytesPerPixel); |
| uint8_t* srcRow = (uint8_t*)storage.get(); |
| + |
| + png_configure_decoder(png_ptr, &actualTop, 0); |
| skip_src_rows(png_ptr, srcRow, sampler.srcY0()); |
| + for (int i = 0; i < rect.fTop - actualTop; i++) { |
| + uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels(); |
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); |
| + } |
| for (int y = 0; y < height; y++) { |
| uint8_t* tmp = srcRow; |
| - png_read_rows(png_ptr, &tmp, NULL, 1); |
| + png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); |
| reallyHasAlpha |= sampler.next(srcRow); |
| if (y < height - 1) { |
| skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); |
| } |
| } |
| - |
| - // skip the rest of the rows (if any) |
| - png_uint_32 read = (height - 1) * sampler.srcDY() + |
| - sampler.srcY0() + 1; |
| - SkASSERT(read <= origHeight); |
| - skip_src_rows(png_ptr, srcRow, origHeight - read); |
| } |
| } |
| - |
| - /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ |
| - png_read_end(png_ptr, info_ptr); |
| + if (swapOnly) { |
| + bm->swap(*decodedBitmap); |
| + } else { |
| + cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(), |
| + region.width(), region.height(), 0, rect.y()); |
| + } |
| if (0 != theTranspColor) { |
| reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); |
| @@ -491,6 +815,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| decodedBitmap->setIsOpaque(!reallyHasAlpha); |
| return true; |
| } |
| +#endif |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -498,7 +823,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, |
| #include "SkUnPreMultiply.h" |
| static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { |
| - SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); |
| + SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr; |
| if (!sk_stream->write(data, len)) { |
| png_error(png_ptr, "sk_write_fn Error!"); |
| } |
| @@ -697,7 +1022,7 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, |
| info_ptr = png_create_info_struct(png_ptr); |
| if (NULL == info_ptr) { |
| - png_destroy_write_struct(&png_ptr, NULL); |
| + png_destroy_write_struct(&png_ptr, png_infopp_NULL); |
| return false; |
| } |
| @@ -709,7 +1034,7 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, |
| return false; |
| } |
| - png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, NULL); |
| + png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL); |
| /* Set the image information here. Width and height are up to 2^31, |
| * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on |