| Index: src/images/SkImageDecoder_libpng.cpp
|
| diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
|
| index cca69fac1049a2b0e31073075629fb7fb5a89b89..501a0d69f7f2f571eeee30d4ef9ea0a959dda52b 100644
|
| --- a/src/images/SkImageDecoder_libpng.cpp
|
| +++ b/src/images/SkImageDecoder_libpng.cpp
|
| @@ -23,14 +23,52 @@ extern "C" {
|
| #include "png.h"
|
| }
|
|
|
| +class SkPNGImageIndex {
|
| +public:
|
| + SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) {
|
| + this->png_ptr = png_ptr;
|
| + this->info_ptr = info_ptr;
|
| + }
|
| + ~SkPNGImageIndex() {
|
| + if (NULL != 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:
|
| - virtual Format getFormat() const {
|
| + SkPNGImageDecoder() {
|
| + fImageIndex = NULL;
|
| + }
|
| + virtual Format getFormat() const SK_OVERRIDE {
|
| return kPNG_Format;
|
| }
|
| + virtual ~SkPNGImageDecoder() {
|
| + SkDELETE(fImageIndex);
|
| + }
|
|
|
| protected:
|
| - virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
|
| +#ifdef SK_BUILD_FOR_ANDROID
|
| + virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
|
| + virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
|
| +#endif
|
| + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
|
| +
|
| +private:
|
| + SkPNGImageIndex* fImageIndex;
|
| +
|
| + 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);
|
| +
|
| + typedef SkImageDecoder INHERITED;
|
| };
|
|
|
| #ifndef png_jmpbuf
|
| @@ -43,7 +81,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 +89,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!");
|
| }
|
| }
|
|
|
| +#ifdef SK_BUILD_FOR_ANDROID
|
| +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);
|
| @@ -67,16 +113,14 @@ static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
|
| }
|
|
|
| static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
|
| -#if 0
|
| - SkDebugf("------ png error %s\n", msg);
|
| -#endif
|
| + SkDEBUGF(("------ png error %s\n", msg));
|
| longjmp(png_jmpbuf(png_ptr), 1);
|
| }
|
|
|
| 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);
|
| +#ifdef SK_BUILD_FOR_ANDROID
|
| + 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 */ );
|
| @@ -179,65 +224,242 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| * PNG file before the first IDAT (image data chunk). */
|
| 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);
|
| + int bitDepth, colorType;
|
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| + &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
|
|
|
| /* tell libpng to strip 16 bit/color files down to 8 bits/color */
|
| - if (bit_depth == 16) {
|
| + if (bitDepth == 16) {
|
| png_set_strip_16(png_ptr);
|
| }
|
| /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
|
| * byte into separate bytes (useful for paletted and grayscale images). */
|
| - if (bit_depth < 8) {
|
| + if (bitDepth < 8) {
|
| png_set_packing(png_ptr);
|
| }
|
| /* 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);
|
| + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
|
| + png_set_gray_1_2_4_to_8(png_ptr);
|
| }
|
|
|
| /* Make a grayscale image into RGB. */
|
| - if (color_type == PNG_COLOR_TYPE_GRAY ||
|
| - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
| + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == 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;
|
| +
|
| + if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
|
| + return false;
|
| + }
|
| +
|
| + if (setjmp(png_jmpbuf(png_ptr))) {
|
| + return false;
|
| + }
|
| +
|
| + PNGAutoClean autoClean(png_ptr, info_ptr);
|
| +
|
| + png_uint_32 origWidth, origHeight;
|
| + int bitDepth, colorType, interlaceType;
|
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| + &colorType, &interlaceType, 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
|
|
|
| + if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
|
| + 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() ||
|
| + 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 (colorType == 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 ? colorTable : NULL)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + 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:
|
| + */
|
| + const int number_passes = (interlaceType != 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 rowBytes = 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 += rowBytes;
|
| + }
|
| + }
|
| + // now sample it
|
| + base += sampler.srcY0() * rowBytes;
|
| + for (int y = 0; y < height; y++) {
|
| + reallyHasAlpha |= sampler.next(base);
|
| + base += sampler.srcDY() * rowBytes;
|
| + }
|
| + } 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 bitDepth, colorType;
|
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| + &colorType, int_p_NULL, 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) {
|
| + if (colorType == 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;
|
| - int numTransp = 0;
|
| + png_color_16p transpColor = NULL;
|
| + int numTransp = 0;
|
|
|
| png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
|
|
|
| @@ -251,40 +473,44 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| fix seems to be to see the actual 16bit components, do the
|
| compare, and then knock it down to 8bits ourselves.
|
| */
|
| - if (color_type & PNG_COLOR_MASK_COLOR) {
|
| - if (16 == bit_depth) {
|
| - theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
|
| - transpColor->green >> 8, transpColor->blue >> 8);
|
| + if (colorType & PNG_COLOR_MASK_COLOR) {
|
| + if (16 == bitDepth) {
|
| + *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);
|
| + if (16 == bitDepth) {
|
| + *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);
|
| }
|
| }
|
| }
|
|
|
| if (valid ||
|
| - PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
|
| - PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
|
| - hasAlpha = true;
|
| + PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
|
| + PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
|
| + *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,99 +528,172 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| }
|
| }
|
|
|
| - if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
|
| - return false;
|
| + return this->chooseFromOneChoice(*configp, origWidth, origHeight);
|
| +}
|
| +
|
| +bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
|
| + bool *hasAlphap, bool *reallyHasAlphap,
|
| + SkColorTable **colorTablep) {
|
| + int numPalette;
|
| + png_colorp palette;
|
| + png_bytep trans;
|
| + int numTrans;
|
| + bool reallyHasAlpha = false;
|
| + SkColorTable* colorTable = NULL;
|
| +
|
| + png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
|
| +
|
| + /* 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 = numPalette + (numPalette < 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, &numTrans, NULL);
|
| + *hasAlphap = (numTrans > 0);
|
| + } else {
|
| + numTrans = 0;
|
| + colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
|
| + }
|
| + // check for bad images that might make us crash
|
| + if (numTrans > numPalette) {
|
| + numTrans = numPalette;
|
| }
|
|
|
| - const int sampleSize = this->getSampleSize();
|
| - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
|
| + int index = 0;
|
| + int transLessThanFF = 0;
|
|
|
| - decodedBitmap->setConfig(config, sampler.scaledWidth(),
|
| - sampler.scaledHeight(), 0);
|
| - if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
| - return true;
|
| + for (; index < numTrans; index++) {
|
| + transLessThanFF |= (int)*trans - 0xFF;
|
| + *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
|
| + palette++;
|
| }
|
| + reallyHasAlpha |= (transLessThanFF < 0);
|
|
|
| - // from here down we are concerned with colortables and pixels
|
| + for (; index < numPalette; index++) {
|
| + *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
|
| + palette++;
|
| + }
|
|
|
| - // 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;
|
| + // see BUGGY IMAGE WORKAROUND comment above
|
| + if (numPalette < 256) {
|
| + *colorPtr = colorPtr[-1];
|
| + }
|
| + colorTable->unlockColors(true);
|
| + *colorTablep = colorTable;
|
| + *reallyHasAlphap = reallyHasAlpha;
|
| + return true;
|
| +}
|
| +
|
| +#ifdef SK_BUILD_FOR_ANDROID
|
|
|
| - if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
| - int num_palette;
|
| - png_colorp palette;
|
| - png_bytep trans;
|
| - int num_trans;
|
| +bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *height) {
|
| + png_structp png_ptr;
|
| + png_infop info_ptr;
|
|
|
| - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
|
| + if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
|
| + return false;
|
| + }
|
|
|
| - /* BUGGY IMAGE WORKAROUND
|
| + if (setjmp(png_jmpbuf(png_ptr)) != 0) {
|
| + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
| + return false;
|
| + }
|
|
|
| - 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);
|
| + png_uint_32 origWidth, origHeight;
|
| + int bitDepth, colorType;
|
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| + &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
|
|
|
| - colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
|
| + *width = origWidth;
|
| + *height = origHeight;
|
|
|
| - 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;
|
| - }
|
| + png_build_index(png_ptr);
|
|
|
| - int index = 0;
|
| - int transLessThanFF = 0;
|
| + if (fImageIndex) {
|
| + SkDELETE(fImageIndex);
|
| + }
|
| + fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr));
|
|
|
| - for (; index < num_trans; index++) {
|
| - transLessThanFF |= (int)*trans - 0xFF;
|
| - *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| - reallyHasAlpha |= (transLessThanFF < 0);
|
| + return true;
|
| +}
|
|
|
| - for (; index < num_palette; index++) {
|
| - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| +bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
|
| + png_structp png_ptr = fImageIndex->png_ptr;
|
| + png_infop info_ptr = fImageIndex->info_ptr;
|
| + if (setjmp(png_jmpbuf(png_ptr))) {
|
| + return false;
|
| + }
|
|
|
| - // see BUGGY IMAGE WORKAROUND comment above
|
| - if (num_palette < 256) {
|
| - *colorPtr = colorPtr[-1];
|
| - }
|
| - colorTable->unlockColors(true);
|
| + png_uint_32 origWidth, origHeight;
|
| + int bitDepth, colorType, interlaceType;
|
| + png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| + &colorType, &interlaceType, int_p_NULL, int_p_NULL);
|
| +
|
| + SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
|
| +
|
| + if (!rect.intersect(region)) {
|
| + // If the requested region is entirely outsides the image, just
|
| + // returns false
|
| + return false;
|
| }
|
|
|
| - SkAutoUnref aur(colorTable);
|
| + SkBitmap::Config config;
|
| + bool hasAlpha = false;
|
| + bool doDither = this->getDitherImage();
|
| + SkPMColor theTranspColor = 0; // 0 tells us not to try to match
|
|
|
| - if (!this->allocPixelRef(decodedBitmap,
|
| - SkBitmap::kIndex8_Config == config ?
|
| - colorTable : NULL)) {
|
| + if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
|
| return false;
|
| }
|
|
|
| - SkAutoLockPixels alp(*decodedBitmap);
|
| + const int sampleSize = this->getSampleSize();
|
| + SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
|
| +
|
| + SkBitmap decodedBitmap;
|
| + 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 (colorType == PNG_COLOR_TYPE_PALETTE) {
|
| + decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
|
| + }
|
|
|
| - /* 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);
|
| + SkAutoUnref aur(colorTable);
|
|
|
| - /* swap bytes of 16 bit files to least significant byte first */
|
| - // png_set_swap(png_ptr);
|
| + // Check ahead of time if the swap(dest, src) is possible.
|
| + // 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;
|
| + const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
|
| + (h == decodedBitmap.height()) && bm->isNull();
|
| + const bool needColorTable = SkBitmap::kIndex8_Config == config;
|
| + if (swapOnly) {
|
| + if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
|
| + return false;
|
| + }
|
| + } else {
|
| + if (!decodedBitmap.allocPixels(NULL, needColorTable ? 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) {
|
| + if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) {
|
| png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
| }
|
|
|
| @@ -402,20 +701,28 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| * 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;
|
| + const int number_passes = (interlaceType != 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_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);
|
| + uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
|
| + png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
| }
|
| }
|
| } else {
|
| @@ -436,10 +743,10 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| upscale png's palette to a direct model
|
| */
|
| SkAutoLockColors ctLock(colorTable);
|
| - if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
|
| + if (!sampler.begin(&decodedBitmap, sc, doDither, ctLock.colors())) {
|
| return false;
|
| }
|
| - const int height = decodedBitmap->height();
|
| + const int height = decodedBitmap.height();
|
|
|
| if (number_passes > 1) {
|
| SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
|
| @@ -447,10 +754,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,34 +775,40 @@ 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 (0 != theTranspColor) {
|
| - reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
|
| + reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
|
| }
|
| - decodedBitmap->setIsOpaque(!reallyHasAlpha);
|
| + decodedBitmap.setIsOpaque(!reallyHasAlpha);
|
| +
|
| + if (swapOnly) {
|
| + bm->swap(decodedBitmap);
|
| + } else {
|
| + cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
|
| + region.width(), region.height(), 0, rect.y());
|
| + }
|
| +
|
| return true;
|
| }
|
| +#endif
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| @@ -498,7 +816,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!");
|
| }
|
| @@ -609,12 +927,14 @@ static inline int pack_palette(SkColorTable* ctable,
|
|
|
| class SkPNGImageEncoder : public SkImageEncoder {
|
| protected:
|
| - virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
|
| + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
|
| private:
|
| bool doEncode(SkWStream* stream, const SkBitmap& bm,
|
| const bool& hasAlpha, int colorType,
|
| int bitDepth, SkBitmap::Config config,
|
| png_color_8& sig_bit);
|
| +
|
| + typedef SkImageEncoder INHERITED;
|
| };
|
|
|
| bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
|
| @@ -697,7 +1017,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 +1029,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
|
|
|