Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/codec/png_codec.h" | 5 #include "ui/gfx/codec/png_codec.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "ui/gfx/size.h" | 10 #include "ui/gfx/size.h" |
| 11 #include "third_party/skia/include/core/SkBitmap.h" | 11 #include "third_party/skia/include/core/SkBitmap.h" |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 23 #include <zlib.h> | 23 #include <zlib.h> |
| 24 #else | 24 #else |
| 25 #include "third_party/zlib/zlib.h" | 25 #include "third_party/zlib/zlib.h" |
| 26 #endif | 26 #endif |
| 27 } | 27 } |
| 28 | 28 |
| 29 namespace gfx { | 29 namespace gfx { |
| 30 | 30 |
| 31 namespace { | 31 namespace { |
| 32 | 32 |
| 33 // Converts BGRA->RGBA and RGBA->BGRA. | |
| 34 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, | |
| 35 unsigned char* output, bool* is_opaque) { | |
| 36 for (int x = 0; x < pixel_width; x++) { | |
| 37 const unsigned char* pixel_in = &input[x * 4]; | |
| 38 unsigned char* pixel_out = &output[x * 4]; | |
| 39 pixel_out[0] = pixel_in[2]; | |
| 40 pixel_out[1] = pixel_in[1]; | |
| 41 pixel_out[2] = pixel_in[0]; | |
| 42 pixel_out[3] = pixel_in[3]; | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, | |
| 47 unsigned char* rgb, bool* is_opaque) { | |
| 48 for (int x = 0; x < pixel_width; x++) { | |
| 49 const unsigned char* pixel_in = &rgba[x * 4]; | |
| 50 unsigned char* pixel_out = &rgb[x * 3]; | |
| 51 pixel_out[0] = pixel_in[0]; | |
| 52 pixel_out[1] = pixel_in[1]; | |
| 53 pixel_out[2] = pixel_in[2]; | |
| 54 } | |
| 55 } | |
|
Francois
2012/02/28 15:00:36
The work done by this converter and the one above
| |
| 56 | |
| 57 void ConvertRGBtoSkia(const unsigned char* rgb, int pixel_width, | |
| 58 unsigned char* rgba, bool* is_opaque) { | |
| 59 for (int x = 0; x < pixel_width; x++) { | |
| 60 const unsigned char* pixel_in = &rgb[x * 3]; | |
| 61 uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x * 4]); | |
| 62 *pixel_out = SkPackARGB32(0xFF, pixel_in[0], pixel_in[1], pixel_in[2]); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 void ConvertRGBAtoSkia(const unsigned char* rgb, int pixel_width, | |
| 67 unsigned char* rgba, bool* is_opaque) { | |
| 68 int total_length = pixel_width * 4; | |
| 69 for (int x = 0; x < total_length; x += 4) { | |
| 70 const unsigned char* pixel_in = &rgb[x]; | |
| 71 uint32_t* pixel_out = reinterpret_cast<uint32_t*>(&rgba[x]); | |
| 72 | |
| 73 unsigned char alpha = pixel_in[3]; | |
| 74 if (alpha != 255) { | |
| 75 *is_opaque = false; | |
| 76 *pixel_out = SkPreMultiplyARGB(alpha, | |
| 77 pixel_in[0], pixel_in[1], pixel_in[2]); | |
| 78 } else { | |
| 79 *pixel_out = SkPackARGB32(alpha, | |
| 80 pixel_in[0], pixel_in[1], pixel_in[2]); | |
| 81 } | |
| 82 } | |
| 83 } | |
|
Francois
2012/02/28 15:00:36
The work done by this converter and the one above
| |
| 84 | |
| 85 void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width, | 33 void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width, |
| 86 unsigned char* rgb, bool* is_opaque) { | 34 unsigned char* rgb, bool* is_opaque) { |
| 87 for (int x = 0; x < pixel_width; x++) { | 35 for (int x = 0; x < pixel_width; x++) { |
| 88 const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]); | 36 const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]); |
| 89 unsigned char* pixel_out = &rgb[x * 3]; | 37 unsigned char* pixel_out = &rgb[x * 3]; |
| 90 | 38 |
| 91 int alpha = SkGetPackedA32(pixel_in); | 39 int alpha = SkGetPackedA32(pixel_in); |
| 92 if (alpha != 0 && alpha != 255) { | 40 if (alpha != 0 && alpha != 255) { |
| 93 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in); | 41 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in); |
| 94 pixel_out[0] = SkColorGetR(unmultiplied); | 42 pixel_out[0] = SkColorGetR(unmultiplied); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 141 | 89 |
| 142 class PngDecoderState { | 90 class PngDecoderState { |
| 143 public: | 91 public: |
| 144 // Output is a vector<unsigned char>. | 92 // Output is a vector<unsigned char>. |
| 145 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o) | 93 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o) |
| 146 : output_format(ofmt), | 94 : output_format(ofmt), |
| 147 output_channels(0), | 95 output_channels(0), |
| 148 bitmap(NULL), | 96 bitmap(NULL), |
| 149 is_opaque(true), | 97 is_opaque(true), |
| 150 output(o), | 98 output(o), |
| 151 row_converter(NULL), | |
| 152 width(0), | 99 width(0), |
| 153 height(0), | 100 height(0), |
| 154 done(false) { | 101 done(false) { |
| 155 } | 102 } |
| 156 | 103 |
| 157 // Output is an SkBitmap. | 104 // Output is an SkBitmap. |
| 158 explicit PngDecoderState(SkBitmap* skbitmap) | 105 explicit PngDecoderState(SkBitmap* skbitmap) |
| 159 : output_format(PNGCodec::FORMAT_SkBitmap), | 106 : output_format(PNGCodec::FORMAT_SkBitmap), |
| 160 output_channels(0), | 107 output_channels(0), |
| 161 bitmap(skbitmap), | 108 bitmap(skbitmap), |
| 162 is_opaque(true), | 109 is_opaque(true), |
| 163 output(NULL), | 110 output(NULL), |
| 164 row_converter(NULL), | |
| 165 width(0), | 111 width(0), |
| 166 height(0), | 112 height(0), |
| 167 done(false) { | 113 done(false) { |
| 168 } | 114 } |
| 169 | 115 |
| 170 PNGCodec::ColorFormat output_format; | 116 PNGCodec::ColorFormat output_format; |
| 171 int output_channels; | 117 int output_channels; |
| 172 | 118 |
| 173 // An incoming SkBitmap to write to. If NULL, we write to output instead. | 119 // An incoming SkBitmap to write to. If NULL, we write to output instead. |
| 174 SkBitmap* bitmap; | 120 SkBitmap* bitmap; |
| 175 | 121 |
| 176 // Used during the reading of an SkBitmap. Defaults to true until we see a | 122 // Used during the reading of an SkBitmap. Defaults to true until we see a |
| 177 // pixel with anything other than an alpha of 255. | 123 // pixel with anything other than an alpha of 255. |
| 178 bool is_opaque; | 124 bool is_opaque; |
| 179 | 125 |
| 180 // The other way to decode output, where we write into an intermediary buffer | 126 // The other way to decode output, where we write into an intermediary buffer |
| 181 // instead of directly to an SkBitmap. | 127 // instead of directly to an SkBitmap. |
| 182 std::vector<unsigned char>* output; | 128 std::vector<unsigned char>* output; |
| 183 | 129 |
| 184 // Called to convert a row from the library to the correct output format. | |
| 185 // When NULL, no conversion is necessary. | |
| 186 void (*row_converter)(const unsigned char* in, int w, unsigned char* out, | |
| 187 bool* is_opaque); | |
|
Francois
2012/02/28 15:00:36
All decode converters have been removed in favor o
| |
| 188 | |
| 189 // Size of the image, set in the info callback. | 130 // Size of the image, set in the info callback. |
| 190 int width; | 131 int width; |
| 191 int height; | 132 int height; |
| 192 | 133 |
| 193 // Set to true when we've found the end of the data. | 134 // Set to true when we've found the end of the data. |
| 194 bool done; | 135 bool done; |
| 195 | 136 |
| 196 private: | 137 private: |
| 197 DISALLOW_COPY_AND_ASSIGN(PngDecoderState); | 138 DISALLOW_COPY_AND_ASSIGN(PngDecoderState); |
| 198 }; | 139 }; |
| 199 | 140 |
| 200 void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, | 141 // User transform (passed to libpng) which converts a row decoded by libpng to |
| 201 unsigned char* rgba, bool* is_opaque) { | 142 // Skia format. Expects the row to have 4 channels, otherwise there won't be |
| 202 for (int x = 0; x < pixel_width; x++) { | 143 // enough room in |data|. |
| 203 const unsigned char* pixel_in = &rgb[x * 3]; | 144 void ConvertRGBARowToSkia(png_structp png_ptr, |
| 204 unsigned char* pixel_out = &rgba[x * 4]; | 145 png_row_infop row_info, |
| 205 pixel_out[0] = pixel_in[0]; | 146 png_bytep data) { |
| 206 pixel_out[1] = pixel_in[1]; | 147 const int channels = row_info->channels; |
| 207 pixel_out[2] = pixel_in[2]; | 148 DCHECK_EQ(channels, 4); |
| 208 pixel_out[3] = 0xff; | 149 |
|
Francois
2012/02/28 15:00:36
This converter's work is now done by libpng itself
| |
| 150 PngDecoderState* state = | |
| 151 static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr)); | |
| 152 DCHECK(state) << "LibPNG user transform pointer is NULL"; | |
| 153 | |
| 154 unsigned char* const end = data + row_info->rowbytes; | |
| 155 for (unsigned char* p = data; p < end; p += channels) { | |
| 156 uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p); | |
| 157 const unsigned char alpha = p[channels - 1]; | |
| 158 if (alpha != 255) { | |
| 159 state->is_opaque = false; | |
| 160 *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]); | |
| 161 } else { | |
| 162 *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]); | |
| 163 } | |
|
Francois
2012/02/28 15:00:36
Replaces the converters called ConvertRGBtoSkia an
| |
| 209 } | 164 } |
| 210 } | 165 } |
| 211 | 166 |
| 212 void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width, | |
| 213 unsigned char* bgra, bool* is_opaque) { | |
| 214 for (int x = 0; x < pixel_width; x++) { | |
| 215 const unsigned char* pixel_in = &rgb[x * 3]; | |
| 216 unsigned char* pixel_out = &bgra[x * 4]; | |
| 217 pixel_out[0] = pixel_in[2]; | |
| 218 pixel_out[1] = pixel_in[1]; | |
| 219 pixel_out[2] = pixel_in[0]; | |
| 220 pixel_out[3] = 0xff; | |
| 221 } | |
| 222 } | |
| 223 | |
|
Francois
2012/02/28 15:00:36
This converter's work is now done by libpng itself
| |
| 224 // Called when the png header has been read. This code is based on the WebKit | 167 // Called when the png header has been read. This code is based on the WebKit |
| 225 // PNGImageDecoder | 168 // PNGImageDecoder |
| 226 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { | 169 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { |
| 227 PngDecoderState* state = static_cast<PngDecoderState*>( | 170 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 228 png_get_progressive_ptr(png_ptr)); | 171 png_get_progressive_ptr(png_ptr)); |
| 229 | 172 |
| 230 int bit_depth, color_type, interlace_type, compression_type; | 173 int bit_depth, color_type, interlace_type; |
| 231 int filter_type, channels; | |
| 232 png_uint_32 w, h; | 174 png_uint_32 w, h; |
| 175 | |
| 233 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, | 176 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, |
| 234 &interlace_type, &compression_type, &filter_type); | 177 &interlace_type, NULL, NULL); |
| 235 | 178 |
| 236 // Bounds check. When the image is unreasonably big, we'll error out and | 179 // Bounds check. When the image is unreasonably big, we'll error out and |
| 237 // end up back at the setjmp call when we set up decoding. "Unreasonably big" | 180 // end up back at the setjmp call when we set up decoding. "Unreasonably big" |
| 238 // means "big enough that w * h * 32bpp might overflow an int"; we choose this | 181 // means "big enough that w * h * 32bpp might overflow an int"; we choose this |
| 239 // threshold to match WebKit and because a number of places in code assume | 182 // threshold to match WebKit and because a number of places in code assume |
| 240 // that an image's size (in bytes) fits in a (signed) int. | 183 // that an image's size (in bytes) fits in a (signed) int. |
| 241 unsigned long long total_size = | 184 unsigned long long total_size = |
| 242 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h); | 185 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h); |
| 243 if (total_size > ((1 << 29) - 1)) | 186 if (total_size > ((1 << 29) - 1)) |
| 244 longjmp(png_jmpbuf(png_ptr), 1); | 187 longjmp(png_jmpbuf(png_ptr), 1); |
| 188 | |
| 245 state->width = static_cast<int>(w); | 189 state->width = static_cast<int>(w); |
| 246 state->height = static_cast<int>(h); | 190 state->height = static_cast<int>(h); |
| 247 | 191 |
| 192 // The order of the following png_set_* calls have to be done in the order | |
| 193 // dictated by the libpng docs. This is why certain things are done outside | |
| 194 // of the switch, even though they look like they belong there. | |
| 195 | |
| 248 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. | 196 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. |
| 249 if (color_type == PNG_COLOR_TYPE_PALETTE || | 197 if (color_type == PNG_COLOR_TYPE_PALETTE || |
| 250 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) | 198 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) |
| 251 png_set_expand(png_ptr); | 199 png_set_expand(png_ptr); |
| 252 | 200 |
| 201 bool input_has_alpha = (color_type & PNG_COLOR_MASK_ALPHA); | |
| 202 | |
| 253 // Transparency for paletted images. | 203 // Transparency for paletted images. |
| 254 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) | 204 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
| 255 png_set_expand(png_ptr); | 205 png_set_expand(png_ptr); |
| 206 input_has_alpha = true; | |
| 207 } | |
| 256 | 208 |
| 257 // Convert 16-bit to 8-bit. | 209 // Convert 16-bit to 8-bit. |
| 258 if (bit_depth == 16) | 210 if (bit_depth == 16) |
| 259 png_set_strip_16(png_ptr); | 211 png_set_strip_16(png_ptr); |
| 260 | 212 |
| 213 // See comment about png_set_* call ordering, above. | |
| 214 if (state->output_format == PNGCodec::FORMAT_BGRA) | |
| 215 png_set_bgr(png_ptr); | |
| 216 | |
| 217 switch (state->output_format) { | |
| 218 case PNGCodec::FORMAT_RGB: | |
| 219 state->output_channels = 3; | |
| 220 if (input_has_alpha) | |
| 221 png_set_strip_alpha(png_ptr); | |
| 222 break; | |
| 223 case PNGCodec::FORMAT_RGBA: | |
| 224 case PNGCodec::FORMAT_BGRA: | |
| 225 case PNGCodec::FORMAT_SkBitmap: | |
| 226 state->output_channels = 4; | |
| 227 if (!input_has_alpha) | |
| 228 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); | |
| 229 break; | |
| 230 default: | |
| 231 NOTREACHED() << "Unknown output format"; | |
| 232 break; | |
| 233 } | |
| 234 | |
| 261 // Expand grayscale to RGB. | 235 // Expand grayscale to RGB. |
| 262 if (color_type == PNG_COLOR_TYPE_GRAY || | 236 if (color_type == PNG_COLOR_TYPE_GRAY || |
| 263 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) | 237 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) |
| 264 png_set_gray_to_rgb(png_ptr); | 238 png_set_gray_to_rgb(png_ptr); |
| 265 | 239 |
| 266 // Deal with gamma and keep it under our control. | 240 // Deal with gamma and keep it under our control. |
| 267 double gamma; | 241 double gamma; |
| 268 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { | 242 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { |
| 269 if (gamma <= 0.0 || gamma > kMaxGamma) { | 243 if (gamma <= 0.0 || gamma > kMaxGamma) { |
| 270 gamma = kInverseGamma; | 244 gamma = kInverseGamma; |
| 271 png_set_gAMA(png_ptr, info_ptr, gamma); | 245 png_set_gAMA(png_ptr, info_ptr, gamma); |
| 272 } | 246 } |
| 273 png_set_gamma(png_ptr, kDefaultGamma, gamma); | 247 png_set_gamma(png_ptr, kDefaultGamma, gamma); |
| 274 } else { | 248 } else { |
| 275 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); | 249 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); |
| 276 } | 250 } |
| 277 | 251 |
| 252 // See comment about png_set_* call ordering, above. | |
| 253 if (state->output_format == PNGCodec::FORMAT_SkBitmap) { | |
| 254 png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia); | |
| 255 png_set_user_transform_info(png_ptr, state, 0, 0); | |
| 256 } | |
| 257 | |
| 278 // Tell libpng to send us rows for interlaced pngs. | 258 // Tell libpng to send us rows for interlaced pngs. |
| 279 if (interlace_type == PNG_INTERLACE_ADAM7) | 259 if (interlace_type == PNG_INTERLACE_ADAM7) |
| 280 png_set_interlace_handling(png_ptr); | 260 png_set_interlace_handling(png_ptr); |
| 281 | 261 |
| 282 // Update our info now | |
| 283 png_read_update_info(png_ptr, info_ptr); | 262 png_read_update_info(png_ptr, info_ptr); |
| 284 channels = png_get_channels(png_ptr, info_ptr); | |
| 285 | |
| 286 // Pick our row format converter necessary for this data. | |
| 287 if (channels == 3) { | |
| 288 switch (state->output_format) { | |
| 289 case PNGCodec::FORMAT_RGB: | |
| 290 state->row_converter = NULL; // no conversion necessary | |
| 291 state->output_channels = 3; | |
| 292 break; | |
| 293 case PNGCodec::FORMAT_RGBA: | |
| 294 state->row_converter = &ConvertRGBtoRGBA; | |
| 295 state->output_channels = 4; | |
| 296 break; | |
| 297 case PNGCodec::FORMAT_BGRA: | |
| 298 state->row_converter = &ConvertRGBtoBGRA; | |
| 299 state->output_channels = 4; | |
| 300 break; | |
| 301 case PNGCodec::FORMAT_SkBitmap: | |
| 302 state->row_converter = &ConvertRGBtoSkia; | |
| 303 state->output_channels = 4; | |
| 304 break; | |
| 305 default: | |
| 306 NOTREACHED() << "Unknown output format"; | |
| 307 break; | |
| 308 } | |
| 309 } else if (channels == 4) { | |
| 310 switch (state->output_format) { | |
| 311 case PNGCodec::FORMAT_RGB: | |
| 312 state->row_converter = &ConvertRGBAtoRGB; | |
| 313 state->output_channels = 3; | |
| 314 break; | |
| 315 case PNGCodec::FORMAT_RGBA: | |
| 316 state->row_converter = NULL; // no conversion necessary | |
| 317 state->output_channels = 4; | |
| 318 break; | |
| 319 case PNGCodec::FORMAT_BGRA: | |
| 320 state->row_converter = &ConvertBetweenBGRAandRGBA; | |
| 321 state->output_channels = 4; | |
| 322 break; | |
| 323 case PNGCodec::FORMAT_SkBitmap: | |
| 324 state->row_converter = &ConvertRGBAtoSkia; | |
| 325 state->output_channels = 4; | |
| 326 break; | |
| 327 default: | |
| 328 NOTREACHED() << "Unknown output format"; | |
| 329 break; | |
| 330 } | |
| 331 } else { | |
| 332 NOTREACHED() << "Unknown input channels"; | |
| 333 longjmp(png_jmpbuf(png_ptr), 1); | |
| 334 } | |
| 335 | 263 |
| 336 if (state->bitmap) { | 264 if (state->bitmap) { |
| 337 state->bitmap->setConfig(SkBitmap::kARGB_8888_Config, | 265 state->bitmap->setConfig(SkBitmap::kARGB_8888_Config, |
| 338 state->width, state->height); | 266 state->width, state->height); |
| 339 state->bitmap->allocPixels(); | 267 state->bitmap->allocPixels(); |
| 340 } else if (state->output) { | 268 } else if (state->output) { |
| 341 state->output->resize( | 269 state->output->resize( |
| 342 state->width * state->output_channels * state->height); | 270 state->width * state->output_channels * state->height); |
| 343 } | 271 } |
| 344 } | 272 } |
| 345 | 273 |
| 346 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, | 274 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, |
| 347 png_uint_32 row_num, int pass) { | 275 png_uint_32 row_num, int pass) { |
| 276 if (!new_row) return; // Interlaced image; row didn't change this pass. | |
| 277 | |
| 348 PngDecoderState* state = static_cast<PngDecoderState*>( | 278 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 349 png_get_progressive_ptr(png_ptr)); | 279 png_get_progressive_ptr(png_ptr)); |
| 350 | 280 |
| 351 DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is " | |
| 352 "giving us interlaced data."; | |
| 353 if (static_cast<int>(row_num) > state->height) { | 281 if (static_cast<int>(row_num) > state->height) { |
| 354 NOTREACHED() << "Invalid row"; | 282 NOTREACHED() << "Invalid row"; |
| 355 return; | 283 return; |
| 356 } | 284 } |
| 357 | 285 |
| 358 unsigned char* base = NULL; | 286 unsigned char* base = NULL; |
| 359 if (state->bitmap) | 287 if (state->bitmap) |
| 360 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0)); | 288 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0)); |
| 361 else if (state->output) | 289 else if (state->output) |
| 362 base = &state->output->front(); | 290 base = &state->output->front(); |
| 291 unsigned char* dest = &base[state->width * state->output_channels * row_num]; | |
| 363 | 292 |
| 364 unsigned char* dest = &base[state->width * state->output_channels * row_num]; | 293 png_progressive_combine_row(png_ptr, dest, new_row); |
|
Francois
2012/02/28 15:00:36
This call merges the latest changes to the rows of
| |
| 365 if (state->row_converter) | |
| 366 state->row_converter(new_row, state->width, dest, &state->is_opaque); | |
| 367 else | |
| 368 memcpy(dest, new_row, state->width * state->output_channels); | |
| 369 } | 294 } |
| 370 | 295 |
| 371 void DecodeEndCallback(png_struct* png_ptr, png_info* info) { | 296 void DecodeEndCallback(png_struct* png_ptr, png_info* info) { |
| 372 PngDecoderState* state = static_cast<PngDecoderState*>( | 297 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 373 png_get_progressive_ptr(png_ptr)); | 298 png_get_progressive_ptr(png_ptr)); |
| 374 | 299 |
| 375 // Mark the image as complete, this will tell the Decode function that we | 300 // Mark the image as complete, this will tell the Decode function that we |
| 376 // have successfully found the end of the data. | 301 // have successfully found the end of the data. |
| 377 state->done = true; | 302 state->done = true; |
| 378 } | 303 } |
| 379 | 304 |
| 380 // Automatically destroys the given read structs on destruction to make | 305 // Automatically destroys the given libpng read or write structs on |
| 381 // cleanup and error handling code cleaner. | 306 // destruction to make cleanup and error handling code cleaner. |
| 382 class PngReadStructDestroyer { | 307 // |
| 383 public: | 308 // |IOMode|: 0 indicates read mode; 1 indicates write mode. |
| 384 PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { | 309 template<int IOMode> |
|
Elliot Glaysher
2012/02/28 18:37:43
We don't use templates like this. Even if you have
Francois
2012/02/29 09:12:29
This has been removed from the current CL.
| |
| 385 } | 310 struct ScopedLibPNGStructs { |
| 386 ~PngReadStructDestroyer() { | 311 ScopedLibPNGStructs(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {} |
| 387 png_destroy_read_struct(ps_, pi_, NULL); | 312 ~ScopedLibPNGStructs() { NOTIMPLEMENTED(); } |
| 388 } | |
| 389 private: | |
| 390 png_struct** ps_; | 313 png_struct** ps_; |
| 391 png_info** pi_; | 314 png_info** pi_; |
| 392 }; | 315 }; |
| 316 template<> | |
| 317 ScopedLibPNGStructs<0>::~ScopedLibPNGStructs() { | |
| 318 png_destroy_read_struct(ps_, pi_, NULL); | |
| 319 } | |
| 320 template<> | |
| 321 ScopedLibPNGStructs<1>::~ScopedLibPNGStructs() { | |
| 322 png_destroy_write_struct(ps_, pi_); | |
| 323 } | |
| 324 // Scoped LibPNG read structs. | |
| 325 typedef ScopedLibPNGStructs<0> ScopedReadStructs; | |
| 326 // Scoped LibPNG write structs. | |
| 327 typedef ScopedLibPNGStructs<1> ScopedWriteStructs; | |
| 393 | 328 |
| 394 bool BuildPNGStruct(const unsigned char* input, size_t input_size, | 329 bool BuildPNGStruct(const unsigned char* input, size_t input_size, |
| 395 png_struct** png_ptr, png_info** info_ptr) { | 330 png_struct** png_ptr, png_info** info_ptr) { |
| 396 if (input_size < 8) | 331 if (input_size < 8) |
| 397 return false; // Input data too small to be a png | 332 return false; // Input data too small to be a png |
| 398 | 333 |
| 399 // Have libpng check the signature, it likes the first 8 bytes. | 334 // Have libpng check the signature, it likes the first 8 bytes. |
| 400 if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0) | 335 if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0) |
| 401 return false; | 336 return false; |
| 402 | 337 |
| 403 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | 338 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); |
| 404 if (!*png_ptr) | 339 if (!*png_ptr) |
| 405 return false; | 340 return false; |
| 406 | 341 |
| 407 *info_ptr = png_create_info_struct(*png_ptr); | 342 *info_ptr = png_create_info_struct(*png_ptr); |
| 408 if (!*info_ptr) { | 343 if (!*info_ptr) { |
| 409 png_destroy_read_struct(png_ptr, NULL, NULL); | 344 png_destroy_read_struct(png_ptr, NULL, NULL); |
| 410 return false; | 345 return false; |
| 411 } | 346 } |
| 412 | 347 |
| 413 return true; | 348 return true; |
| 414 } | 349 } |
| 415 | 350 |
| 351 // Libpng user error and warning functions which allows us to print libpng | |
| 352 // errors and warnings using Chrome's logging facilities instead of stderr. | |
|
Elliot Glaysher
2012/02/28 18:37:43
Sweet!
| |
| 353 | |
| 354 void LogLibPNGDecodeError(png_structp png_ptr, png_const_charp error_msg) { | |
| 355 DLOG(ERROR) << "libpng decode error: " << error_msg; | |
| 356 longjmp(png_jmpbuf(png_ptr), 1); | |
| 357 } | |
| 358 | |
| 359 void LogLibPNGDecodeWarning(png_structp png_ptr, png_const_charp warning_msg) { | |
| 360 DLOG(ERROR) << "libpng decode warning: " << warning_msg; | |
| 361 } | |
| 362 | |
| 363 void LogLibPNGEncodeError(png_structp png_ptr, png_const_charp error_msg) { | |
| 364 DLOG(ERROR) << "libpng encode error: " << error_msg; | |
| 365 longjmp(png_jmpbuf(png_ptr), 1); | |
| 366 } | |
| 367 | |
| 368 void LogLibPNGEncodeWarning(png_structp png_ptr, png_const_charp warning_msg) { | |
| 369 DLOG(ERROR) << "libpng encode warning: " << warning_msg; | |
| 370 } | |
| 371 | |
| 416 } // namespace | 372 } // namespace |
| 417 | 373 |
| 418 // static | 374 // static |
| 419 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, | 375 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
| 420 ColorFormat format, std::vector<unsigned char>* output, | 376 ColorFormat format, std::vector<unsigned char>* output, |
| 421 int* w, int* h) { | 377 int* w, int* h) { |
| 422 png_struct* png_ptr = NULL; | 378 png_struct* png_ptr = NULL; |
| 423 png_info* info_ptr = NULL; | 379 png_info* info_ptr = NULL; |
| 424 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) | 380 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) |
| 425 return false; | 381 return false; |
| 426 | 382 |
| 427 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); | 383 ScopedReadStructs scoped_structs(&png_ptr, &info_ptr); |
| 428 if (setjmp(png_jmpbuf(png_ptr))) { | 384 if (setjmp(png_jmpbuf(png_ptr))) { |
| 429 // The destroyer will ensure that the structures are cleaned up in this | 385 // The destroyer will ensure that the structures are cleaned up in this |
| 430 // case, even though we may get here as a jump from random parts of the | 386 // case, even though we may get here as a jump from random parts of the |
| 431 // PNG library called below. | 387 // PNG library called below. |
| 432 return false; | 388 return false; |
| 433 } | 389 } |
| 434 | 390 |
| 435 PngDecoderState state(format, output); | 391 PngDecoderState state(format, output); |
| 436 | 392 |
| 437 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, | 393 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, |
| 438 &DecodeRowCallback, &DecodeEndCallback); | 394 &DecodeRowCallback, &DecodeEndCallback); |
| 395 png_set_error_fn(png_ptr, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning); | |
| 396 | |
| 439 png_process_data(png_ptr, | 397 png_process_data(png_ptr, |
| 440 info_ptr, | 398 info_ptr, |
| 441 const_cast<unsigned char*>(input), | 399 const_cast<unsigned char*>(input), |
| 442 input_size); | 400 input_size); |
| 443 | 401 |
| 444 if (!state.done) { | 402 if (!state.done) { |
| 445 // Fed it all the data but the library didn't think we got all the data, so | 403 // Fed it all the data but the library didn't think we got all the data, so |
| 446 // this file must be truncated. | 404 // this file must be truncated. |
| 447 output->clear(); | 405 output->clear(); |
| 448 return false; | 406 return false; |
| 449 } | 407 } |
| 450 | 408 |
| 451 *w = state.width; | 409 *w = state.width; |
| 452 *h = state.height; | 410 *h = state.height; |
| 453 return true; | 411 return true; |
| 454 } | 412 } |
| 455 | 413 |
| 456 // static | 414 // static |
| 457 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, | 415 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
| 458 SkBitmap* bitmap) { | 416 SkBitmap* bitmap) { |
| 459 DCHECK(bitmap); | 417 DCHECK(bitmap); |
| 460 png_struct* png_ptr = NULL; | 418 png_struct* png_ptr = NULL; |
| 461 png_info* info_ptr = NULL; | 419 png_info* info_ptr = NULL; |
| 462 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) | 420 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) |
| 463 return false; | 421 return false; |
| 464 | 422 |
| 465 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); | 423 ScopedReadStructs scoped_structs(&png_ptr, &info_ptr); |
| 466 if (setjmp(png_jmpbuf(png_ptr))) { | 424 if (setjmp(png_jmpbuf(png_ptr))) { |
| 467 // The destroyer will ensure that the structures are cleaned up in this | 425 // The destroyer will ensure that the structures are cleaned up in this |
| 468 // case, even though we may get here as a jump from random parts of the | 426 // case, even though we may get here as a jump from random parts of the |
| 469 // PNG library called below. | 427 // PNG library called below. |
| 470 return false; | 428 return false; |
| 471 } | 429 } |
| 472 | 430 |
| 473 PngDecoderState state(bitmap); | 431 PngDecoderState state(bitmap); |
| 474 | 432 |
| 475 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, | 433 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 536 size_t old_size = state->out->size(); | 494 size_t old_size = state->out->size(); |
| 537 state->out->resize(old_size + size); | 495 state->out->resize(old_size + size); |
| 538 memcpy(&(*state->out)[old_size], data, size); | 496 memcpy(&(*state->out)[old_size], data, size); |
| 539 } | 497 } |
| 540 | 498 |
| 541 void FakeFlushCallback(png_structp png) { | 499 void FakeFlushCallback(png_structp png) { |
| 542 // We don't need to perform any flushing since we aren't doing real IO, but | 500 // We don't need to perform any flushing since we aren't doing real IO, but |
| 543 // we're required to provide this function by libpng. | 501 // we're required to provide this function by libpng. |
| 544 } | 502 } |
| 545 | 503 |
| 546 void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width, | |
| 547 unsigned char* rgb, bool* is_opaque) { | |
| 548 for (int x = 0; x < pixel_width; x++) { | |
| 549 const unsigned char* pixel_in = &bgra[x * 4]; | |
| 550 unsigned char* pixel_out = &rgb[x * 3]; | |
| 551 pixel_out[0] = pixel_in[2]; | |
| 552 pixel_out[1] = pixel_in[1]; | |
| 553 pixel_out[2] = pixel_in[0]; | |
| 554 } | |
|
Francois
2012/02/28 15:00:36
Replaced by a combination of png_set_filler (stips
| |
| 555 } | |
| 556 | |
| 557 #ifdef PNG_TEXT_SUPPORTED | 504 #ifdef PNG_TEXT_SUPPORTED |
| 558 class CommentWriter { | 505 class CommentWriter { |
| 559 public: | 506 public: |
| 560 explicit CommentWriter(const std::vector<PNGCodec::Comment>& comments) | 507 explicit CommentWriter(const std::vector<PNGCodec::Comment>& comments) |
| 561 : comments_(comments), | 508 : comments_(comments), |
| 562 png_text_(new png_text[comments.size()]) { | 509 png_text_(new png_text[comments.size()]) { |
| 563 for (size_t i = 0; i < comments.size(); ++i) | 510 for (size_t i = 0; i < comments.size(); ++i) |
| 564 AddComment(i, comments[i]); | 511 AddComment(i, comments[i]); |
| 565 } | 512 } |
| 566 | 513 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 603 | 550 |
| 604 const std::vector<PNGCodec::Comment> comments_; | 551 const std::vector<PNGCodec::Comment> comments_; |
| 605 png_text* png_text_; | 552 png_text* png_text_; |
| 606 }; | 553 }; |
| 607 #endif // PNG_TEXT_SUPPORTED | 554 #endif // PNG_TEXT_SUPPORTED |
| 608 | 555 |
| 609 // The type of functions usable for converting between pixel formats. | 556 // The type of functions usable for converting between pixel formats. |
| 610 typedef void (*FormatConverter)(const unsigned char* in, int w, | 557 typedef void (*FormatConverter)(const unsigned char* in, int w, |
| 611 unsigned char* out, bool* is_opaque); | 558 unsigned char* out, bool* is_opaque); |
| 612 | 559 |
| 613 // libpng uses a wacky setjmp-based API, which makes the compiler nervous. | |
| 614 // We constrain all of the calls we make to libpng where the setjmp() is in | |
| 615 // place to this function. | |
| 616 // Returns true on success. | |
| 617 bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr, | |
| 618 PngEncoderState* state, | |
| 619 int width, int height, int row_byte_width, | |
| 620 const unsigned char* input, int compression_level, | |
| 621 int png_output_color_type, int output_color_components, | |
| 622 FormatConverter converter, | |
| 623 const std::vector<PNGCodec::Comment>& comments) { | |
| 624 // Make sure to not declare any locals here -- locals in the presence | |
| 625 // of setjmp() in C++ code makes gcc complain. | |
| 626 | |
| 627 if (setjmp(png_jmpbuf(png_ptr))) | |
| 628 return false; | |
| 629 | |
| 630 png_set_compression_level(png_ptr, compression_level); | |
| 631 | |
| 632 // Set our callback for libpng to give us the data. | |
| 633 png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback); | |
| 634 | |
| 635 png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type, | |
| 636 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |
| 637 PNG_FILTER_TYPE_DEFAULT); | |
| 638 | |
| 639 #ifdef PNG_TEXT_SUPPORTED | |
| 640 CommentWriter comment_writer(comments); | |
| 641 if (comment_writer.HasComments()) { | |
| 642 png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(), | |
| 643 comment_writer.size()); | |
| 644 } | |
| 645 #endif | |
| 646 | |
| 647 png_write_info(png_ptr, info_ptr); | |
| 648 | |
| 649 if (!converter) { | |
| 650 // No conversion needed, give the data directly to libpng. | |
| 651 for (int y = 0; y < height; y ++) { | |
| 652 png_write_row(png_ptr, | |
| 653 const_cast<unsigned char*>(&input[y * row_byte_width])); | |
| 654 } | |
| 655 } else { | |
| 656 // Needs conversion using a separate buffer. | |
| 657 unsigned char* row = new unsigned char[width * output_color_components]; | |
| 658 for (int y = 0; y < height; y ++) { | |
| 659 converter(&input[y * row_byte_width], width, row, NULL); | |
| 660 png_write_row(png_ptr, row); | |
| 661 } | |
| 662 delete[] row; | |
| 663 } | |
| 664 | |
| 665 png_write_end(png_ptr, info_ptr); | |
| 666 return true; | |
| 667 } | |
|
Francois
2012/02/28 15:00:36
I merged this function back into EncodeWithCompres
| |
| 668 | |
| 669 } // namespace | 560 } // namespace |
| 670 | 561 |
| 671 // static | 562 // static |
| 672 bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, | 563 bool PNGCodec::Encode(const unsigned char* input, ColorFormat format, |
| 673 const Size& size, int row_byte_width, | 564 const Size& size, int row_byte_width, |
| 674 bool discard_transparency, | 565 bool discard_transparency, |
| 675 const std::vector<Comment>& comments, | 566 const std::vector<Comment>& comments, |
| 676 std::vector<unsigned char>* output) { | 567 std::vector<unsigned char>* output) { |
| 677 return PNGCodec::EncodeWithCompressionLevel(input, format, size, | 568 return PNGCodec::EncodeWithCompressionLevel(input, format, size, |
| 678 row_byte_width, | 569 row_byte_width, |
| 679 discard_transparency, | 570 discard_transparency, |
| 680 comments, Z_DEFAULT_COMPRESSION, | 571 comments, Z_DEFAULT_COMPRESSION, |
| 681 output); | 572 output); |
| 682 } | 573 } |
| 683 | 574 |
| 684 // static | 575 // static |
| 685 bool PNGCodec::EncodeWithCompressionLevel(const unsigned char* input, | 576 bool PNGCodec::EncodeWithCompressionLevel(const unsigned char* input, |
| 686 ColorFormat format, const Size& size, | 577 ColorFormat format, const Size& size, |
| 687 int row_byte_width, | 578 int row_byte_width, |
| 688 bool discard_transparency, | 579 bool discard_transparency, |
| 689 const std::vector<Comment>& comments, | 580 const std::vector<Comment>& comments, |
| 690 int compression_level, | 581 int compression_level, |
| 691 std::vector<unsigned char>* output) { | 582 std::vector<unsigned char>* output) { |
| 692 // Run to convert an input row into the output row format, NULL means no | 583 // Run to convert an input row into the output row format, NULL means no |
| 693 // conversion is necessary. | 584 // conversion is necessary. |
| 694 FormatConverter converter = NULL; | 585 FormatConverter converter = NULL; |
| 695 | 586 |
| 696 int input_color_components, output_color_components; | 587 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| 588 NULL, NULL, NULL); | |
| 589 if (!png_ptr) return false; | |
| 590 png_info* info_ptr = png_create_info_struct(png_ptr); | |
| 591 if (!info_ptr) { | |
| 592 png_destroy_write_struct(&png_ptr, NULL); | |
| 593 return false; | |
| 594 } | |
| 595 | |
| 596 ScopedWriteStructs scoped_structs(&png_ptr, &info_ptr); | |
| 597 | |
| 598 if (setjmp(png_jmpbuf(png_ptr))) | |
| 599 return false; | |
| 600 | |
| 601 PngEncoderState state(output); | |
| 602 | |
| 603 png_set_compression_level(png_ptr, compression_level); | |
| 604 | |
| 605 // Set our callback for libpng to give us the data. | |
| 606 png_set_write_fn(png_ptr, &state, EncoderWriteCallback, FakeFlushCallback); | |
| 607 png_set_error_fn(png_ptr, NULL, LogLibPNGEncodeError, LogLibPNGEncodeWarning); | |
| 608 | |
| 609 int input_color_components; | |
|
Francois
2012/02/28 15:00:36
This block was merged from what used to be DoLibpn
| |
| 697 int png_output_color_type; | 610 int png_output_color_type; |
| 698 switch (format) { | 611 switch (format) { |
| 699 case FORMAT_RGB: | 612 case FORMAT_RGB: |
| 700 input_color_components = 3; | 613 input_color_components = 3; |
| 701 output_color_components = 3; | |
| 702 png_output_color_type = PNG_COLOR_TYPE_RGB; | 614 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 703 break; | 615 break; |
| 704 | 616 |
| 705 case FORMAT_RGBA: | 617 case FORMAT_RGBA: |
| 706 input_color_components = 4; | |
| 707 if (discard_transparency) { | |
| 708 output_color_components = 3; | |
| 709 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
| 710 converter = ConvertRGBAtoRGB; | |
| 711 } else { | |
| 712 output_color_components = 4; | |
| 713 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
| 714 converter = NULL; | |
| 715 } | |
| 716 break; | |
| 717 | |
| 718 case FORMAT_BGRA: | 618 case FORMAT_BGRA: |
| 719 input_color_components = 4; | 619 input_color_components = 4; |
| 720 if (discard_transparency) { | 620 if (discard_transparency) |
| 721 output_color_components = 3; | |
| 722 png_output_color_type = PNG_COLOR_TYPE_RGB; | 621 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 723 converter = ConvertBGRAtoRGB; | 622 else |
| 724 } else { | |
| 725 output_color_components = 4; | |
| 726 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | 623 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
| 727 converter = ConvertBetweenBGRAandRGBA; | |
| 728 } | |
| 729 break; | 624 break; |
| 730 | 625 |
| 731 case FORMAT_SkBitmap: | 626 case FORMAT_SkBitmap: |
| 732 input_color_components = 4; | 627 input_color_components = 4; |
| 733 if (discard_transparency) { | 628 if (discard_transparency) { |
| 734 output_color_components = 3; | |
| 735 png_output_color_type = PNG_COLOR_TYPE_RGB; | 629 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 736 converter = ConvertSkiatoRGB; | 630 converter = ConvertSkiatoRGB; |
| 737 } else { | 631 } else { |
| 738 output_color_components = 4; | |
| 739 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | 632 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
| 740 converter = ConvertSkiatoRGBA; | 633 converter = ConvertSkiatoRGBA; |
| 741 } | 634 } |
| 742 break; | 635 break; |
| 743 | 636 |
| 744 default: | 637 default: |
| 745 NOTREACHED() << "Unknown pixel format"; | 638 NOTREACHED() << "Unknown pixel format"; |
| 746 return false; | 639 return false; |
| 747 } | 640 } |
| 748 | 641 |
| 749 // Row stride should be at least as long as the length of the data. | 642 // Row stride should be at least as long as the length of the data. |
| 750 DCHECK(input_color_components * size.width() <= row_byte_width); | 643 DCHECK(input_color_components * size.width() <= row_byte_width); |
| 751 | 644 |
| 752 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | 645 png_set_IHDR(png_ptr, info_ptr, |
| 753 NULL, NULL, NULL); | 646 size.width(), size.height(), |
| 754 if (!png_ptr) | 647 8, png_output_color_type, |
| 755 return false; | 648 PNG_INTERLACE_NONE, |
| 756 png_info* info_ptr = png_create_info_struct(png_ptr); | 649 PNG_COMPRESSION_TYPE_DEFAULT, |
| 757 if (!info_ptr) { | 650 PNG_FILTER_TYPE_DEFAULT); |
| 758 png_destroy_write_struct(&png_ptr, NULL); | 651 png_write_info(png_ptr, info_ptr); |
| 759 return false; | 652 |
| 653 // Done outside of the switch above because the of the order of operations | |
| 654 // dictated by libpng. | |
| 655 if (format != FORMAT_SkBitmap) { | |
| 656 if (input_color_components == 4 && discard_transparency) | |
| 657 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); // Strip alpha | |
| 658 if (format == FORMAT_BGRA) | |
| 659 png_set_bgr(png_ptr); | |
| 760 } | 660 } |
| 761 | 661 |
| 762 PngEncoderState state(output); | 662 #ifdef PNG_TEXT_SUPPORTED |
| 763 bool success = DoLibpngWrite(png_ptr, info_ptr, &state, | 663 CommentWriter comment_writer(comments); |
| 764 size.width(), size.height(), row_byte_width, | 664 if (comment_writer.HasComments()) { |
| 765 input, compression_level, png_output_color_type, | 665 png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(), |
| 766 output_color_components, converter, comments); | 666 comment_writer.size()); |
| 767 png_destroy_write_struct(&png_ptr, &info_ptr); | 667 } |
| 668 #endif | |
| 768 | 669 |
| 769 return success; | 670 if (!converter) { |
| 671 // No conversion needed, give the data directly to libpng. | |
| 672 for (int y = 0; y < size.height(); y ++) { | |
| 673 png_write_row(png_ptr, | |
| 674 const_cast<unsigned char*>(&input[y * row_byte_width])); | |
| 675 } | |
| 676 } else { | |
| 677 // Needs conversion using a separate buffer. | |
| 678 const int color_components = | |
| 679 (png_output_color_type & PNG_COLOR_MASK_ALPHA ? 4 : 3); | |
| 680 unsigned char* row = new unsigned char[size.width() * color_components]; | |
| 681 for (int y = 0; y < size.height(); y ++) { | |
| 682 converter(&input[y * row_byte_width], size.width(), row, NULL); | |
| 683 png_write_row(png_ptr, row); | |
| 684 } | |
| 685 delete[] row; | |
| 686 } | |
| 687 | |
| 688 png_write_end(png_ptr, info_ptr); | |
| 689 return true; | |
|
Francois
2012/02/28 15:00:36
Large parts of this block and the two above it wer
| |
| 770 } | 690 } |
| 771 | 691 |
| 772 // static | 692 // static |
| 773 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input, | 693 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input, |
| 774 bool discard_transparency, | 694 bool discard_transparency, |
| 775 std::vector<unsigned char>* output) { | 695 std::vector<unsigned char>* output) { |
| 776 static const int bbp = 4; | 696 static const int bbp = 4; |
| 777 | 697 |
| 778 SkAutoLockPixels lock_input(input); | 698 SkAutoLockPixels lock_input(input); |
| 779 DCHECK(input.empty() || input.bytesPerPixel() == bbp); | 699 DCHECK(input.empty() || input.bytesPerPixel() == bbp); |
| 780 | 700 |
| 781 return Encode(reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)), | 701 return Encode(reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)), |
| 782 FORMAT_SkBitmap, Size(input.width(), input.height()), | 702 FORMAT_SkBitmap, Size(input.width(), input.height()), |
| 783 input.width() * bbp, discard_transparency, | 703 input.width() * bbp, discard_transparency, |
| 784 std::vector<Comment>(), output); | 704 std::vector<Comment>(), output); |
| 785 } | 705 } |
| 786 | 706 |
| 787 PNGCodec::Comment::Comment(const std::string& k, const std::string& t) | 707 PNGCodec::Comment::Comment(const std::string& k, const std::string& t) |
| 788 : key(k), text(t) { | 708 : key(k), text(t) { |
| 789 } | 709 } |
| 790 | 710 |
| 791 PNGCodec::Comment::~Comment() { | 711 PNGCodec::Comment::~Comment() { |
| 792 } | 712 } |
| 793 | 713 |
| 794 } // namespace gfx | 714 } // namespace gfx |
| OLD | NEW |