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

Side by Side Diff: ui/gfx/chromeos/codec/jpeg_codec_robust_slow.cc

Issue 569623002: Switch ChromeOS Chromium login to use IJG libjpeg (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix copyright and add unit test assertions Created 5 years, 7 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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/jpeg_codec.h" 5 #include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
6 6
7 #include <setjmp.h> 7 #include <setjmp.h>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
11 #include "third_party/skia/include/core/SkBitmap.h" 11 #include "third_party/skia/include/core/SkBitmap.h"
12 #include "third_party/skia/include/core/SkColorPriv.h" 12 #include "third_party/skia/include/core/SkColorPriv.h"
13 13
14 extern "C" { 14 extern "C" {
15 #if defined(USE_SYSTEM_LIBJPEG) 15 // IJG provides robust JPEG decode
16 #include <jpeglib.h>
17 #elif defined(USE_LIBJPEG_TURBO)
18 #include "third_party/libjpeg_turbo/jpeglib.h"
19 #else
20 #include "third_party/libjpeg/jpeglib.h" 16 #include "third_party/libjpeg/jpeglib.h"
21 #endif
22 } 17 }
23 18
24 namespace gfx { 19 namespace gfx {
25 20
26 // Encoder/decoder shared stuff ------------------------------------------------ 21 // Encoder/decoder shared stuff ------------------------------------------------
27 22
28 namespace { 23 namespace {
29 24
30 // used to pass error info through the JPEG library 25 // used to pass error info through the JPEG library
31 struct CoderErrorMgr { 26 struct CoderErrorMgr {
32 jpeg_error_mgr pub; 27 jpeg_error_mgr pub;
33 jmp_buf setjmp_buffer; 28 jmp_buf setjmp_buffer;
34 }; 29 };
35 30
36 void ErrorExit(jpeg_common_struct* cinfo) { 31 void ErrorExit(jpeg_common_struct* cinfo) {
37 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err); 32 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
38 33
39 // Return control to the setjmp point. 34 // Return control to the setjmp point.
40 longjmp(err->setjmp_buffer, false); 35 longjmp(err->setjmp_buffer, false);
41 } 36 }
42 37
43 } // namespace 38 } // namespace
44 39
45 // This method helps identify at run time which library chromium is using.
46 JPEGCodec::LibraryVariant JPEGCodec::JpegLibraryVariant() {
47 #if defined(USE_SYSTEM_LIBJPEG)
48 return SYSTEM_LIBJPEG;
49 #elif defined(USE_LIBJPEG_TURBO)
50 return LIBJPEG_TURBO;
51 #else
52 return IJG_LIBJPEG;
53 #endif
54 }
55
56 // Encoder ---------------------------------------------------------------------
57 //
58 // This code is based on nsJPEGEncoder from Mozilla.
59 // Copyright 2005 Google Inc. (Brett Wilson, contributor)
60
61 namespace {
62
63 // Initial size for the output buffer in the JpegEncoderState below.
64 static const int initial_output_buffer_size = 8192;
65
66 struct JpegEncoderState {
67 explicit JpegEncoderState(std::vector<unsigned char>* o)
68 : out(o),
69 image_buffer_used(0) {
70 }
71
72 // Output buffer, of which 'image_buffer_used' bytes are actually used (this
73 // will often be less than the actual size of the vector because we size it
74 // so that libjpeg can write directly into it.
75 std::vector<unsigned char>* out;
76
77 // Number of bytes in the 'out' buffer that are actually used (see above).
78 size_t image_buffer_used;
79 };
80
81 // Initializes the JpegEncoderState for encoding, and tells libjpeg about where
82 // the output buffer is.
83 //
84 // From the JPEG library:
85 // "Initialize destination. This is called by jpeg_start_compress() before
86 // any data is actually written. It must initialize next_output_byte and
87 // free_in_buffer. free_in_buffer must be initialized to a positive value."
88 void InitDestination(jpeg_compress_struct* cinfo) {
89 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
90 DCHECK(state->image_buffer_used == 0) << "initializing after use";
91
92 state->out->resize(initial_output_buffer_size);
93 state->image_buffer_used = 0;
94
95 cinfo->dest->next_output_byte = &(*state->out)[0];
96 cinfo->dest->free_in_buffer = initial_output_buffer_size;
97 }
98
99 // Resize the buffer that we give to libjpeg and update our and its state.
100 //
101 // From the JPEG library:
102 // "Callback used by libjpeg whenever the buffer has filled (free_in_buffer
103 // reaches zero). In typical applications, it should write out the *entire*
104 // buffer (use the saved start address and buffer length; ignore the current
105 // state of next_output_byte and free_in_buffer). Then reset the pointer &
106 // count to the start of the buffer, and return TRUE indicating that the
107 // buffer has been dumped. free_in_buffer must be set to a positive value
108 // when TRUE is returned. A FALSE return should only be used when I/O
109 // suspension is desired (this operating mode is discussed in the next
110 // section)."
111 boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) {
112 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
113
114 // note the new size, the buffer is full
115 state->image_buffer_used = state->out->size();
116
117 // expand buffer, just double size each time
118 state->out->resize(state->out->size() * 2);
119
120 // tell libjpeg where to write the next data
121 cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used];
122 cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used;
123 return 1;
124 }
125
126 // Cleans up the JpegEncoderState to prepare for returning in the final form.
127 //
128 // From the JPEG library:
129 // "Terminate destination --- called by jpeg_finish_compress() after all data
130 // has been written. In most applications, this must flush any data
131 // remaining in the buffer. Use either next_output_byte or free_in_buffer to
132 // determine how much data is in the buffer."
133 void TermDestination(jpeg_compress_struct* cinfo) {
134 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
135 DCHECK(state->out->size() >= state->image_buffer_used);
136
137 // update the used byte based on the next byte libjpeg would write to
138 state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0];
139 DCHECK(state->image_buffer_used < state->out->size()) <<
140 "JPEG library busted, got a bad image buffer size";
141
142 // update our buffer so that it exactly encompases the desired data
143 state->out->resize(state->image_buffer_used);
144 }
145
146 #if !defined(JCS_EXTENSIONS)
147 // Converts RGBA to RGB (removing the alpha values) to prepare to send data to
148 // libjpeg. This converts one row of data in rgba with the given width in
149 // pixels the the given rgb destination buffer (which should have enough space
150 // reserved for the final data).
151 void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
152 {
153 for (int x = 0; x < pixel_width; x++)
154 memcpy(&rgb[x * 3], &rgba[x * 4], 3);
155 }
156
157 // Converts BGRA to RGB by reordering the color components and dropping the
158 // alpha. This converts one row of data in rgba with the given width in
159 // pixels the the given rgb destination buffer (which should have enough space
160 // reserved for the final data).
161 void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
162 {
163 for (int x = 0; x < pixel_width; x++) {
164 const unsigned char* pixel_in = &bgra[x * 4];
165 unsigned char* pixel_out = &rgb[x * 3];
166 pixel_out[0] = pixel_in[2];
167 pixel_out[1] = pixel_in[1];
168 pixel_out[2] = pixel_in[0];
169 }
170 }
171 #endif // !defined(JCS_EXTENSIONS)
172
173 // This class destroys the given jpeg_compress object when it goes out of
174 // scope. It simplifies the error handling in Encode (and even applies to the
175 // success case).
176 class CompressDestroyer {
177 public:
178 CompressDestroyer() : cinfo_(NULL) {
179 }
180 ~CompressDestroyer() {
181 DestroyManagedObject();
182 }
183 void SetManagedObject(jpeg_compress_struct* ci) {
184 DestroyManagedObject();
185 cinfo_ = ci;
186 }
187 void DestroyManagedObject() {
188 if (cinfo_) {
189 jpeg_destroy_compress(cinfo_);
190 cinfo_ = NULL;
191 }
192 }
193 private:
194 jpeg_compress_struct* cinfo_;
195 };
196
197 } // namespace
198
199 bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format,
200 int w, int h, int row_byte_width,
201 int quality, std::vector<unsigned char>* output) {
202 jpeg_compress_struct cinfo;
203 CompressDestroyer destroyer;
204 destroyer.SetManagedObject(&cinfo);
205 output->clear();
206 #if !defined(JCS_EXTENSIONS)
207 unsigned char* row_buffer = NULL;
208 #endif
209
210 // We set up the normal JPEG error routines, then override error_exit.
211 // This must be done before the call to create_compress.
212 CoderErrorMgr errmgr;
213 cinfo.err = jpeg_std_error(&errmgr.pub);
214 errmgr.pub.error_exit = ErrorExit;
215
216 // Establish the setjmp return context for ErrorExit to use.
217 if (setjmp(errmgr.setjmp_buffer)) {
218 // If we get here, the JPEG code has signaled an error.
219 // MSDN notes: "if you intend your code to be portable, do not rely on
220 // correct destruction of frame-based objects when executing a nonlocal
221 // goto using a call to longjmp." So we delete the CompressDestroyer's
222 // object manually instead.
223 destroyer.DestroyManagedObject();
224 #if !defined(JCS_EXTENSIONS)
225 delete[] row_buffer;
226 #endif
227 return false;
228 }
229
230 // The destroyer will destroy() cinfo on exit.
231 jpeg_create_compress(&cinfo);
232
233 cinfo.image_width = w;
234 cinfo.image_height = h;
235 cinfo.input_components = 3;
236 #ifdef JCS_EXTENSIONS
237 // Choose an input colorspace and return if it is an unsupported one. Since
238 // libjpeg-turbo supports all input formats used by Chromium (i.e. RGB, RGBA,
239 // and BGRA), we just map the input parameters to a colorspace used by
240 // libjpeg-turbo.
241 if (format == FORMAT_RGB) {
242 cinfo.input_components = 3;
243 cinfo.in_color_space = JCS_RGB;
244 } else if (format == FORMAT_RGBA ||
245 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
246 cinfo.input_components = 4;
247 cinfo.in_color_space = JCS_EXT_RGBX;
248 } else if (format == FORMAT_BGRA ||
249 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
250 cinfo.input_components = 4;
251 cinfo.in_color_space = JCS_EXT_BGRX;
252 } else {
253 // We can exit this function without calling jpeg_destroy_compress() because
254 // CompressDestroyer automaticaly calls it.
255 NOTREACHED() << "Invalid pixel format";
256 return false;
257 }
258 #else
259 cinfo.in_color_space = JCS_RGB;
260 #endif
261 cinfo.data_precision = 8;
262
263 jpeg_set_defaults(&cinfo);
264 jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
265
266 // set up the destination manager
267 jpeg_destination_mgr destmgr;
268 destmgr.init_destination = InitDestination;
269 destmgr.empty_output_buffer = EmptyOutputBuffer;
270 destmgr.term_destination = TermDestination;
271 cinfo.dest = &destmgr;
272
273 JpegEncoderState state(output);
274 cinfo.client_data = &state;
275
276 jpeg_start_compress(&cinfo, 1);
277
278 // feed it the rows, doing necessary conversions for the color format
279 #ifdef JCS_EXTENSIONS
280 // This function already returns when the input format is not supported by
281 // libjpeg-turbo and needs conversion. Therefore, we just encode lines without
282 // conversions.
283 while (cinfo.next_scanline < cinfo.image_height) {
284 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
285 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
286 }
287 #else
288 if (format == FORMAT_RGB) {
289 // no conversion necessary
290 while (cinfo.next_scanline < cinfo.image_height) {
291 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
292 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
293 }
294 } else {
295 // get the correct format converter
296 void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
297 if (format == FORMAT_RGBA ||
298 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
299 converter = StripAlpha;
300 } else if (format == FORMAT_BGRA ||
301 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
302 converter = BGRAtoRGB;
303 } else {
304 NOTREACHED() << "Invalid pixel format";
305 return false;
306 }
307
308 // output row after converting
309 row_buffer = new unsigned char[w * 3];
310
311 while (cinfo.next_scanline < cinfo.image_height) {
312 converter(&input[cinfo.next_scanline * row_byte_width], w, row_buffer);
313 jpeg_write_scanlines(&cinfo, &row_buffer, 1);
314 }
315 delete[] row_buffer;
316 }
317 #endif
318
319 jpeg_finish_compress(&cinfo);
320 return true;
321 }
322
323 // Decoder -------------------------------------------------------------------- 40 // Decoder --------------------------------------------------------------------
324 41
325 namespace { 42 namespace {
326 43
327 struct JpegDecoderState { 44 struct JpegDecoderState {
328 JpegDecoderState(const unsigned char* in, size_t len) 45 JpegDecoderState(const unsigned char* in, size_t len)
329 : input_buffer(in), input_buffer_length(len) { 46 : input_buffer(in), input_buffer_length(len) {
330 } 47 }
331 48
332 const unsigned char* input_buffer; 49 const unsigned char* input_buffer;
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 jpeg_destroy_decompress(cinfo_); 158 jpeg_destroy_decompress(cinfo_);
442 cinfo_ = NULL; 159 cinfo_ = NULL;
443 } 160 }
444 } 161 }
445 private: 162 private:
446 jpeg_decompress_struct* cinfo_; 163 jpeg_decompress_struct* cinfo_;
447 }; 164 };
448 165
449 } // namespace 166 } // namespace
450 167
451 bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, 168 bool JPEGCodecRobustSlow::Decode(const unsigned char* input, size_t input_size,
452 ColorFormat format, std::vector<unsigned char>* output, 169 ColorFormat format,
453 int* w, int* h) { 170 std::vector<unsigned char>* output, int* w,
171 int* h) {
454 jpeg_decompress_struct cinfo; 172 jpeg_decompress_struct cinfo;
455 DecompressDestroyer destroyer; 173 DecompressDestroyer destroyer;
456 destroyer.SetManagedObject(&cinfo); 174 destroyer.SetManagedObject(&cinfo);
457 output->clear(); 175 output->clear();
458 176
459 // We set up the normal JPEG error routines, then override error_exit. 177 // We set up the normal JPEG error routines, then override error_exit.
460 // This must be done before the call to create_decompress. 178 // This must be done before the call to create_decompress.
461 CoderErrorMgr errmgr; 179 CoderErrorMgr errmgr;
462 cinfo.err = jpeg_std_error(&errmgr.pub); 180 cinfo.err = jpeg_std_error(&errmgr.pub);
463 errmgr.pub.error_exit = ErrorExit; 181 errmgr.pub.error_exit = ErrorExit;
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 } 314 }
597 } 315 }
598 #endif 316 #endif
599 317
600 jpeg_finish_decompress(&cinfo); 318 jpeg_finish_decompress(&cinfo);
601 jpeg_destroy_decompress(&cinfo); 319 jpeg_destroy_decompress(&cinfo);
602 return true; 320 return true;
603 } 321 }
604 322
605 // static 323 // static
606 SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) { 324 SkBitmap* JPEGCodecRobustSlow::Decode(const unsigned char* input,
325 size_t input_size) {
607 int w, h; 326 int w, h;
608 std::vector<unsigned char> data_vector; 327 std::vector<unsigned char> data_vector;
609 if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h)) 328 if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h))
610 return NULL; 329 return NULL;
611 330
612 // Skia only handles 32 bit images. 331 // Skia only handles 32 bit images.
613 int data_length = w * h * 4; 332 int data_length = w * h * 4;
614 333
615 SkBitmap* bitmap = new SkBitmap(); 334 SkBitmap* bitmap = new SkBitmap();
616 bitmap->allocN32Pixels(w, h); 335 bitmap->allocN32Pixels(w, h);
617 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length); 336 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
618 337
619 return bitmap; 338 return bitmap;
620 } 339 }
621 340
622 } // namespace gfx 341 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h ('k') | ui/gfx/chromeos/codec/jpeg_codec_robust_slow_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698