| Index: ui/gfx/codec/png_codec.cc
|
| ===================================================================
|
| --- ui/gfx/codec/png_codec.cc (revision 125325)
|
| +++ 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]);
|
| - }
|
| - }
|
| -}
|
| -
|
| 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);
|
| -
|
| // 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;
|
| - }
|
| -}
|
| +// 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]);
|
| + }
|
| }
|
| }
|
|
|
| @@ -228,7 +195,7 @@
|
| png_get_progressive_ptr(png_ptr));
|
|
|
| int bit_depth, color_type, interlace_type, compression_type;
|
| - int filter_type, channels;
|
| + int filter_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);
|
| @@ -245,94 +212,99 @@
|
| 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. Please take care if you have to move any of them. This
|
| + // is also 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);
|
|
|
| + // The '!= 0' is for silencing a Windows compiler warning.
|
| + bool input_has_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
|
| +
|
| // 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);
|
|
|
| - // Expand grayscale to RGB.
|
| - if (color_type == PNG_COLOR_TYPE_GRAY ||
|
| - color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
| - png_set_gray_to_rgb(png_ptr);
|
| -
|
| - // Deal with gamma and keep it under our control.
|
| - double gamma;
|
| - if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
|
| - if (gamma <= 0.0 || gamma > kMaxGamma) {
|
| - gamma = kInverseGamma;
|
| - png_set_gAMA(png_ptr, info_ptr, gamma);
|
| - }
|
| - png_set_gamma(png_ptr, kDefaultGamma, gamma);
|
| - } else {
|
| - png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
|
| - }
|
| -
|
| - // 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) {
|
| + if (!input_has_alpha) {
|
| 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;
|
| + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
|
| break;
|
| case PNGCodec::FORMAT_BGRA:
|
| - state->row_converter = &ConvertRGBtoBGRA;
|
| state->output_channels = 4;
|
| + png_set_bgr(png_ptr);
|
| + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
|
| break;
|
| case PNGCodec::FORMAT_SkBitmap:
|
| - state->row_converter = &ConvertRGBtoSkia;
|
| state->output_channels = 4;
|
| + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
|
| break;
|
| - default:
|
| - NOTREACHED() << "Unknown output format";
|
| - break;
|
| }
|
| - } else if (channels == 4) {
|
| + } else {
|
| switch (state->output_format) {
|
| case PNGCodec::FORMAT_RGB:
|
| - state->row_converter = &ConvertRGBAtoRGB;
|
| state->output_channels = 3;
|
| + png_set_strip_alpha(png_ptr);
|
| 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;
|
| + png_set_bgr(png_ptr);
|
| break;
|
| case PNGCodec::FORMAT_SkBitmap:
|
| - state->row_converter = &ConvertRGBAtoSkia;
|
| state->output_channels = 4;
|
| 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)
|
| + png_set_gray_to_rgb(png_ptr);
|
| +
|
| + // Deal with gamma and keep it under our control.
|
| + double gamma;
|
| + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
|
| + if (gamma <= 0.0 || gamma > kMaxGamma) {
|
| + gamma = kInverseGamma;
|
| + png_set_gAMA(png_ptr, info_ptr, gamma);
|
| + }
|
| + png_set_gamma(png_ptr, kDefaultGamma, gamma);
|
| } else {
|
| - NOTREACHED() << "Unknown input channels";
|
| - longjmp(png_jmpbuf(png_ptr), 1);
|
| + png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
|
| }
|
|
|
| + // Setting the user transforms here (as opposed to inside the switch above)
|
| + // because all png_set_* calls need to be done in the specific order
|
| + // mandated by libpng.
|
| + 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);
|
| +
|
| + png_read_update_info(png_ptr, info_ptr);
|
| +
|
| if (state->bitmap) {
|
| state->bitmap->setConfig(SkBitmap::kARGB_8888_Config,
|
| state->width, state->height);
|
| @@ -345,11 +317,12 @@
|
|
|
| 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.
|
| +
|
| 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;
|
| @@ -362,10 +335,7 @@
|
| 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);
|
| }
|
|
|
| void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
|
|
|