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

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

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

Powered by Google App Engine
This is Rietveld 408576698