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

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
« no previous file with comments | « third_party/libpng/pngusr.h ('k') | ui/gfx/codec/png_codec_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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"
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 unsigned char* rgb, bool* is_opaque) { 47 unsigned char* rgb, bool* is_opaque) {
48 for (int x = 0; x < pixel_width; x++) { 48 for (int x = 0; x < pixel_width; x++) {
49 const unsigned char* pixel_in = &rgba[x * 4]; 49 const unsigned char* pixel_in = &rgba[x * 4];
50 unsigned char* pixel_out = &rgb[x * 3]; 50 unsigned char* pixel_out = &rgb[x * 3];
51 pixel_out[0] = pixel_in[0]; 51 pixel_out[0] = pixel_in[0];
52 pixel_out[1] = pixel_in[1]; 52 pixel_out[1] = pixel_in[1];
53 pixel_out[2] = pixel_in[2]; 53 pixel_out[2] = pixel_in[2];
54 } 54 }
55 } 55 }
56 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 }
84
85 void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width, 57 void ConvertSkiatoRGB(const unsigned char* skia, int pixel_width,
86 unsigned char* rgb, bool* is_opaque) { 58 unsigned char* rgb, bool* is_opaque) {
87 for (int x = 0; x < pixel_width; x++) { 59 for (int x = 0; x < pixel_width; x++) {
88 const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]); 60 const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]);
89 unsigned char* pixel_out = &rgb[x * 3]; 61 unsigned char* pixel_out = &rgb[x * 3];
90 62
91 int alpha = SkGetPackedA32(pixel_in); 63 int alpha = SkGetPackedA32(pixel_in);
92 if (alpha != 0 && alpha != 255) { 64 if (alpha != 0 && alpha != 255) {
93 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in); 65 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in);
94 pixel_out[0] = SkColorGetR(unmultiplied); 66 pixel_out[0] = SkColorGetR(unmultiplied);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 113
142 class PngDecoderState { 114 class PngDecoderState {
143 public: 115 public:
144 // Output is a vector<unsigned char>. 116 // Output is a vector<unsigned char>.
145 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o) 117 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o)
146 : output_format(ofmt), 118 : output_format(ofmt),
147 output_channels(0), 119 output_channels(0),
148 bitmap(NULL), 120 bitmap(NULL),
149 is_opaque(true), 121 is_opaque(true),
150 output(o), 122 output(o),
151 row_converter(NULL),
152 width(0), 123 width(0),
153 height(0), 124 height(0),
154 done(false) { 125 done(false) {
155 } 126 }
156 127
157 // Output is an SkBitmap. 128 // Output is an SkBitmap.
158 explicit PngDecoderState(SkBitmap* skbitmap) 129 explicit PngDecoderState(SkBitmap* skbitmap)
159 : output_format(PNGCodec::FORMAT_SkBitmap), 130 : output_format(PNGCodec::FORMAT_SkBitmap),
160 output_channels(0), 131 output_channels(0),
161 bitmap(skbitmap), 132 bitmap(skbitmap),
162 is_opaque(true), 133 is_opaque(true),
163 output(NULL), 134 output(NULL),
164 row_converter(NULL),
165 width(0), 135 width(0),
166 height(0), 136 height(0),
167 done(false) { 137 done(false) {
168 } 138 }
169 139
170 PNGCodec::ColorFormat output_format; 140 PNGCodec::ColorFormat output_format;
171 int output_channels; 141 int output_channels;
172 142
173 // An incoming SkBitmap to write to. If NULL, we write to output instead. 143 // An incoming SkBitmap to write to. If NULL, we write to output instead.
174 SkBitmap* bitmap; 144 SkBitmap* bitmap;
175 145
176 // Used during the reading of an SkBitmap. Defaults to true until we see a 146 // Used during the reading of an SkBitmap. Defaults to true until we see a
177 // pixel with anything other than an alpha of 255. 147 // pixel with anything other than an alpha of 255.
178 bool is_opaque; 148 bool is_opaque;
179 149
180 // The other way to decode output, where we write into an intermediary buffer 150 // The other way to decode output, where we write into an intermediary buffer
181 // instead of directly to an SkBitmap. 151 // instead of directly to an SkBitmap.
182 std::vector<unsigned char>* output; 152 std::vector<unsigned char>* output;
183 153
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);
188
189 // Size of the image, set in the info callback. 154 // Size of the image, set in the info callback.
190 int width; 155 int width;
191 int height; 156 int height;
192 157
193 // Set to true when we've found the end of the data. 158 // Set to true when we've found the end of the data.
194 bool done; 159 bool done;
195 160
196 private: 161 private:
197 DISALLOW_COPY_AND_ASSIGN(PngDecoderState); 162 DISALLOW_COPY_AND_ASSIGN(PngDecoderState);
198 }; 163 };
199 164
200 void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, 165 // User transform (passed to libpng) which converts a row decoded by libpng to
201 unsigned char* rgba, bool* is_opaque) { 166 // Skia format. Expects the row to have 4 channels, otherwise there won't be
202 for (int x = 0; x < pixel_width; x++) { 167 // enough room in |data|.
203 const unsigned char* pixel_in = &rgb[x * 3]; 168 void ConvertRGBARowToSkia(png_structp png_ptr,
204 unsigned char* pixel_out = &rgba[x * 4]; 169 png_row_infop row_info,
205 pixel_out[0] = pixel_in[0]; 170 png_bytep data) {
206 pixel_out[1] = pixel_in[1]; 171 const int channels = row_info->channels;
207 pixel_out[2] = pixel_in[2]; 172 DCHECK_EQ(channels, 4);
208 pixel_out[3] = 0xff; 173
174 PngDecoderState* state =
175 static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr));
176 DCHECK(state) << "LibPNG user transform pointer is NULL";
177
178 unsigned char* const end = data + row_info->rowbytes;
179 for (unsigned char* p = data; p < end; p += channels) {
180 uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p);
181 const unsigned char alpha = p[channels - 1];
182 if (alpha != 255) {
183 state->is_opaque = false;
184 *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]);
185 } else {
186 *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]);
187 }
209 } 188 }
210 } 189 }
211 190
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
224 // Called when the png header has been read. This code is based on the WebKit 191 // Called when the png header has been read. This code is based on the WebKit
225 // PNGImageDecoder 192 // PNGImageDecoder
226 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { 193 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
227 PngDecoderState* state = static_cast<PngDecoderState*>( 194 PngDecoderState* state = static_cast<PngDecoderState*>(
228 png_get_progressive_ptr(png_ptr)); 195 png_get_progressive_ptr(png_ptr));
229 196
230 int bit_depth, color_type, interlace_type, compression_type; 197 int bit_depth, color_type, interlace_type, compression_type;
231 int filter_type, channels; 198 int filter_type;
232 png_uint_32 w, h; 199 png_uint_32 w, h;
233 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, 200 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
234 &interlace_type, &compression_type, &filter_type); 201 &interlace_type, &compression_type, &filter_type);
235 202
236 // Bounds check. When the image is unreasonably big, we'll error out and 203 // 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" 204 // 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 205 // 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 206 // 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. 207 // that an image's size (in bytes) fits in a (signed) int.
241 unsigned long long total_size = 208 unsigned long long total_size =
242 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h); 209 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
243 if (total_size > ((1 << 29) - 1)) 210 if (total_size > ((1 << 29) - 1))
244 longjmp(png_jmpbuf(png_ptr), 1); 211 longjmp(png_jmpbuf(png_ptr), 1);
245 state->width = static_cast<int>(w); 212 state->width = static_cast<int>(w);
246 state->height = static_cast<int>(h); 213 state->height = static_cast<int>(h);
247 214
215 // The following png_set_* calls have to be done in the order dictated by
216 // the libpng docs. Please take care if you have to move any of them. This
217 // is also why certain things are done outside of the switch, even though
218 // they look like they belong there.
219
248 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 220 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
249 if (color_type == PNG_COLOR_TYPE_PALETTE || 221 if (color_type == PNG_COLOR_TYPE_PALETTE ||
250 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) 222 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
251 png_set_expand(png_ptr); 223 png_set_expand(png_ptr);
252 224
225 // The '!= 0' is for silencing a Windows compiler warning.
226 bool input_has_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
227
253 // Transparency for paletted images. 228 // Transparency for paletted images.
254 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 229 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
255 png_set_expand(png_ptr); 230 png_set_expand(png_ptr);
231 input_has_alpha = true;
232 }
256 233
257 // Convert 16-bit to 8-bit. 234 // Convert 16-bit to 8-bit.
258 if (bit_depth == 16) 235 if (bit_depth == 16)
259 png_set_strip_16(png_ptr); 236 png_set_strip_16(png_ptr);
260 237
238 // Pick our row format converter necessary for this data.
239 if (!input_has_alpha) {
240 switch (state->output_format) {
241 case PNGCodec::FORMAT_RGB:
242 state->output_channels = 3;
243 break;
244 case PNGCodec::FORMAT_RGBA:
245 state->output_channels = 4;
246 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
247 break;
248 case PNGCodec::FORMAT_BGRA:
249 state->output_channels = 4;
250 png_set_bgr(png_ptr);
251 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
252 break;
253 case PNGCodec::FORMAT_SkBitmap:
254 state->output_channels = 4;
255 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
256 break;
257 }
258 } else {
259 switch (state->output_format) {
260 case PNGCodec::FORMAT_RGB:
261 state->output_channels = 3;
262 png_set_strip_alpha(png_ptr);
263 break;
264 case PNGCodec::FORMAT_RGBA:
265 state->output_channels = 4;
266 break;
267 case PNGCodec::FORMAT_BGRA:
268 state->output_channels = 4;
269 png_set_bgr(png_ptr);
270 break;
271 case PNGCodec::FORMAT_SkBitmap:
272 state->output_channels = 4;
273 break;
274 }
275 }
276
261 // Expand grayscale to RGB. 277 // Expand grayscale to RGB.
262 if (color_type == PNG_COLOR_TYPE_GRAY || 278 if (color_type == PNG_COLOR_TYPE_GRAY ||
263 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 279 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
264 png_set_gray_to_rgb(png_ptr); 280 png_set_gray_to_rgb(png_ptr);
265 281
266 // Deal with gamma and keep it under our control. 282 // Deal with gamma and keep it under our control.
267 double gamma; 283 double gamma;
268 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { 284 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
269 if (gamma <= 0.0 || gamma > kMaxGamma) { 285 if (gamma <= 0.0 || gamma > kMaxGamma) {
270 gamma = kInverseGamma; 286 gamma = kInverseGamma;
271 png_set_gAMA(png_ptr, info_ptr, gamma); 287 png_set_gAMA(png_ptr, info_ptr, gamma);
272 } 288 }
273 png_set_gamma(png_ptr, kDefaultGamma, gamma); 289 png_set_gamma(png_ptr, kDefaultGamma, gamma);
274 } else { 290 } else {
275 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); 291 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
276 } 292 }
277 293
294 // Setting the user transforms here (as opposed to inside the switch above)
295 // because all png_set_* calls need to be done in the specific order
296 // mandated by libpng.
297 if (state->output_format == PNGCodec::FORMAT_SkBitmap) {
298 png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia);
299 png_set_user_transform_info(png_ptr, state, 0, 0);
300 }
301
278 // Tell libpng to send us rows for interlaced pngs. 302 // Tell libpng to send us rows for interlaced pngs.
279 if (interlace_type == PNG_INTERLACE_ADAM7) 303 if (interlace_type == PNG_INTERLACE_ADAM7)
280 png_set_interlace_handling(png_ptr); 304 png_set_interlace_handling(png_ptr);
281 305
282 // Update our info now
283 png_read_update_info(png_ptr, info_ptr); 306 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 307
336 if (state->bitmap) { 308 if (state->bitmap) {
337 state->bitmap->setConfig(SkBitmap::kARGB_8888_Config, 309 state->bitmap->setConfig(SkBitmap::kARGB_8888_Config,
338 state->width, state->height); 310 state->width, state->height);
339 state->bitmap->allocPixels(); 311 state->bitmap->allocPixels();
340 } else if (state->output) { 312 } else if (state->output) {
341 state->output->resize( 313 state->output->resize(
342 state->width * state->output_channels * state->height); 314 state->width * state->output_channels * state->height);
343 } 315 }
344 } 316 }
345 317
346 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, 318 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
347 png_uint_32 row_num, int pass) { 319 png_uint_32 row_num, int pass) {
320 if (!new_row)
321 return; // Interlaced image; row didn't change this pass.
322
348 PngDecoderState* state = static_cast<PngDecoderState*>( 323 PngDecoderState* state = static_cast<PngDecoderState*>(
349 png_get_progressive_ptr(png_ptr)); 324 png_get_progressive_ptr(png_ptr));
350 325
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) { 326 if (static_cast<int>(row_num) > state->height) {
354 NOTREACHED() << "Invalid row"; 327 NOTREACHED() << "Invalid row";
355 return; 328 return;
356 } 329 }
357 330
358 unsigned char* base = NULL; 331 unsigned char* base = NULL;
359 if (state->bitmap) 332 if (state->bitmap)
360 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0)); 333 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0));
361 else if (state->output) 334 else if (state->output)
362 base = &state->output->front(); 335 base = &state->output->front();
363 336
364 unsigned char* dest = &base[state->width * state->output_channels * row_num]; 337 unsigned char* dest = &base[state->width * state->output_channels * row_num];
365 if (state->row_converter) 338 png_progressive_combine_row(png_ptr, dest, new_row);
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 } 339 }
370 340
371 void DecodeEndCallback(png_struct* png_ptr, png_info* info) { 341 void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
372 PngDecoderState* state = static_cast<PngDecoderState*>( 342 PngDecoderState* state = static_cast<PngDecoderState*>(
373 png_get_progressive_ptr(png_ptr)); 343 png_get_progressive_ptr(png_ptr));
374 344
375 // Mark the image as complete, this will tell the Decode function that we 345 // Mark the image as complete, this will tell the Decode function that we
376 // have successfully found the end of the data. 346 // have successfully found the end of the data.
377 state->done = true; 347 state->done = true;
378 } 348 }
(...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after
785 } 755 }
786 756
787 PNGCodec::Comment::Comment(const std::string& k, const std::string& t) 757 PNGCodec::Comment::Comment(const std::string& k, const std::string& t)
788 : key(k), text(t) { 758 : key(k), text(t) {
789 } 759 }
790 760
791 PNGCodec::Comment::~Comment() { 761 PNGCodec::Comment::~Comment() {
792 } 762 }
793 763
794 } // namespace gfx 764 } // namespace gfx
OLDNEW
« no previous file with comments | « third_party/libpng/pngusr.h ('k') | ui/gfx/codec/png_codec_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698