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

Side by Side 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, 9 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698