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 |