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 |