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

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

Issue 2755563002: Move ui/gfx/codec/ into its own component. (Closed)
Patch Set: none 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/chromeos/codec/jpeg_codec_robust_slow.h"
6
7 #include <setjmp.h>
8
9 #include <memory>
10
11 #include "base/logging.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "third_party/skia/include/core/SkColorPriv.h"
14
15 extern "C" {
16 // IJG provides robust JPEG decode
17 #include "third_party/libjpeg/jpeglib.h"
18 }
19
20 namespace gfx {
21
22 // Encoder/decoder shared stuff ------------------------------------------------
23
24 namespace {
25
26 // used to pass error info through the JPEG library
27 struct CoderErrorMgr {
28 jpeg_error_mgr pub;
29 jmp_buf setjmp_buffer;
30 };
31
32 void ErrorExit(jpeg_common_struct* cinfo) {
33 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
34
35 // Return control to the setjmp point.
36 longjmp(err->setjmp_buffer, false);
37 }
38
39 } // namespace
40
41 // Decoder --------------------------------------------------------------------
42
43 namespace {
44
45 struct JpegDecoderState {
46 JpegDecoderState(const unsigned char* in, size_t len)
47 : input_buffer(in), input_buffer_length(len) {
48 }
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
118 #if !defined(JCS_EXTENSIONS)
119 // Converts one row of rgb data to rgba data by adding a fully-opaque alpha
120 // value.
121 void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
122 for (int x = 0; x < pixel_width; x++) {
123 memcpy(&rgba[x * 4], &rgb[x * 3], 3);
124 rgba[x * 4 + 3] = 0xff;
125 }
126 }
127
128 // Converts one row of RGB data to BGRA by reordering the color components and
129 // adding alpha values of 0xff.
130 void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
131 {
132 for (int x = 0; x < pixel_width; x++) {
133 const unsigned char* pixel_in = &bgra[x * 3];
134 unsigned char* pixel_out = &rgb[x * 4];
135 pixel_out[0] = pixel_in[2];
136 pixel_out[1] = pixel_in[1];
137 pixel_out[2] = pixel_in[0];
138 pixel_out[3] = 0xff;
139 }
140 }
141 #endif // !defined(JCS_EXTENSIONS)
142
143 // This class destroys the given jpeg_decompress object when it goes out of
144 // scope. It simplifies the error handling in Decode (and even applies to the
145 // success case).
146 class DecompressDestroyer {
147 public:
148 DecompressDestroyer() : cinfo_(NULL) {
149 }
150 ~DecompressDestroyer() {
151 DestroyManagedObject();
152 }
153 void SetManagedObject(jpeg_decompress_struct* ci) {
154 DestroyManagedObject();
155 cinfo_ = ci;
156 }
157 void DestroyManagedObject() {
158 if (cinfo_) {
159 jpeg_destroy_decompress(cinfo_);
160 cinfo_ = NULL;
161 }
162 }
163 private:
164 jpeg_decompress_struct* cinfo_;
165 };
166
167 } // namespace
168
169 bool JPEGCodecRobustSlow::Decode(const unsigned char* input, size_t input_size,
170 ColorFormat format,
171 std::vector<unsigned char>* output, int* w,
172 int* h) {
173 jpeg_decompress_struct cinfo;
174 DecompressDestroyer destroyer;
175 destroyer.SetManagedObject(&cinfo);
176 output->clear();
177
178 // We set up the normal JPEG error routines, then override error_exit.
179 // This must be done before the call to create_decompress.
180 CoderErrorMgr errmgr;
181 cinfo.err = jpeg_std_error(&errmgr.pub);
182 errmgr.pub.error_exit = ErrorExit;
183 // Establish the setjmp return context for ErrorExit to use.
184 if (setjmp(errmgr.setjmp_buffer)) {
185 // If we get here, the JPEG code has signaled an error.
186 // See note in JPEGCodec::Encode() for why we need to destroy the cinfo
187 // manually here.
188 destroyer.DestroyManagedObject();
189 return false;
190 }
191
192 // The destroyer will destroy() cinfo on exit. We don't want to set the
193 // destroyer's object until cinfo is initialized.
194 jpeg_create_decompress(&cinfo);
195
196 // set up the source manager
197 jpeg_source_mgr srcmgr;
198 srcmgr.init_source = InitSource;
199 srcmgr.fill_input_buffer = FillInputBuffer;
200 srcmgr.skip_input_data = SkipInputData;
201 srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine
202 srcmgr.term_source = TermSource;
203 cinfo.src = &srcmgr;
204
205 JpegDecoderState state(input, input_size);
206 cinfo.client_data = &state;
207
208 // fill the file metadata into our buffer
209 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
210 return false;
211
212 // we want to always get RGB data out
213 switch (cinfo.jpeg_color_space) {
214 case JCS_GRAYSCALE:
215 case JCS_RGB:
216 case JCS_YCbCr:
217 #ifdef JCS_EXTENSIONS
218 // Choose an output colorspace and return if it is an unsupported one.
219 // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
220 // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input
221 // parameters to a colorspace.
222 if (format == FORMAT_RGB) {
223 cinfo.out_color_space = JCS_RGB;
224 cinfo.output_components = 3;
225 } else if (format == FORMAT_RGBA ||
226 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
227 cinfo.out_color_space = JCS_EXT_RGBX;
228 cinfo.output_components = 4;
229 } else if (format == FORMAT_BGRA ||
230 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
231 cinfo.out_color_space = JCS_EXT_BGRX;
232 cinfo.output_components = 4;
233 } else {
234 // We can exit this function without calling jpeg_destroy_decompress()
235 // because DecompressDestroyer automaticaly calls it.
236 NOTREACHED() << "Invalid pixel format";
237 return false;
238 }
239 #else
240 cinfo.out_color_space = JCS_RGB;
241 #endif
242 break;
243 case JCS_CMYK:
244 case JCS_YCCK:
245 default:
246 // Mozilla errors out on these color spaces, so I presume that the jpeg
247 // library can't do automatic color space conversion for them. We don't
248 // care about these anyway.
249 return false;
250 }
251 #ifndef JCS_EXTENSIONS
252 cinfo.output_components = 3;
253 #endif
254
255 jpeg_calc_output_dimensions(&cinfo);
256 *w = cinfo.output_width;
257 *h = cinfo.output_height;
258
259 jpeg_start_decompress(&cinfo);
260
261 // FIXME(brettw) we may want to allow the capability for callers to request
262 // how to align row lengths as we do for the compressor.
263 int row_read_stride = cinfo.output_width * cinfo.output_components;
264
265 #ifdef JCS_EXTENSIONS
266 // Create memory for a decoded image and write decoded lines to the memory
267 // without conversions same as JPEGCodec::Encode().
268 int row_write_stride = row_read_stride;
269 output->resize(row_write_stride * cinfo.output_height);
270
271 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
272 unsigned char* rowptr = &(*output)[row * row_write_stride];
273 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
274 return false;
275 }
276 #else
277 if (format == FORMAT_RGB) {
278 // easy case, row needs no conversion
279 int row_write_stride = row_read_stride;
280 output->resize(row_write_stride * cinfo.output_height);
281
282 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
283 unsigned char* rowptr = &(*output)[row * row_write_stride];
284 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
285 return false;
286 }
287 } else {
288 // Rows need conversion to output format: read into a temporary buffer and
289 // expand to the final one. Performance: we could avoid the extra
290 // allocation by doing the expansion in-place.
291 int row_write_stride;
292 void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
293 if (format == FORMAT_RGBA ||
294 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
295 row_write_stride = cinfo.output_width * 4;
296 converter = AddAlpha;
297 } else if (format == FORMAT_BGRA ||
298 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
299 row_write_stride = cinfo.output_width * 4;
300 converter = RGBtoBGRA;
301 } else {
302 NOTREACHED() << "Invalid pixel format";
303 jpeg_destroy_decompress(&cinfo);
304 return false;
305 }
306
307 output->resize(row_write_stride * cinfo.output_height);
308
309 std::unique_ptr<unsigned char[]> row_data(
310 new unsigned char[row_read_stride]);
311 unsigned char* rowptr = row_data.get();
312 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
313 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
314 return false;
315 converter(rowptr, *w, &(*output)[row * row_write_stride]);
316 }
317 }
318 #endif
319
320 jpeg_finish_decompress(&cinfo);
321 jpeg_destroy_decompress(&cinfo);
322 return true;
323 }
324
325 // static
326 SkBitmap* JPEGCodecRobustSlow::Decode(const unsigned char* input,
327 size_t input_size) {
328 int w, h;
329 std::vector<unsigned char> data_vector;
330 if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h))
331 return NULL;
332
333 // Skia only handles 32 bit images.
334 int data_length = w * h * 4;
335
336 SkBitmap* bitmap = new SkBitmap();
337 bitmap->allocN32Pixels(w, h);
338 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
339
340 return bitmap;
341 }
342
343 } // 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