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

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

Issue 2751103004: Revert of Move ui/gfx/codec/ into its own component. (Closed)
Patch Set: Created 3 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/gfx//codec/chromeos/jpeg_codec_robust_slow.h"
6
7 #include <setjmp.h>
8
9 #include <memory>
10
11 #include "base/logging.h"
12
13 extern "C" {
14 // IJG provides robust JPEG decode
15 #include "third_party/libjpeg/jpeglib.h"
16 }
17
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "third_party/skia/include/core/SkColorPriv.h"
20
21 namespace gfx {
22
23 // Encoder/decoder shared stuff ------------------------------------------------
24
25 namespace {
26
27 // used to pass error info through the JPEG library
28 struct CoderErrorMgr {
29 jpeg_error_mgr pub;
30 jmp_buf setjmp_buffer;
31 };
32
33 void ErrorExit(jpeg_common_struct* cinfo) {
34 CoderErrorMgr* err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
35
36 // Return control to the setjmp point.
37 longjmp(err->setjmp_buffer, false);
38 }
39
40 } // namespace
41
42 // Decoder --------------------------------------------------------------------
43
44 namespace {
45
46 struct JpegDecoderState {
47 JpegDecoderState(const unsigned char* in, size_t len)
48 : input_buffer(in), input_buffer_length(len) {}
49
50 const unsigned char* input_buffer;
51 size_t input_buffer_length;
52 };
53
54 // Callback to initialize the source.
55 //
56 // From the JPEG library:
57 // "Initialize source. This is called by jpeg_read_header() before any data is
58 // actually read. May leave bytes_in_buffer set to 0 (in which case a
59 // fill_input_buffer() call will occur immediately)."
60 void InitSource(j_decompress_ptr cinfo) {
61 JpegDecoderState* state = static_cast<JpegDecoderState*>(cinfo->client_data);
62 cinfo->src->next_input_byte = state->input_buffer;
63 cinfo->src->bytes_in_buffer = state->input_buffer_length;
64 }
65
66 // Callback to fill the buffer. Since our buffer already contains all the data,
67 // we should never need to provide more data. If libjpeg thinks it needs more
68 // data, our input is probably corrupt.
69 //
70 // From the JPEG library:
71 // "This is called whenever bytes_in_buffer has reached zero and more data is
72 // wanted. In typical applications, it should read fresh data into the buffer
73 // (ignoring the current state of next_input_byte and bytes_in_buffer), reset
74 // the pointer & count to the start of the buffer, and return TRUE indicating
75 // that the buffer has been reloaded. It is not necessary to fill the buffer
76 // entirely, only to obtain at least one more byte. bytes_in_buffer MUST be
77 // set to a positive value if TRUE is returned. A FALSE return should only
78 // be used when I/O suspension is desired."
79 boolean FillInputBuffer(j_decompress_ptr cinfo) {
80 return false;
81 }
82
83 // Skip data in the buffer. Since we have all the data at once, this operation
84 // is easy. It is not clear if this ever gets called because the JPEG library
85 // should be able to do the skip itself (it has all the data).
86 //
87 // From the JPEG library:
88 // "Skip num_bytes worth of data. The buffer pointer and count should be
89 // advanced over num_bytes input bytes, refilling the buffer as needed. This
90 // is used to skip over a potentially large amount of uninteresting data
91 // (such as an APPn marker). In some applications it may be possible to
92 // optimize away the reading of the skipped data, but it's not clear that
93 // being smart is worth much trouble; large skips are uncommon.
94 // bytes_in_buffer may be zero on return. A zero or negative skip count
95 // should be treated as a no-op."
96 void SkipInputData(j_decompress_ptr cinfo, long num_bytes) {
97 if (num_bytes > static_cast<long>(cinfo->src->bytes_in_buffer)) {
98 // Since all our data should be in the buffer, trying to skip beyond it
99 // means that there is some kind of error or corrupt input data. A 0 for
100 // bytes left means it will call FillInputBuffer which will then fail.
101 cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer;
102 cinfo->src->bytes_in_buffer = 0;
103 } else if (num_bytes > 0) {
104 cinfo->src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
105 cinfo->src->next_input_byte += num_bytes;
106 }
107 }
108
109 // Our source doesn't need any cleanup, so this is a NOP.
110 //
111 // From the JPEG library:
112 // "Terminate source --- called by jpeg_finish_decompress() after all data has
113 // been read to clean up JPEG source manager. NOT called by jpeg_abort() or
114 // jpeg_destroy()."
115 void TermSource(j_decompress_ptr cinfo) {}
116
117 #if !defined(JCS_EXTENSIONS)
118 // Converts one row of rgb data to rgba data by adding a fully-opaque alpha
119 // value.
120 void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
121 for (int x = 0; x < pixel_width; x++) {
122 memcpy(&rgba[x * 4], &rgb[x * 3], 3);
123 rgba[x * 4 + 3] = 0xff;
124 }
125 }
126
127 // Converts one row of RGB data to BGRA by reordering the color components and
128 // adding alpha values of 0xff.
129 void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb) {
130 for (int x = 0; x < pixel_width; x++) {
131 const unsigned char* pixel_in = &bgra[x * 3];
132 unsigned char* pixel_out = &rgb[x * 4];
133 pixel_out[0] = pixel_in[2];
134 pixel_out[1] = pixel_in[1];
135 pixel_out[2] = pixel_in[0];
136 pixel_out[3] = 0xff;
137 }
138 }
139 #endif // !defined(JCS_EXTENSIONS)
140
141 // This class destroys the given jpeg_decompress object when it goes out of
142 // scope. It simplifies the error handling in Decode (and even applies to the
143 // success case).
144 class DecompressDestroyer {
145 public:
146 DecompressDestroyer() : cinfo_(NULL) {}
147 ~DecompressDestroyer() { DestroyManagedObject(); }
148 void SetManagedObject(jpeg_decompress_struct* ci) {
149 DestroyManagedObject();
150 cinfo_ = ci;
151 }
152 void DestroyManagedObject() {
153 if (cinfo_) {
154 jpeg_destroy_decompress(cinfo_);
155 cinfo_ = NULL;
156 }
157 }
158
159 private:
160 jpeg_decompress_struct* cinfo_;
161 };
162
163 } // namespace
164
165 bool JPEGCodecRobustSlow::Decode(const unsigned char* input,
166 size_t input_size,
167 ColorFormat format,
168 std::vector<unsigned char>* output,
169 int* w,
170 int* h) {
171 jpeg_decompress_struct cinfo;
172 DecompressDestroyer destroyer;
173 destroyer.SetManagedObject(&cinfo);
174 output->clear();
175
176 // We set up the normal JPEG error routines, then override error_exit.
177 // This must be done before the call to create_decompress.
178 CoderErrorMgr errmgr;
179 cinfo.err = jpeg_std_error(&errmgr.pub);
180 errmgr.pub.error_exit = ErrorExit;
181 // Establish the setjmp return context for ErrorExit to use.
182 if (setjmp(errmgr.setjmp_buffer)) {
183 // If we get here, the JPEG code has signaled an error.
184 // See note in JPEGCodec::Encode() for why we need to destroy the cinfo
185 // manually here.
186 destroyer.DestroyManagedObject();
187 return false;
188 }
189
190 // The destroyer will destroy() cinfo on exit. We don't want to set the
191 // destroyer's object until cinfo is initialized.
192 jpeg_create_decompress(&cinfo);
193
194 // set up the source manager
195 jpeg_source_mgr srcmgr;
196 srcmgr.init_source = InitSource;
197 srcmgr.fill_input_buffer = FillInputBuffer;
198 srcmgr.skip_input_data = SkipInputData;
199 srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine
200 srcmgr.term_source = TermSource;
201 cinfo.src = &srcmgr;
202
203 JpegDecoderState state(input, input_size);
204 cinfo.client_data = &state;
205
206 // fill the file metadata into our buffer
207 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
208 return false;
209
210 // we want to always get RGB data out
211 switch (cinfo.jpeg_color_space) {
212 case JCS_GRAYSCALE:
213 case JCS_RGB:
214 case JCS_YCbCr:
215 #ifdef JCS_EXTENSIONS
216 // Choose an output colorspace and return if it is an unsupported one.
217 // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
218 // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input
219 // parameters to a colorspace.
220 if (format == FORMAT_RGB) {
221 cinfo.out_color_space = JCS_RGB;
222 cinfo.output_components = 3;
223 } else if (format == FORMAT_RGBA ||
224 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
225 cinfo.out_color_space = JCS_EXT_RGBX;
226 cinfo.output_components = 4;
227 } else if (format == FORMAT_BGRA ||
228 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
229 cinfo.out_color_space = JCS_EXT_BGRX;
230 cinfo.output_components = 4;
231 } else {
232 // We can exit this function without calling jpeg_destroy_decompress()
233 // because DecompressDestroyer automaticaly calls it.
234 NOTREACHED() << "Invalid pixel format";
235 return false;
236 }
237 #else
238 cinfo.out_color_space = JCS_RGB;
239 #endif
240 break;
241 case JCS_CMYK:
242 case JCS_YCCK:
243 default:
244 // Mozilla errors out on these color spaces, so I presume that the jpeg
245 // library can't do automatic color space conversion for them. We don't
246 // care about these anyway.
247 return false;
248 }
249 #ifndef JCS_EXTENSIONS
250 cinfo.output_components = 3;
251 #endif
252
253 jpeg_calc_output_dimensions(&cinfo);
254 *w = cinfo.output_width;
255 *h = cinfo.output_height;
256
257 jpeg_start_decompress(&cinfo);
258
259 // FIXME(brettw) we may want to allow the capability for callers to request
260 // how to align row lengths as we do for the compressor.
261 int row_read_stride = cinfo.output_width * cinfo.output_components;
262
263 #ifdef JCS_EXTENSIONS
264 // Create memory for a decoded image and write decoded lines to the memory
265 // without conversions same as JPEGCodec::Encode().
266 int row_write_stride = row_read_stride;
267 output->resize(row_write_stride * cinfo.output_height);
268
269 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
270 unsigned char* rowptr = &(*output)[row * row_write_stride];
271 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
272 return false;
273 }
274 #else
275 if (format == FORMAT_RGB) {
276 // easy case, row needs no conversion
277 int row_write_stride = row_read_stride;
278 output->resize(row_write_stride * cinfo.output_height);
279
280 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
281 unsigned char* rowptr = &(*output)[row * row_write_stride];
282 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
283 return false;
284 }
285 } else {
286 // Rows need conversion to output format: read into a temporary buffer and
287 // expand to the final one. Performance: we could avoid the extra
288 // allocation by doing the expansion in-place.
289 int row_write_stride;
290 void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
291 if (format == FORMAT_RGBA ||
292 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
293 row_write_stride = cinfo.output_width * 4;
294 converter = AddAlpha;
295 } else if (format == FORMAT_BGRA ||
296 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
297 row_write_stride = cinfo.output_width * 4;
298 converter = RGBtoBGRA;
299 } else {
300 NOTREACHED() << "Invalid pixel format";
301 jpeg_destroy_decompress(&cinfo);
302 return false;
303 }
304
305 output->resize(row_write_stride * cinfo.output_height);
306
307 std::unique_ptr<unsigned char[]> row_data(
308 new unsigned char[row_read_stride]);
309 unsigned char* rowptr = row_data.get();
310 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
311 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
312 return false;
313 converter(rowptr, *w, &(*output)[row * row_write_stride]);
314 }
315 }
316 #endif
317
318 jpeg_finish_decompress(&cinfo);
319 jpeg_destroy_decompress(&cinfo);
320 return true;
321 }
322
323 // static
324 SkBitmap* JPEGCodecRobustSlow::Decode(const unsigned char* input,
325 size_t input_size) {
326 int w, h;
327 std::vector<unsigned char> data_vector;
328 if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h))
329 return NULL;
330
331 // Skia only handles 32 bit images.
332 int data_length = w * h * 4;
333
334 SkBitmap* bitmap = new SkBitmap();
335 bitmap->allocN32Pixels(w, h);
336 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
337
338 return bitmap;
339 }
340
341 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/codec/chromeos/jpeg_codec_robust_slow.h ('k') | ui/gfx/codec/chromeos/jpeg_codec_robust_slow_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698