diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-22 17:36:17 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-22 17:36:17 +0000 |
commit | 52d70a7c296f2eeef7343ed94375a7abad4a65dc (patch) | |
tree | 83f282358ee747ae897c440d781ade9d6a0730bc /app | |
parent | 52bf465e9d660330d267d5b29c36fe3e9ce1d905 (diff) | |
download | chromium_src-52d70a7c296f2eeef7343ed94375a7abad4a65dc.zip chromium_src-52d70a7c296f2eeef7343ed94375a7abad4a65dc.tar.gz chromium_src-52d70a7c296f2eeef7343ed94375a7abad4a65dc.tar.bz2 |
Make PNGCodec::Decode(...) not make an intermediary copy of the decoded data;
instead have it write directly to the returned SkBitmap.
BUG=http://crbug.com/24493
TEST=Perf should get better. On the perf trybot, tab_complex_theme_cold got an average of ~40ms better.
Review URL: http://codereview.chromium.org/305001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29780 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r-- | app/gfx/codec/png_codec.cc | 197 | ||||
-rw-r--r-- | app/gfx/codec/png_codec.h | 16 | ||||
-rw-r--r-- | app/gfx/codec/png_codec_unittest.cc | 3 | ||||
-rw-r--r-- | app/resource_bundle.cc | 17 |
4 files changed, 160 insertions, 73 deletions
diff --git a/app/gfx/codec/png_codec.cc b/app/gfx/codec/png_codec.cc index 41057a0..b0799c6 100644 --- a/app/gfx/codec/png_codec.cc +++ b/app/gfx/codec/png_codec.cc @@ -8,6 +8,7 @@ #include "base/scoped_ptr.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkUnPreMultiply.h" +#include "third_party/skia/include/core/SkColorPriv.h" extern "C" { #if defined(USE_SYSTEM_LIBPNG) @@ -23,7 +24,7 @@ namespace { // Converts BGRA->RGBA and RGBA->BGRA. void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, - unsigned char* output) { + unsigned char* output, bool* is_opaque) { for (int x = 0; x < pixel_width; x++) { const unsigned char* pixel_in = &input[x * 4]; unsigned char* pixel_out = &output[x * 4]; @@ -35,7 +36,7 @@ void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, } void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, - unsigned char* rgb) { + unsigned char* rgb, bool* is_opaque) { for (int x = 0; x < pixel_width; x++) { const unsigned char* pixel_in = &rgba[x * 4]; unsigned char* pixel_out = &rgb[x * 3]; @@ -45,6 +46,34 @@ void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, } } +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]); + } + } +} + } // namespace // Decoder -------------------------------------------------------------------- @@ -61,9 +90,12 @@ const double kInverseGamma = 1.0 / kDefaultGamma; class PngDecoderState { public: + // Output is a vector<unsigned char>. PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o) : output_format(ofmt), output_channels(0), + bitmap(NULL), + is_opaque(true), output(o), row_converter(NULL), width(0), @@ -71,14 +103,37 @@ class PngDecoderState { done(false) { } + // Output is an SkBitmap. + explicit PngDecoderState(SkBitmap* skbitmap) + : output_format(PNGCodec::FORMAT_SkBitmap), + output_channels(0), + bitmap(skbitmap), + is_opaque(true), + output(NULL), + row_converter(NULL), + width(0), + height(0), + done(false) { + } + PNGCodec::ColorFormat output_format; int output_channels; + // An incoming SkBitmap to write to. If NULL, we write to output instead. + SkBitmap* bitmap; + + // Used during the reading of an SkBitmap. Defaults to true until we see a + // pixel with anything other than an alpha of 255. + bool is_opaque; + + // The other way to decode output, where we write into an intermediary buffer + // 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); + 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; @@ -92,7 +147,7 @@ class PngDecoderState { }; void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, - unsigned char* rgba) { + 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]; @@ -104,7 +159,7 @@ void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, } void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width, - unsigned char* bgra) { + 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]; @@ -192,6 +247,10 @@ void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { 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; @@ -210,6 +269,10 @@ void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { 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; @@ -219,7 +282,14 @@ void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { longjmp(png_ptr->jmpbuf, 1); } - state->output->resize(state->width * state->output_channels * state->height); + if (state->bitmap) { + state->bitmap->setConfig(SkBitmap::kARGB_8888_Config, + state->width, state->height); + state->bitmap->allocPixels(); + } else if (state->output) { + state->output->resize( + state->width * state->output_channels * state->height); + } } void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, @@ -234,10 +304,15 @@ void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, return; } - unsigned char* dest = &(*state->output)[ - state->width * state->output_channels * row_num]; + unsigned char* base = NULL; + if (state->bitmap) + 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->row_converter(new_row, state->width, dest, &state->is_opaque); else memcpy(dest, new_row, state->width * state->output_channels); } @@ -265,12 +340,8 @@ class PngReadStructDestroyer { png_info** pi_; }; -} // namespace - -// static -bool PNGCodec::Decode(const unsigned char* input, size_t input_size, - ColorFormat format, std::vector<unsigned char>* output, - int* w, int* h) { +bool BuildPNGStruct(const unsigned char* input, size_t input_size, + png_struct** png_ptr, png_info** info_ptr) { if (input_size < 8) return false; // Input data too small to be a png @@ -278,19 +349,33 @@ bool PNGCodec::Decode(const unsigned char* input, size_t input_size, if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0) return false; - png_struct* png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - png_voidp_NULL, - png_error_ptr_NULL, - png_error_ptr_NULL); - if (!png_ptr) + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + png_voidp_NULL, + png_error_ptr_NULL, + png_error_ptr_NULL); + if (!*png_ptr) return false; - png_info* info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); + *info_ptr = png_create_info_struct(*png_ptr); + if (!*info_ptr) { + png_destroy_read_struct(png_ptr, NULL, NULL); return false; } + return true; +} + +} // namespace + +// static +bool PNGCodec::Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector<unsigned char>* output, + int* w, int* h) { + png_struct* png_ptr = NULL; + png_info* info_ptr = NULL; + if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) + return false; + PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); if (setjmp(png_jmpbuf(png_ptr))) { // The destroyer will ensure that the structures are cleaned up in this @@ -321,38 +406,39 @@ bool PNGCodec::Decode(const unsigned char* input, size_t input_size, } // static -bool PNGCodec::Decode(const std::vector<unsigned char>* data, +bool PNGCodec::Decode(const unsigned char* input, size_t input_size, SkBitmap* bitmap) { DCHECK(bitmap); - if (!data || data->empty()) + png_struct* png_ptr = NULL; + png_info* info_ptr = NULL; + if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) + return false; + + PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); + if (setjmp(png_jmpbuf(png_ptr))) { + // The destroyer will ensure that the structures are cleaned up in this + // case, even though we may get here as a jump from random parts of the + // PNG library called below. return false; - int width, height; - std::vector<unsigned char> decoded_data; - if (PNGCodec::Decode(&data->front(), data->size(), PNGCodec::FORMAT_BGRA, - &decoded_data, &width, &height)) { - bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); - bitmap->allocPixels(); - unsigned char* bitmap_data = - reinterpret_cast<unsigned char*>(bitmap->getAddr32(0, 0)); - for (int i = width * height * 4 - 4; i >= 0; i -= 4) { - unsigned char alpha = decoded_data[i + 3]; - if (alpha != 0 && alpha != 255) { - SkColor premultiplied = SkPreMultiplyARGB(alpha, - decoded_data[i], decoded_data[i + 1], decoded_data[i + 2]); - bitmap_data[i + 3] = alpha; - bitmap_data[i] = SkColorGetR(premultiplied); - bitmap_data[i + 1] = SkColorGetG(premultiplied); - bitmap_data[i + 2] = SkColorGetB(premultiplied); - } else { - bitmap_data[i + 3] = alpha; - bitmap_data[i] = decoded_data[i]; - bitmap_data[i + 1] = decoded_data[i + 1]; - bitmap_data[i + 2] = decoded_data[i + 2]; - } - } - return true; } - return false; + + PngDecoderState state(bitmap); + + png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, + &DecodeRowCallback, &DecodeEndCallback); + png_process_data(png_ptr, + info_ptr, + const_cast<unsigned char*>(input), + input_size); + + if (!state.done) { + return false; + } + + // Set the bitmap's opaqueness based on what we saw. + bitmap->setIsOpaque(state.is_opaque); + + return true; } // static @@ -390,7 +476,7 @@ namespace { // Passed around as the io_ptr in the png structs so our callbacks know where // to write data. struct PngEncoderState { - PngEncoderState(std::vector<unsigned char>* o) : out(o) {} + explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {} std::vector<unsigned char>* out; }; @@ -405,7 +491,7 @@ void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) { } void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width, - unsigned char* rgb) { + unsigned char* rgb, bool* is_opaque) { for (int x = 0; x < pixel_width; x++) { const unsigned char* pixel_in = &bgra[x * 4]; unsigned char* pixel_out = &rgb[x * 3]; @@ -440,7 +526,8 @@ bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, std::vector<unsigned char>* output) { // Run to convert an input row into the output row format, NULL means no // conversion is necessary. - void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL; + void (*converter)(const unsigned char* in, int w, unsigned char* out, + bool* is_opaque) = NULL; int input_color_components, output_color_components; int png_output_color_type; @@ -524,7 +611,7 @@ bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, // Needs conversion using a separate buffer. unsigned char* row = new unsigned char[w * output_color_components]; for (int y = 0; y < h; y ++) { - converter(&input[y * row_byte_width], w, row); + converter(&input[y * row_byte_width], w, row, NULL); png_write_row(png_ptr, row); } delete[] row; diff --git a/app/gfx/codec/png_codec.h b/app/gfx/codec/png_codec.h index 1e07632..6460701 100644 --- a/app/gfx/codec/png_codec.h +++ b/app/gfx/codec/png_codec.h @@ -31,7 +31,11 @@ class PNGCodec { // 4 bytes per pixel, in BGRA order in memory regardless of endianness. // This is the default Windows DIB order. - FORMAT_BGRA + FORMAT_BGRA, + + // 4 bytes per pixel, in pre-multiplied kARGB_8888_Config format. For use + // with directly writing to a skia bitmap. + FORMAT_SkBitmap }; // Encodes the given raw 'input' data, with each pixel being represented as @@ -75,12 +79,16 @@ class PNGCodec { ColorFormat format, std::vector<unsigned char>* output, int* w, int* h); - // A convenience function for decoding PNGs as previously encoded by the PNG - // encoder. Chrome encodes png in the format PNGCodec::FORMAT_BGRA. + // Decodes the PNG data directly into the passed in SkBitmap. This is + // significantly faster than the vector<unsigned char> version of Decode() + // above when dealing with PNG files that are >500K, which a lot of theme + // images are. (There are a lot of themes that have a NTP image of about ~1 + // megabyte, and those require a 7-10 megabyte side buffer.) // // Returns true if data is non-null and can be decoded as a png, false // otherwise. - static bool Decode(const std::vector<unsigned char>* data, SkBitmap* icon); + static bool Decode(const unsigned char* input, size_t input_size, + SkBitmap* bitmap); // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned // SkBitmap. diff --git a/app/gfx/codec/png_codec_unittest.cc b/app/gfx/codec/png_codec_unittest.cc index d16f519..37734f8 100644 --- a/app/gfx/codec/png_codec_unittest.cc +++ b/app/gfx/codec/png_codec_unittest.cc @@ -234,7 +234,8 @@ TEST(PNGCodec, EncodeBGRASkBitmap) { // Decode the encoded string. SkBitmap decoded_bitmap; - EXPECT_TRUE(PNGCodec::Decode(&encoded, &decoded_bitmap)); + EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(), + &decoded_bitmap)); // Compare the original bitmap and the output bitmap. We use ColorsClose // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication diff --git a/app/resource_bundle.cc b/app/resource_bundle.cc index 7261dfd..28674a8 100644 --- a/app/resource_bundle.cc +++ b/app/resource_bundle.cc @@ -69,27 +69,18 @@ void ResourceBundle::FreeImages() { /* static */ SkBitmap* ResourceBundle::LoadBitmap(DataHandle data_handle, int resource_id) { - std::vector<unsigned char> png_data; - scoped_refptr<RefCountedMemory> memory( LoadResourceBytes(data_handle, resource_id)); if (!memory) return NULL; - // Decode the PNG. - int image_width; - int image_height; - if (!gfx::PNGCodec::Decode( - memory->front(), memory->size(), - gfx::PNGCodec::FORMAT_BGRA, - &png_data, &image_width, &image_height)) { - NOTREACHED() << "Unable to decode image resource " << resource_id; + SkBitmap bitmap; + if (!gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) { + NOTREACHED() << "Unable to decode theme image resource " << resource_id; return NULL; } - return gfx::PNGCodec::CreateSkBitmapFromBGRAFormat(png_data, - image_width, - image_height); + return new SkBitmap(bitmap); } std::string ResourceBundle::GetDataResource(int resource_id) { |