summaryrefslogtreecommitdiffstats
path: root/skia/images/SkImageDecoder_libpng.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'skia/images/SkImageDecoder_libpng.cpp')
-rw-r--r--skia/images/SkImageDecoder_libpng.cpp242
1 files changed, 197 insertions, 45 deletions
diff --git a/skia/images/SkImageDecoder_libpng.cpp b/skia/images/SkImageDecoder_libpng.cpp
index 4378ca9..862ebf1 100644
--- a/skia/images/SkImageDecoder_libpng.cpp
+++ b/skia/images/SkImageDecoder_libpng.cpp
@@ -1,6 +1,6 @@
/* libs/graphics/images/SkImageDecoder_libpng.cpp
**
-** Copyright 2006, Google Inc.
+** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -83,7 +83,9 @@ 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
longjmp(png_jmpbuf(png_ptr), 1);
}
@@ -98,6 +100,24 @@ static bool pos_le(int value, int max) {
return value > 0 && value <= max;
}
+static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
+ SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
+
+ bool reallyHasAlpha = false;
+
+ for (int y = bm->height() - 1; y >= 0; --y) {
+ SkPMColor* p = bm->getAddr32(0, y);
+ for (int x = bm->width() - 1; x >= 0; --x) {
+ if (match == *p) {
+ *p = 0;
+ reallyHasAlpha = true;
+ }
+ p += 1;
+ }
+ }
+ return reallyHasAlpha;
+}
+
bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
SkBitmap::Config prefConfig, Mode mode) {
// SkAutoTrace apr("SkPNGImageDecoder::onDecode");
@@ -153,9 +173,30 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
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) {
+ 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) {
+ 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_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) {
+ png_set_gray_to_rgb(png_ptr);
+ }
+
SkBitmap::Config config;
bool hasAlpha = false;
bool doDither = this->getDitherImage();
+ SkPMColor theTranspColor = 0; // 0 tells us not to try to match
// check for sBIT chunk data, in case we should disable dithering because
// our data is not truely 8bits per component
@@ -176,11 +217,41 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
if (color_type == PNG_COLOR_TYPE_PALETTE) {
config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha
} else {
- png_color_16p transColor;
+ png_color_16p transpColor = NULL;
+ int numTransp = 0;
+
+ png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
- png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor);
+ bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
+ if (valid && numTransp == 1 && transpColor != NULL) {
+ /* Compute our transparent color, which we'll match against later.
+ We don't really handle 16bit components properly here, since we
+ do our compare *after* the values have been knocked down to 8bit
+ which means we will find more matches than we should. The real
+ 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);
+ } else {
+ theTranspColor = 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);
+ } else {
+ theTranspColor = 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;
@@ -212,26 +283,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
// from here down we are concerned with colortables and pixels
- /* tell libpng to strip 16 bit/color files down to 8 bits/color */
- if (bit_depth == 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) {
- 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_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) {
- png_set_gray_to_rgb(png_ptr);
- }
-
// 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
@@ -295,7 +346,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
SkAutoUnref aur(colorTable);
if (!this->allocPixelRef(decodedBitmap, colorTable)) {
- delete colorTable;
return false;
}
@@ -374,14 +424,19 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
}
if (hasAlpha && !reallyHasAlpha) {
+#if 0
SkDEBUGF(("Image doesn't really have alpha [%d %d]\n",
origWidth, origHeight));
+#endif
}
}
/* 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);
return true;
}
@@ -488,8 +543,19 @@ static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
}
}
+static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ memcpy(dst, src, width);
+}
+
static transform_scanline_proc choose_proc(SkBitmap::Config config,
bool hasAlpha) {
+ // we don't care about search on alpha if we're kIndex8, since only the
+ // colortable packing cares about that distinction, not the pixels
+ if (SkBitmap::kIndex8_Config == config) {
+ hasAlpha = false; // we store false in the table entries for kIndex8
+ }
+
static const struct {
SkBitmap::Config fConfig;
bool fHasAlpha;
@@ -500,6 +566,7 @@ static transform_scanline_proc choose_proc(SkBitmap::Config config,
{ SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
{ SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
{ SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
+ { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
};
for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
@@ -511,6 +578,78 @@ static transform_scanline_proc choose_proc(SkBitmap::Config config,
return NULL;
}
+// return the minimum legal bitdepth (by png standards) for this many colortable
+// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
+// we can use fewer bits per in png
+static int computeBitDepth(int colorCount) {
+#if 0
+ int bits = SkNextLog2(colorCount);
+ SkASSERT(bits >= 1 && bits <= 8);
+ // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
+ return SkNextPow2(bits);
+#else
+ // for the moment, we don't know how to pack bitdepth < 8
+ return 8;
+#endif
+}
+
+/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
+ pack trans[] and return the number of trans[] entries written. If hasAlpha
+ is false, the return value will always be 0.
+
+ Note: this routine takes care of unpremultiplying the RGB values when we
+ have alpha in the colortable, since png doesn't support premul colors
+*/
+static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
+ png_byte* SK_RESTRICT trans, bool hasAlpha) {
+ SkAutoLockColors alc(ctable);
+ const SkPMColor* SK_RESTRICT colors = alc.colors();
+ const int ctCount = ctable->count();
+ int i, num_trans = 0;
+
+ if (hasAlpha) {
+ /* first see if we have some number of fully opaque at the end of the
+ ctable. PNG allows num_trans < num_palette, but all of the trans
+ entries must come first in the palette. If I was smarter, I'd
+ reorder the indices and ctable so that all non-opaque colors came
+ first in the palette. But, since that would slow down the encode,
+ I'm leaving the indices and ctable order as is, and just looking
+ at the tail of the ctable for opaqueness.
+ */
+ num_trans = ctCount;
+ for (i = ctCount - 1; i >= 0; --i) {
+ if (SkGetPackedA32(colors[i]) != 0xFF) {
+ break;
+ }
+ num_trans -= 1;
+ }
+
+ const SkUnPreMultiply::Scale* SK_RESTRICT table =
+ SkUnPreMultiply::GetScaleTable();
+
+ for (i = 0; i < num_trans; i++) {
+ const SkPMColor c = *colors++;
+ const unsigned a = SkGetPackedA32(c);
+ const SkUnPreMultiply::Scale s = table[a];
+ trans[i] = a;
+ palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
+ palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
+ palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
+ }
+ // now fall out of this if-block to use common code for the trailing
+ // opaque entries
+ }
+
+ // these (remaining) entries are opaque
+ for (i = num_trans; i < ctCount; i++) {
+ SkPMColor c = *colors++;
+ palette[i].red = SkGetPackedR32(c);
+ palette[i].green = SkGetPackedG32(c);
+ palette[i].blue = SkGetPackedB32(c);
+ }
+ return num_trans;
+}
+
class SkPNGImageEncoder : public SkImageEncoder {
protected:
virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
@@ -521,20 +660,25 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
SkBitmap::Config config = bitmap.getConfig();
const bool hasAlpha = !bitmap.isOpaque();
+ int colorType = PNG_COLOR_MASK_COLOR;
+ int bitDepth = 8; // default for color
png_color_8 sig_bit;
switch (config) {
+ case SkBitmap::kIndex8_Config:
+ colorType |= PNG_COLOR_MASK_PALETTE;
+ // fall through to the ARGB_8888 case
case SkBitmap::kARGB_8888_Config:
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
- sig_bit.alpha = hasAlpha ? 8 : 0;
+ sig_bit.alpha = 8;
break;
case SkBitmap::kARGB_4444_Config:
sig_bit.red = 4;
sig_bit.green = 4;
sig_bit.blue = 4;
- sig_bit.alpha = hasAlpha ? 4 : 0;
+ sig_bit.alpha = 4;
break;
case SkBitmap::kRGB_565_Config:
sig_bit.red = 5;
@@ -543,16 +687,34 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
sig_bit.alpha = 0;
break;
default:
- SkDEBUGF(("SkPNGImageEncoder::onEncode can't encode %d config\n",
- config));
return false;
}
-
+
+ if (hasAlpha) {
+ // don't specify alpha if we're a palette, even if our ctable has alpha
+ if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
+ colorType |= PNG_COLOR_MASK_ALPHA;
+ }
+ } else {
+ sig_bit.alpha = 0;
+ }
+
SkAutoLockPixels alp(bitmap);
- if (NULL == bitmap.getPixels()) {
+ // readyToDraw checks for pixels (and colortable if that is required)
+ if (!bitmap.readyToDraw()) {
return false;
}
-
+
+ // we must do this after we have locked the pixels
+ SkColorTable* ctable = bitmap.getColorTable();
+ if (NULL != ctable) {
+ if (ctable->count() == 0) {
+ return false;
+ }
+ // check if we can store in fewer than 8 bits
+ bitDepth = computeBitDepth(ctable->count());
+ }
+
png_structp png_ptr;
png_infop info_ptr;
@@ -587,12 +749,12 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
- png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), 8,
- hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
+ png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
+ bitDepth, colorType,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
-#if 0 // need to support this some day <reed>
+#if 0 // need to support this some day
/* set the palette if there is one. REQUIRED for indexed-color images */
palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
* png_sizeof (png_color));
@@ -620,15 +782,6 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
png_write_end(png_ptr, info_ptr);
-#if 0
- /* If you png_malloced a palette, free it here (don't free info_ptr->palette,
- as recommended in versions 1.0.5m and earlier of this example; if
- libpng mallocs info_ptr->palette, libpng will free it). If you
- allocated it with malloc() instead of png_malloc(), use free() instead
- of png_free(). */
- png_free(png_ptr, palette);
-#endif
-
/* clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, &info_ptr);
return true;
@@ -640,4 +793,3 @@ SkImageEncoder* SkImageEncoder_PNG_Factory() {
}
#endif /* SK_SUPPORT_IMAGE_ENCODE */
-