Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Unified Diff: ui/gfx/codec/png_codec.cc

Issue 9496004: Support for interlaced PNGs (in gfx::PNGCodec) (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/codec/png_codec.cc
===================================================================
--- ui/gfx/codec/png_codec.cc (revision 123937)
+++ ui/gfx/codec/png_codec.cc (working copy)
@@ -54,34 +54,6 @@
}
}
-void ConvertRGBtoSkia(const unsigned char* rgb, int pixel_width,
- unsigned char* rgba, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const unsigned char* pixel_in = &rgb[x * 3];
- uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x * 4]);
- *pixel_out = SkPackARGB32(0xFF, pixel_in[0], pixel_in[1], pixel_in[2]);
- }
-}
-
-void ConvertRGBAtoSkia(const unsigned char* rgb, int pixel_width,
- unsigned char* rgba, bool* is_opaque) {
- int total_length = pixel_width * 4;
- for (int x = 0; x < total_length; x += 4) {
- const unsigned char* pixel_in = &rgb[x];
- uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x]);
-
- unsigned char alpha = pixel_in[3];
- if (alpha != 255) {
- *is_opaque = false;
- *pixel_out = SkPreMultiplyARGB(alpha,
- pixel_in[0], pixel_in[1], pixel_in[2]);
- } else {
- *pixel_out = SkPackARGB32(alpha,
- pixel_in[0], pixel_in[1], pixel_in[2]);
- }
Francois 2012/02/29 09:12:29 The work done by this converter and the one above
- }
-}
-
void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width,
unsigned char* rgb, bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
@@ -148,7 +120,6 @@
bitmap(NULL),
is_opaque(true),
output(o),
- row_converter(NULL),
width(0),
height(0),
done(false) {
@@ -161,7 +132,6 @@
bitmap(skbitmap),
is_opaque(true),
output(NULL),
- row_converter(NULL),
width(0),
height(0),
done(false) {
@@ -181,11 +151,6 @@
// instead of directly to an SkBitmap.
std::vector<unsigned char>* output;
- // Called to convert a row from the library to the correct output format.
- // When NULL, no conversion is necessary.
- void (*row_converter)(const unsigned char* in, int w, unsigned char* out,
- bool* is_opaque);
Francois 2012/02/29 09:12:29 All decode converters have been removed in favor o
-
// Size of the image, set in the info callback.
int width;
int height;
@@ -197,27 +162,29 @@
DISALLOW_COPY_AND_ASSIGN(PngDecoderState);
};
-void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
- unsigned char* rgba, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const unsigned char* pixel_in = &rgb[x * 3];
- unsigned char* pixel_out = &rgba[x * 4];
- pixel_out[0] = pixel_in[0];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[2];
- pixel_out[3] = 0xff;
Francois 2012/02/29 09:12:29 This converter's work is now done by libpng itself
- }
-}
+// User transform (passed to libpng) which converts a row decoded by libpng to
+// Skia format. Expects the row to have 4 channels, otherwise there won't be
+// enough room in |data|.
+void ConvertRGBARowToSkia(png_structp png_ptr,
+ png_row_infop row_info,
+ png_bytep data) {
+ const int channels = row_info->channels;
+ DCHECK_EQ(channels, 4);
-void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
- unsigned char* bgra, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const unsigned char* pixel_in = &rgb[x * 3];
- unsigned char* pixel_out = &bgra[x * 4];
- pixel_out[0] = pixel_in[2];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[0];
- pixel_out[3] = 0xff;
+ PngDecoderState* state =
+ static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr));
+ DCHECK(state) << "LibPNG user transform pointer is NULL";
+
+ unsigned char* const end = data + row_info->rowbytes;
+ for (unsigned char* p = data; p < end; p += channels) {
+ uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p);
+ const unsigned char alpha = p[channels - 1];
+ if (alpha != 255) {
+ state->is_opaque = false;
+ *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]);
+ } else {
+ *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]);
+ }
Francois 2012/02/29 09:12:29 This is the libpng user transform which replaces t
}
}
Francois 2012/02/29 09:12:29 This converter's work is now done by libpng itself
@@ -227,11 +194,11 @@
PngDecoderState* state = static_cast<PngDecoderState*>(
png_get_progressive_ptr(png_ptr));
- int bit_depth, color_type, interlace_type, compression_type;
- int filter_type, channels;
+ int bit_depth, color_type, interlace_type;
png_uint_32 w, h;
+
png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
- &interlace_type, &compression_type, &filter_type);
+ &interlace_type, NULL, NULL);
// Bounds check. When the image is unreasonably big, we'll error out and
// end up back at the setjmp call when we set up decoding. "Unreasonably big"
@@ -242,22 +209,53 @@
static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
if (total_size > ((1 << 29) - 1))
longjmp(png_jmpbuf(png_ptr), 1);
+
state->width = static_cast<int>(w);
state->height = static_cast<int>(h);
+ // The following png_set_* calls have to be done in the order dictated by
+ // the libpng docs. This is why certain things are done outside of the
+ // switch, even though they look like they belong there.
+
// Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
if (color_type == PNG_COLOR_TYPE_PALETTE ||
(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
png_set_expand(png_ptr);
+ bool input_has_alpha = (color_type & PNG_COLOR_MASK_ALPHA);
+
// Transparency for paletted images.
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_expand(png_ptr);
+ input_has_alpha = true;
+ }
// Convert 16-bit to 8-bit.
if (bit_depth == 16)
png_set_strip_16(png_ptr);
+ // See comment about png_set_* call ordering, above.
+ if (state->output_format == PNGCodec::FORMAT_BGRA)
+ png_set_bgr(png_ptr);
+
+ switch (state->output_format) {
+ case PNGCodec::FORMAT_RGB:
+ state->output_channels = 3;
+ if (input_has_alpha)
+ png_set_strip_alpha(png_ptr);
+ break;
+ case PNGCodec::FORMAT_RGBA:
+ case PNGCodec::FORMAT_BGRA:
+ case PNGCodec::FORMAT_SkBitmap:
+ state->output_channels = 4;
+ if (!input_has_alpha)
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ break;
+ default:
+ NOTREACHED() << "Unknown output format";
+ break;
+ }
+
// Expand grayscale to RGB.
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
@@ -275,64 +273,18 @@
png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
}
+ // See comment about png_set_* call ordering, above.
+ if (state->output_format == PNGCodec::FORMAT_SkBitmap) {
+ png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia);
+ png_set_user_transform_info(png_ptr, state, 0, 0);
+ }
+
// Tell libpng to send us rows for interlaced pngs.
if (interlace_type == PNG_INTERLACE_ADAM7)
png_set_interlace_handling(png_ptr);
- // Update our info now
png_read_update_info(png_ptr, info_ptr);
- channels = png_get_channels(png_ptr, info_ptr);
- // Pick our row format converter necessary for this data.
- if (channels == 3) {
- switch (state->output_format) {
- case PNGCodec::FORMAT_RGB:
- state->row_converter = NULL; // no conversion necessary
- state->output_channels = 3;
- break;
- case PNGCodec::FORMAT_RGBA:
- state->row_converter = &ConvertRGBtoRGBA;
- state->output_channels = 4;
- break;
- case PNGCodec::FORMAT_BGRA:
- state->row_converter = &ConvertRGBtoBGRA;
- state->output_channels = 4;
- break;
- case PNGCodec::FORMAT_SkBitmap:
- state->row_converter = &ConvertRGBtoSkia;
- state->output_channels = 4;
- break;
- default:
- NOTREACHED() << "Unknown output format";
- break;
- }
- } else if (channels == 4) {
- switch (state->output_format) {
- case PNGCodec::FORMAT_RGB:
- state->row_converter = &ConvertRGBAtoRGB;
- state->output_channels = 3;
- break;
- case PNGCodec::FORMAT_RGBA:
- state->row_converter = NULL; // no conversion necessary
- state->output_channels = 4;
- break;
- case PNGCodec::FORMAT_BGRA:
- state->row_converter = &ConvertBetweenBGRAandRGBA;
- state->output_channels = 4;
- break;
- case PNGCodec::FORMAT_SkBitmap:
- state->row_converter = &ConvertRGBAtoSkia;
- state->output_channels = 4;
- break;
- default:
- NOTREACHED() << "Unknown output format";
- break;
- }
- } else {
- NOTREACHED() << "Unknown input channels";
- longjmp(png_jmpbuf(png_ptr), 1);
- }
-
if (state->bitmap) {
state->bitmap->setConfig(SkBitmap::kARGB_8888_Config,
state->width, state->height);
@@ -345,11 +297,11 @@
void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
png_uint_32 row_num, int pass) {
+ if (!new_row) return; // Interlaced image; row didn't change this pass.
tony 2012/02/29 18:57:57 Nit: Put the return on it's own line.
Francois 2012/03/01 07:53:15 Done.
+
PngDecoderState* state = static_cast<PngDecoderState*>(
png_get_progressive_ptr(png_ptr));
- DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is "
- "giving us interlaced data.";
if (static_cast<int>(row_num) > state->height) {
NOTREACHED() << "Invalid row";
return;
@@ -360,12 +312,9 @@
base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0));
else if (state->output)
base = &state->output->front();
-
unsigned char* dest = &base[state->width * state->output_channels * row_num];
- if (state->row_converter)
- state->row_converter(new_row, state->width, dest, &state->is_opaque);
- else
- memcpy(dest, new_row, state->width * state->output_channels);
+
+ png_progressive_combine_row(png_ptr, dest, new_row);
Francois 2012/02/29 09:12:29 This call merges the latest changes to the rows of
}
void DecodeEndCallback(png_struct* png_ptr, png_info* info) {

Powered by Google App Engine
This is Rietveld 408576698