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

Side by Side Diff: base/gfx/jpeg_codec.cc

Issue 243076: Move the JPEG and PNG codecs from base/gfx to app/gfx/codec. Move the classes... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 2 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
(Empty)
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/gfx/jpeg_codec.h"
6
7 #include <setjmp.h>
8
9 #include "base/logging.h"
10 #include "base/scoped_ptr.h"
11 #include "third_party/skia/include/core/SkBitmap.h"
12
13 extern "C" {
14 #if defined(USE_SYSTEM_LIBJPEG)
15 #include <jpeglib.h>
16 #else
17 #include "third_party/libjpeg/jpeglib.h"
18 #endif
19 }
20
21 // Encoder/decoder shared stuff ------------------------------------------------
22
23 namespace {
24
25 // used to pass error info through the JPEG library
26 struct CoderErrorMgr {
27 jpeg_error_mgr pub;
28 jmp_buf setjmp_buffer;
29 };
30
31 void ErrorExit(jpeg_common_struct* cinfo) {
32 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
33
34 // Return control to the setjmp point.
35 longjmp(err->setjmp_buffer, false);
36 }
37
38 } // namespace
39
40 // Encoder ---------------------------------------------------------------------
41 //
42 // This code is based on nsJPEGEncoder from Mozilla.
43 // Copyright 2005 Google Inc. (Brett Wilson, contributor)
44
45 namespace {
46
47 // Initial size for the output buffer in the JpegEncoderState below.
48 const static int initial_output_buffer_size = 8192;
49
50 struct JpegEncoderState {
51 JpegEncoderState(std::vector<unsigned char>* o)
52 : out(o),
53 image_buffer_used(0) {
54 }
55
56 // Output buffer, of which 'image_buffer_used' bytes are actually used (this
57 // will often be less than the actual size of the vector because we size it
58 // so that libjpeg can write directly into it.
59 std::vector<unsigned char>* out;
60
61 // Number of bytes in the 'out' buffer that are actually used (see above).
62 size_t image_buffer_used;
63 };
64
65 // Initializes the JpegEncoderState for encoding, and tells libjpeg about where
66 // the output buffer is.
67 //
68 // From the JPEG library:
69 // "Initialize destination. This is called by jpeg_start_compress() before
70 // any data is actually written. It must initialize next_output_byte and
71 // free_in_buffer. free_in_buffer must be initialized to a positive value."
72 void InitDestination(jpeg_compress_struct* cinfo) {
73 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
74 DCHECK(state->image_buffer_used == 0) << "initializing after use";
75
76 state->out->resize(initial_output_buffer_size);
77 state->image_buffer_used = 0;
78
79 cinfo->dest->next_output_byte = &(*state->out)[0];
80 cinfo->dest->free_in_buffer = initial_output_buffer_size;
81 }
82
83 // Resize the buffer that we give to libjpeg and update our and its state.
84 //
85 // From the JPEG library:
86 // "Callback used by libjpeg whenever the buffer has filled (free_in_buffer
87 // reaches zero). In typical applications, it should write out the *entire*
88 // buffer (use the saved start address and buffer length; ignore the current
89 // state of next_output_byte and free_in_buffer). Then reset the pointer &
90 // count to the start of the buffer, and return TRUE indicating that the
91 // buffer has been dumped. free_in_buffer must be set to a positive value
92 // when TRUE is returned. A FALSE return should only be used when I/O
93 // suspension is desired (this operating mode is discussed in the next
94 // section)."
95 boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) {
96 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
97
98 // note the new size, the buffer is full
99 state->image_buffer_used = state->out->size();
100
101 // expand buffer, just double size each time
102 state->out->resize(state->out->size() * 2);
103
104 // tell libjpeg where to write the next data
105 cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used];
106 cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used;
107 return 1;
108 }
109
110 // Cleans up the JpegEncoderState to prepare for returning in the final form.
111 //
112 // From the JPEG library:
113 // "Terminate destination --- called by jpeg_finish_compress() after all data
114 // has been written. In most applications, this must flush any data
115 // remaining in the buffer. Use either next_output_byte or free_in_buffer to
116 // determine how much data is in the buffer."
117 void TermDestination(jpeg_compress_struct* cinfo) {
118 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
119 DCHECK(state->out->size() >= state->image_buffer_used);
120
121 // update the used byte based on the next byte libjpeg would write to
122 state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0];
123 DCHECK(state->image_buffer_used < state->out->size()) <<
124 "JPEG library busted, got a bad image buffer size";
125
126 // update our buffer so that it exactly encompases the desired data
127 state->out->resize(state->image_buffer_used);
128 }
129
130 // Converts RGBA to RGB (removing the alpha values) to prepare to send data to
131 // libjpeg. This converts one row of data in rgba with the given width in
132 // pixels the the given rgb destination buffer (which should have enough space
133 // reserved for the final data).
134 void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
135 {
136 for (int x = 0; x < pixel_width; x++) {
137 const unsigned char* pixel_in = &rgba[x * 4];
138 unsigned char* pixel_out = &rgb[x * 3];
139 pixel_out[0] = pixel_in[0];
140 pixel_out[1] = pixel_in[1];
141 pixel_out[2] = pixel_in[2];
142 }
143 }
144
145 // Converts BGRA to RGB by reordering the color components and dropping the
146 // alpha. This converts one row of data in rgba with the given width in
147 // pixels the the given rgb destination buffer (which should have enough space
148 // reserved for the final data).
149 void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
150 {
151 for (int x = 0; x < pixel_width; x++) {
152 const unsigned char* pixel_in = &bgra[x * 4];
153 unsigned char* pixel_out = &rgb[x * 3];
154 pixel_out[0] = pixel_in[2];
155 pixel_out[1] = pixel_in[1];
156 pixel_out[2] = pixel_in[0];
157 }
158 }
159
160 // This class destroys the given jpeg_compress object when it goes out of
161 // scope. It simplifies the error handling in Encode (and even applies to the
162 // success case).
163 class CompressDestroyer {
164 public:
165 CompressDestroyer() : cinfo_(NULL) {
166 }
167 ~CompressDestroyer() {
168 DestroyManagedObject();
169 }
170 void SetManagedObject(jpeg_compress_struct* ci) {
171 DestroyManagedObject();
172 cinfo_ = ci;
173 }
174 void DestroyManagedObject() {
175 if (cinfo_) {
176 jpeg_destroy_compress(cinfo_);
177 cinfo_ = NULL;
178 }
179 }
180 private:
181 jpeg_compress_struct* cinfo_;
182 };
183
184 } // namespace
185
186 bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format,
187 int w, int h, int row_byte_width,
188 int quality, std::vector<unsigned char>* output) {
189 jpeg_compress_struct cinfo;
190 CompressDestroyer destroyer;
191 destroyer.SetManagedObject(&cinfo);
192 output->clear();
193
194 // We set up the normal JPEG error routines, then override error_exit.
195 // This must be done before the call to create_compress.
196 CoderErrorMgr errmgr;
197 cinfo.err = jpeg_std_error(&errmgr.pub);
198 errmgr.pub.error_exit = ErrorExit;
199 // Establish the setjmp return context for ErrorExit to use.
200 if (setjmp(errmgr.setjmp_buffer)) {
201 // If we get here, the JPEG code has signaled an error.
202 // MSDN notes: "if you intend your code to be portable, do not rely on
203 // correct destruction of frame-based objects when executing a nonlocal
204 // goto using a call to longjmp." So we delete the CompressDestroyer's
205 // object manually instead.
206 destroyer.DestroyManagedObject();
207 return false;
208 }
209
210 // The destroyer will destroy() cinfo on exit.
211 jpeg_create_compress(&cinfo);
212
213 cinfo.image_width = w;
214 cinfo.image_height = h;
215 cinfo.input_components = 3;
216 cinfo.in_color_space = JCS_RGB;
217 cinfo.data_precision = 8;
218
219 jpeg_set_defaults(&cinfo);
220 jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
221
222 // set up the destination manager
223 jpeg_destination_mgr destmgr;
224 destmgr.init_destination = InitDestination;
225 destmgr.empty_output_buffer = EmptyOutputBuffer;
226 destmgr.term_destination = TermDestination;
227 cinfo.dest = &destmgr;
228
229 JpegEncoderState state(output);
230 cinfo.client_data = &state;
231
232 jpeg_start_compress(&cinfo, 1);
233
234 // feed it the rows, doing necessary conversions for the color format
235 if (format == FORMAT_RGB) {
236 // no conversion necessary
237 while (cinfo.next_scanline < cinfo.image_height) {
238 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
239 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
240 }
241 } else {
242 // get the correct format converter
243 void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
244 if (format == FORMAT_RGBA) {
245 converter = StripAlpha;
246 } else if (format == FORMAT_BGRA) {
247 converter = BGRAtoRGB;
248 } else {
249 NOTREACHED() << "Invalid pixel format";
250 return false;
251 }
252
253 // output row after converting
254 unsigned char* row = new unsigned char[w * 3];
255
256 while (cinfo.next_scanline < cinfo.image_height) {
257 converter(&input[cinfo.next_scanline * row_byte_width], w, row);
258 jpeg_write_scanlines(&cinfo, &row, 1);
259 }
260 delete[] row;
261 }
262
263 jpeg_finish_compress(&cinfo);
264 return true;
265 }
266
267 // Decoder --------------------------------------------------------------------
268
269 namespace {
270
271 struct JpegDecoderState {
272 JpegDecoderState(const unsigned char* in, size_t len)
273 : input_buffer(in), input_buffer_length(len) {
274 }
275
276 const unsigned char* input_buffer;
277 size_t input_buffer_length;
278 };
279
280 // Callback to initialize the source.
281 //
282 // From the JPEG library:
283 // "Initialize source. This is called by jpeg_read_header() before any data is
284 // actually read. May leave bytes_in_buffer set to 0 (in which case a
285 // fill_input_buffer() call will occur immediately)."
286 void InitSource(j_decompress_ptr cinfo) {
287 JpegDecoderState* state = static_cast<JpegDecoderState*>(cinfo->client_data);
288 cinfo->src->next_input_byte = state->input_buffer;
289 cinfo->src->bytes_in_buffer = state->input_buffer_length;
290 }
291
292 // Callback to fill the buffer. Since our buffer already contains all the data,
293 // we should never need to provide more data. If libjpeg thinks it needs more
294 // data, our input is probably corrupt.
295 //
296 // From the JPEG library:
297 // "This is called whenever bytes_in_buffer has reached zero and more data is
298 // wanted. In typical applications, it should read fresh data into the buffer
299 // (ignoring the current state of next_input_byte and bytes_in_buffer), reset
300 // the pointer & count to the start of the buffer, and return TRUE indicating
301 // that the buffer has been reloaded. It is not necessary to fill the buffer
302 // entirely, only to obtain at least one more byte. bytes_in_buffer MUST be
303 // set to a positive value if TRUE is returned. A FALSE return should only
304 // be used when I/O suspension is desired."
305 boolean FillInputBuffer(j_decompress_ptr cinfo) {
306 return false;
307 }
308
309 // Skip data in the buffer. Since we have all the data at once, this operation
310 // is easy. It is not clear if this ever gets called because the JPEG library
311 // should be able to do the skip itself (it has all the data).
312 //
313 // From the JPEG library:
314 // "Skip num_bytes worth of data. The buffer pointer and count should be
315 // advanced over num_bytes input bytes, refilling the buffer as needed. This
316 // is used to skip over a potentially large amount of uninteresting data
317 // (such as an APPn marker). In some applications it may be possible to
318 // optimize away the reading of the skipped data, but it's not clear that
319 // being smart is worth much trouble; large skips are uncommon.
320 // bytes_in_buffer may be zero on return. A zero or negative skip count
321 // should be treated as a no-op."
322 void SkipInputData(j_decompress_ptr cinfo, long num_bytes) {
323 if (num_bytes > static_cast<long>(cinfo->src->bytes_in_buffer)) {
324 // Since all our data should be in the buffer, trying to skip beyond it
325 // means that there is some kind of error or corrupt input data. A 0 for
326 // bytes left means it will call FillInputBuffer which will then fail.
327 cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer;
328 cinfo->src->bytes_in_buffer = 0;
329 } else if (num_bytes > 0) {
330 cinfo->src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
331 cinfo->src->next_input_byte += num_bytes;
332 }
333 }
334
335 // Our source doesn't need any cleanup, so this is a NOP.
336 //
337 // From the JPEG library:
338 // "Terminate source --- called by jpeg_finish_decompress() after all data has
339 // been read to clean up JPEG source manager. NOT called by jpeg_abort() or
340 // jpeg_destroy()."
341 void TermSource(j_decompress_ptr cinfo) {
342 }
343
344 // Converts one row of rgb data to rgba data by adding a fully-opaque alpha
345 // value.
346 void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
347 for (int x = 0; x < pixel_width; x++) {
348 const unsigned char* pixel_in = &rgb[x * 3];
349 unsigned char* pixel_out = &rgba[x * 4];
350 pixel_out[0] = pixel_in[0];
351 pixel_out[1] = pixel_in[1];
352 pixel_out[2] = pixel_in[2];
353 pixel_out[3] = 0xff;
354 }
355 }
356
357 // Converts one row of RGB data to BGRA by reordering the color components and
358 // adding alpha values of 0xff.
359 void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
360 {
361 for (int x = 0; x < pixel_width; x++) {
362 const unsigned char* pixel_in = &bgra[x * 3];
363 unsigned char* pixel_out = &rgb[x * 4];
364 pixel_out[0] = pixel_in[2];
365 pixel_out[1] = pixel_in[1];
366 pixel_out[2] = pixel_in[0];
367 pixel_out[3] = 0xff;
368 }
369 }
370
371 // This class destroys the given jpeg_decompress object when it goes out of
372 // scope. It simplifies the error handling in Decode (and even applies to the
373 // success case).
374 class DecompressDestroyer {
375 public:
376 DecompressDestroyer() : cinfo_(NULL) {
377 }
378 ~DecompressDestroyer() {
379 DestroyManagedObject();
380 }
381 void SetManagedObject(jpeg_decompress_struct* ci) {
382 DestroyManagedObject();
383 cinfo_ = ci;
384 }
385 void DestroyManagedObject() {
386 if (cinfo_) {
387 jpeg_destroy_decompress(cinfo_);
388 cinfo_ = NULL;
389 }
390 }
391 private:
392 jpeg_decompress_struct* cinfo_;
393 };
394
395 } // namespace
396
397 bool JPEGCodec::Decode(const unsigned char* input, size_t input_size,
398 ColorFormat format, std::vector<unsigned char>* output,
399 int* w, int* h) {
400 jpeg_decompress_struct cinfo;
401 DecompressDestroyer destroyer;
402 destroyer.SetManagedObject(&cinfo);
403 output->clear();
404
405 // We set up the normal JPEG error routines, then override error_exit.
406 // This must be done before the call to create_decompress.
407 CoderErrorMgr errmgr;
408 cinfo.err = jpeg_std_error(&errmgr.pub);
409 errmgr.pub.error_exit = ErrorExit;
410 // Establish the setjmp return context for ErrorExit to use.
411 if (setjmp(errmgr.setjmp_buffer)) {
412 // If we get here, the JPEG code has signaled an error.
413 // See note in JPEGCodec::Encode() for why we need to destroy the cinfo
414 // manually here.
415 destroyer.DestroyManagedObject();
416 return false;
417 }
418
419 // The destroyer will destroy() cinfo on exit. We don't want to set the
420 // destroyer's object until cinfo is initialized.
421 jpeg_create_decompress(&cinfo);
422
423 // set up the source manager
424 jpeg_source_mgr srcmgr;
425 srcmgr.init_source = InitSource;
426 srcmgr.fill_input_buffer = FillInputBuffer;
427 srcmgr.skip_input_data = SkipInputData;
428 srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine
429 srcmgr.term_source = TermSource;
430 cinfo.src = &srcmgr;
431
432 JpegDecoderState state(input, input_size);
433 cinfo.client_data = &state;
434
435 // fill the file metadata into our buffer
436 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
437 return false;
438
439 // we want to always get RGB data out
440 switch (cinfo.jpeg_color_space) {
441 case JCS_GRAYSCALE:
442 case JCS_RGB:
443 case JCS_YCbCr:
444 cinfo.out_color_space = JCS_RGB;
445 break;
446 case JCS_CMYK:
447 case JCS_YCCK:
448 default:
449 // Mozilla errors out on these color spaces, so I presume that the jpeg
450 // library can't do automatic color space conversion for them. We don't
451 // care about these anyway.
452 return false;
453 }
454 cinfo.output_components = 3;
455
456 jpeg_calc_output_dimensions(&cinfo);
457 *w = cinfo.output_width;
458 *h = cinfo.output_height;
459
460 jpeg_start_decompress(&cinfo);
461
462 // FIXME(brettw) we may want to allow the capability for callers to request
463 // how to align row lengths as we do for the compressor.
464 int row_read_stride = cinfo.output_width * cinfo.output_components;
465
466 if (format == FORMAT_RGB) {
467 // easy case, row needs no conversion
468 int row_write_stride = row_read_stride;
469 output->resize(row_write_stride * cinfo.output_height);
470
471 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
472 unsigned char* rowptr = &(*output)[row * row_write_stride];
473 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
474 return false;
475 }
476 } else {
477 // Rows need conversion to output format: read into a temporary buffer and
478 // expand to the final one. Performance: we could avoid the extra
479 // allocation by doing the expansion in-place.
480 int row_write_stride;
481 void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
482 if (format == FORMAT_RGBA) {
483 row_write_stride = cinfo.output_width * 4;
484 converter = AddAlpha;
485 } else if (format == FORMAT_BGRA) {
486 row_write_stride = cinfo.output_width * 4;
487 converter = RGBtoBGRA;
488 } else {
489 NOTREACHED() << "Invalid pixel format";
490 jpeg_destroy_decompress(&cinfo);
491 return false;
492 }
493
494 output->resize(row_write_stride * cinfo.output_height);
495
496 scoped_array<unsigned char> row_data(new unsigned char[row_read_stride]);
497 unsigned char* rowptr = row_data.get();
498 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
499 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
500 return false;
501 converter(rowptr, *w, &(*output)[row * row_write_stride]);
502 }
503 }
504
505 jpeg_finish_decompress(&cinfo);
506 jpeg_destroy_decompress(&cinfo);
507 return true;
508 }
509
510 // static
511 SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) {
512 int w, h;
513 std::vector<unsigned char> data_vector;
514 // Use FORMAT_BGRA as that maps to Skia's 32 bit (kARGB_8888_Config) format.
515 if (!Decode(input, input_size, FORMAT_BGRA, &data_vector, &w, &h))
516 return NULL;
517
518 // Skia only handles 32 bit images.
519 int data_length = w * h * 4;
520
521 SkBitmap* bitmap = new SkBitmap();
522 bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
523 bitmap->allocPixels();
524 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
525
526 return bitmap;
527 }
OLDNEW
« no previous file with comments | « base/gfx/jpeg_codec.h ('k') | base/gfx/jpeg_codec_unittest.cc » ('j') | build/common.gypi » ('J')

Powered by Google App Engine
This is Rietveld 408576698