Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /*
2 * Copyright 2012 The LibYuv Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
11 #include "libyuv/mjpeg_decoder.h"
13 #ifdef HAVE_JPEG
14 #include <assert.h>
16 #if !defined(__pnacl__) && !defined(__CLR_VER) && !defined(COVERAGE_ENABLED) &&\
17 !defined(TARGET_IPHONE_SIMULATOR)
18 // Must be included before jpeglib.
19 #include <setjmp.h>
20 #define HAVE_SETJMP
21 #endif
22 struct FILE; // For jpeglib.h.
24 // C++ build requires extern C for jpeg internals.
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
29 #include <jpeglib.h>
31 #ifdef __cplusplus
32 } // extern "C"
33 #endif
35 #include "libyuv/planar_functions.h" // For CopyPlane().
37 namespace libyuv {
39 #ifdef HAVE_SETJMP
40 struct SetJmpErrorMgr {
41 jpeg_error_mgr base; // Must be at the top
42 jmp_buf setjmp_buffer;
43 };
44 #endif
46 const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN;
47 const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE;
48 const int MJpegDecoder::kColorSpaceRgb = JCS_RGB;
49 const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr;
50 const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK;
51 const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK;
53 MJpegDecoder::MJpegDecoder()
54 : has_scanline_padding_(LIBYUV_FALSE),
55 num_outbufs_(0),
56 scanlines_(NULL),
57 scanlines_sizes_(NULL),
58 databuf_(NULL),
59 databuf_strides_(NULL) {
60 decompress_struct_ = new jpeg_decompress_struct;
61 source_mgr_ = new jpeg_source_mgr;
62 #ifdef HAVE_SETJMP
63 error_mgr_ = new SetJmpErrorMgr;
64 decompress_struct_->err = jpeg_std_error(&error_mgr_->base);
65 // Override standard exit()-based error handler.
66 error_mgr_->base.error_exit = &ErrorHandler;
67 #endif
68 decompress_struct_->client_data = NULL;
69 source_mgr_->init_source = &init_source;
70 source_mgr_->fill_input_buffer = &fill_input_buffer;
71 source_mgr_->skip_input_data = &skip_input_data;
72 source_mgr_->resync_to_restart = &jpeg_resync_to_restart;
73 source_mgr_->term_source = &term_source;
74 jpeg_create_decompress(decompress_struct_);
75 decompress_struct_->src = source_mgr_;
76 buf_vec_.buffers = &buf_;
77 buf_vec_.len = 1;
78 }
80 MJpegDecoder::~MJpegDecoder() {
81 jpeg_destroy_decompress(decompress_struct_);
82 delete decompress_struct_;
83 delete source_mgr_;
84 #ifdef HAVE_SETJMP
85 delete error_mgr_;
86 #endif
87 DestroyOutputBuffers();
88 }
90 LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8* src, size_t src_len) {
91 if (!ValidateJpeg(src, src_len)) {
92 return LIBYUV_FALSE;
93 }
95 buf_.data = src;
96 buf_.len = (int)(src_len);
97 buf_vec_.pos = 0;
98 decompress_struct_->client_data = &buf_vec_;
99 #ifdef HAVE_SETJMP
100 if (setjmp(error_mgr_->setjmp_buffer)) {
101 // We called jpeg_read_header, it experienced an error, and we called
102 // longjmp() and rewound the stack to here. Return error.
103 return LIBYUV_FALSE;
104 }
105 #endif
106 if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) {
107 // ERROR: Bad MJPEG header
108 return LIBYUV_FALSE;
109 }
110 AllocOutputBuffers(GetNumComponents());
111 for (int i = 0; i < num_outbufs_; ++i) {
112 int scanlines_size = GetComponentScanlinesPerImcuRow(i);
113 if (scanlines_sizes_[i] != scanlines_size) {
114 if (scanlines_[i]) {
115 delete scanlines_[i];
116 }
117 scanlines_[i] = new uint8* [scanlines_size];
118 scanlines_sizes_[i] = scanlines_size;
119 }
121 // We allocate padding for the final scanline to pad it up to DCTSIZE bytes
122 // to avoid memory errors, since jpeglib only reads full MCUs blocks. For
123 // the preceding scanlines, the padding is not needed/wanted because the
124 // following addresses will already be valid (they are the initial bytes of
125 // the next scanline) and will be overwritten when jpeglib writes out that
126 // next scanline.
127 int databuf_stride = GetComponentStride(i);
128 int databuf_size = scanlines_size * databuf_stride;
129 if (databuf_strides_[i] != databuf_stride) {
130 if (databuf_[i]) {
131 delete databuf_[i];
132 }
133 databuf_[i] = new uint8[databuf_size];
134 databuf_strides_[i] = databuf_stride;
135 }
137 if (GetComponentStride(i) != GetComponentWidth(i)) {
138 has_scanline_padding_ = LIBYUV_TRUE;
139 }
140 }
141 return LIBYUV_TRUE;
142 }
144 static int DivideAndRoundUp(int numerator, int denominator) {
145 return (numerator + denominator - 1) / denominator;
146 }
148 static int DivideAndRoundDown(int numerator, int denominator) {
149 return numerator / denominator;
150 }
152 // Returns width of the last loaded frame.
153 int MJpegDecoder::GetWidth() {
154 return decompress_struct_->image_width;
155 }
157 // Returns height of the last loaded frame.
158 int MJpegDecoder::GetHeight() {
159 return decompress_struct_->image_height;
160 }
162 // Returns format of the last loaded frame. The return value is one of the
163 // kColorSpace* constants.
164 int MJpegDecoder::GetColorSpace() {
165 return decompress_struct_->jpeg_color_space;
166 }
168 // Number of color components in the color space.
169 int MJpegDecoder::GetNumComponents() {
170 return decompress_struct_->num_components;
171 }
173 // Sample factors of the n-th component.
174 int MJpegDecoder::GetHorizSampFactor(int component) {
175 return decompress_struct_->comp_info[component].h_samp_factor;
176 }
178 int MJpegDecoder::GetVertSampFactor(int component) {
179 return decompress_struct_->comp_info[component].v_samp_factor;
180 }
182 int MJpegDecoder::GetHorizSubSampFactor(int component) {
183 return decompress_struct_->max_h_samp_factor /
184 GetHorizSampFactor(component);
185 }
187 int MJpegDecoder::GetVertSubSampFactor(int component) {
188 return decompress_struct_->max_v_samp_factor /
189 GetVertSampFactor(component);
190 }
192 int MJpegDecoder::GetImageScanlinesPerImcuRow() {
193 return decompress_struct_->max_v_samp_factor * DCTSIZE;
194 }
196 int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) {
197 int vs = GetVertSubSampFactor(component);
198 return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs);
199 }
201 int MJpegDecoder::GetComponentWidth(int component) {
202 int hs = GetHorizSubSampFactor(component);
203 return DivideAndRoundUp(GetWidth(), hs);
204 }
206 int MJpegDecoder::GetComponentHeight(int component) {
207 int vs = GetVertSubSampFactor(component);
208 return DivideAndRoundUp(GetHeight(), vs);
209 }
211 // Get width in bytes padded out to a multiple of DCTSIZE
212 int MJpegDecoder::GetComponentStride(int component) {
213 return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1);
214 }
216 int MJpegDecoder::GetComponentSize(int component) {
217 return GetComponentWidth(component) * GetComponentHeight(component);
218 }
220 LIBYUV_BOOL MJpegDecoder::UnloadFrame() {
221 #ifdef HAVE_SETJMP
222 if (setjmp(error_mgr_->setjmp_buffer)) {
223 // We called jpeg_abort_decompress, it experienced an error, and we called
224 // longjmp() and rewound the stack to here. Return error.
225 return LIBYUV_FALSE;
226 }
227 #endif
228 jpeg_abort_decompress(decompress_struct_);
229 return LIBYUV_TRUE;
230 }
232 // TODO(fbarchard): Allow rectangle to be specified: x, y, width, height.
233 LIBYUV_BOOL MJpegDecoder::DecodeToBuffers(
234 uint8** planes, int dst_width, int dst_height) {
235 if (dst_width != GetWidth() ||
236 dst_height > GetHeight()) {
237 // ERROR: Bad dimensions
238 return LIBYUV_FALSE;
239 }
240 #ifdef HAVE_SETJMP
241 if (setjmp(error_mgr_->setjmp_buffer)) {
242 // We called into jpeglib, it experienced an error sometime during this
243 // function call, and we called longjmp() and rewound the stack to here.
244 // Return error.
245 return LIBYUV_FALSE;
246 }
247 #endif
248 if (!StartDecode()) {
249 return LIBYUV_FALSE;
250 }
251 SetScanlinePointers(databuf_);
252 int lines_left = dst_height;
253 // Compute amount of lines to skip to implement vertical crop.
254 // TODO(fbarchard): Ensure skip is a multiple of maximum component
255 // subsample. ie 2
256 int skip = (GetHeight() - dst_height) / 2;
257 if (skip > 0) {
258 // There is no API to skip lines in the output data, so we read them
259 // into the temp buffer.
260 while (skip >= GetImageScanlinesPerImcuRow()) {
261 if (!DecodeImcuRow()) {
262 FinishDecode();
263 return LIBYUV_FALSE;
264 }
265 skip -= GetImageScanlinesPerImcuRow();
266 }
267 if (skip > 0) {
268 // Have a partial iMCU row left over to skip. Must read it and then
269 // copy the parts we want into the destination.
270 if (!DecodeImcuRow()) {
271 FinishDecode();
272 return LIBYUV_FALSE;
273 }
274 for (int i = 0; i < num_outbufs_; ++i) {
275 // TODO(fbarchard): Compute skip to avoid this
276 assert(skip % GetVertSubSampFactor(i) == 0);
277 int rows_to_skip =
278 DivideAndRoundDown(skip, GetVertSubSampFactor(i));
279 int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) -
280 rows_to_skip;
281 int data_to_skip = rows_to_skip * GetComponentStride(i);
282 CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i),
283 planes[i], GetComponentWidth(i),
284 GetComponentWidth(i), scanlines_to_copy);
285 planes[i] += scanlines_to_copy * GetComponentWidth(i);
286 }
287 lines_left -= (GetImageScanlinesPerImcuRow() - skip);
288 }
289 }
291 // Read full MCUs but cropped horizontally
292 for (; lines_left > GetImageScanlinesPerImcuRow();
293 lines_left -= GetImageScanlinesPerImcuRow()) {
294 if (!DecodeImcuRow()) {
295 FinishDecode();
296 return LIBYUV_FALSE;
297 }
298 for (int i = 0; i < num_outbufs_; ++i) {
299 int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i);
300 CopyPlane(databuf_[i], GetComponentStride(i),
301 planes[i], GetComponentWidth(i),
302 GetComponentWidth(i), scanlines_to_copy);
303 planes[i] += scanlines_to_copy * GetComponentWidth(i);
304 }
305 }
307 if (lines_left > 0) {
308 // Have a partial iMCU row left over to decode.
309 if (!DecodeImcuRow()) {
310 FinishDecode();
311 return LIBYUV_FALSE;
312 }
313 for (int i = 0; i < num_outbufs_; ++i) {
314 int scanlines_to_copy =
315 DivideAndRoundUp(lines_left, GetVertSubSampFactor(i));
316 CopyPlane(databuf_[i], GetComponentStride(i),
317 planes[i], GetComponentWidth(i),
318 GetComponentWidth(i), scanlines_to_copy);
319 planes[i] += scanlines_to_copy * GetComponentWidth(i);
320 }
321 }
322 return FinishDecode();
323 }
325 LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque,
326 int dst_width, int dst_height) {
327 if (dst_width != GetWidth() ||
328 dst_height > GetHeight()) {
329 // ERROR: Bad dimensions
330 return LIBYUV_FALSE;
331 }
332 #ifdef HAVE_SETJMP
333 if (setjmp(error_mgr_->setjmp_buffer)) {
334 // We called into jpeglib, it experienced an error sometime during this
335 // function call, and we called longjmp() and rewound the stack to here.
336 // Return error.
337 return LIBYUV_FALSE;
338 }
339 #endif
340 if (!StartDecode()) {
341 return LIBYUV_FALSE;
342 }
343 SetScanlinePointers(databuf_);
344 int lines_left = dst_height;
345 // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop
346 int skip = (GetHeight() - dst_height) / 2;
347 if (skip > 0) {
348 while (skip >= GetImageScanlinesPerImcuRow()) {
349 if (!DecodeImcuRow()) {
350 FinishDecode();
351 return LIBYUV_FALSE;
352 }
353 skip -= GetImageScanlinesPerImcuRow();
354 }
355 if (skip > 0) {
356 // Have a partial iMCU row left over to skip.
357 if (!DecodeImcuRow()) {
358 FinishDecode();
359 return LIBYUV_FALSE;
360 }
361 for (int i = 0; i < num_outbufs_; ++i) {
362 // TODO(fbarchard): Compute skip to avoid this
363 assert(skip % GetVertSubSampFactor(i) == 0);
364 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
365 int data_to_skip = rows_to_skip * GetComponentStride(i);
366 // Change our own data buffer pointers so we can pass them to the
367 // callback.
368 databuf_[i] += data_to_skip;
369 }
370 int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip;
371 (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy);
372 // Now change them back.
373 for (int i = 0; i < num_outbufs_; ++i) {
374 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
375 int data_to_skip = rows_to_skip * GetComponentStride(i);
376 databuf_[i] -= data_to_skip;
377 }
378 lines_left -= scanlines_to_copy;
379 }
380 }
381 // Read full MCUs until we get to the crop point.
382 for (; lines_left >= GetImageScanlinesPerImcuRow();
383 lines_left -= GetImageScanlinesPerImcuRow()) {
384 if (!DecodeImcuRow()) {
385 FinishDecode();
386 return LIBYUV_FALSE;
387 }
388 (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow());
389 }
390 if (lines_left > 0) {
391 // Have a partial iMCU row left over to decode.
392 if (!DecodeImcuRow()) {
393 FinishDecode();
394 return LIBYUV_FALSE;
395 }
396 (*fn)(opaque, databuf_, databuf_strides_, lines_left);
397 }
398 return FinishDecode();
399 }
401 void MJpegDecoder::init_source(j_decompress_ptr cinfo) {
402 fill_input_buffer(cinfo);
403 }
405 boolean MJpegDecoder::fill_input_buffer(j_decompress_ptr cinfo) {
406 BufferVector* buf_vec = (BufferVector*)(cinfo->client_data);
407 if (buf_vec->pos >= buf_vec->len) {
408 assert(0 && "No more data");
409 // ERROR: No more data
410 return FALSE;
411 }
412 cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data;
413 cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len;
414 ++buf_vec->pos;
415 return TRUE;
416 }
418 void MJpegDecoder::skip_input_data(j_decompress_ptr cinfo,
419 long num_bytes) { // NOLINT
420 cinfo->src->next_input_byte += num_bytes;
421 }
423 void MJpegDecoder::term_source(j_decompress_ptr cinfo) {
424 // Nothing to do.
425 }
427 #ifdef HAVE_SETJMP
428 void MJpegDecoder::ErrorHandler(j_common_ptr cinfo) {
429 // This is called when a jpeglib command experiences an error. Unfortunately
430 // jpeglib's error handling model is not very flexible, because it expects the
431 // error handler to not return--i.e., it wants the program to terminate. To
432 // recover from errors we use setjmp() as shown in their example. setjmp() is
433 // C's implementation for the "call with current continuation" functionality
434 // seen in some functional programming languages.
435 // A formatted message can be output, but is unsafe for release.
436 #ifdef DEBUG
437 char buf[JMSG_LENGTH_MAX];
438 (*cinfo->err->format_message)(cinfo, buf);
439 // ERROR: Error in jpeglib: buf
440 #endif
442 SetJmpErrorMgr* mgr = (SetJmpErrorMgr*)(cinfo->err);
443 // This rewinds the call stack to the point of the corresponding setjmp()
444 // and causes it to return (for a second time) with value 1.
445 longjmp(mgr->setjmp_buffer, 1);
446 }
447 #endif
449 void MJpegDecoder::AllocOutputBuffers(int num_outbufs) {
450 if (num_outbufs != num_outbufs_) {
451 // We could perhaps optimize this case to resize the output buffers without
452 // necessarily having to delete and recreate each one, but it's not worth
453 // it.
454 DestroyOutputBuffers();
456 scanlines_ = new uint8** [num_outbufs];
457 scanlines_sizes_ = new int[num_outbufs];
458 databuf_ = new uint8* [num_outbufs];
459 databuf_strides_ = new int[num_outbufs];
461 for (int i = 0; i < num_outbufs; ++i) {
462 scanlines_[i] = NULL;
463 scanlines_sizes_[i] = 0;
464 databuf_[i] = NULL;
465 databuf_strides_[i] = 0;
466 }
468 num_outbufs_ = num_outbufs;
469 }
470 }
472 void MJpegDecoder::DestroyOutputBuffers() {
473 for (int i = 0; i < num_outbufs_; ++i) {
474 delete [] scanlines_[i];
475 delete [] databuf_[i];
476 }
477 delete [] scanlines_;
478 delete [] databuf_;
479 delete [] scanlines_sizes_;
480 delete [] databuf_strides_;
481 scanlines_ = NULL;
482 databuf_ = NULL;
483 scanlines_sizes_ = NULL;
484 databuf_strides_ = NULL;
485 num_outbufs_ = 0;
486 }
488 // JDCT_IFAST and do_block_smoothing improve performance substantially.
489 LIBYUV_BOOL MJpegDecoder::StartDecode() {
490 decompress_struct_->raw_data_out = TRUE;
491 decompress_struct_->dct_method = JDCT_IFAST; // JDCT_ISLOW is default
492 decompress_struct_->dither_mode = JDITHER_NONE;
493 // Not applicable to 'raw':
494 decompress_struct_->do_fancy_upsampling = LIBYUV_FALSE;
495 // Only for buffered mode:
496 decompress_struct_->enable_2pass_quant = LIBYUV_FALSE;
497 // Blocky but fast:
498 decompress_struct_->do_block_smoothing = LIBYUV_FALSE;
500 if (!jpeg_start_decompress(decompress_struct_)) {
501 // ERROR: Couldn't start JPEG decompressor";
502 return LIBYUV_FALSE;
503 }
504 return LIBYUV_TRUE;
505 }
507 LIBYUV_BOOL MJpegDecoder::FinishDecode() {
508 // jpeglib considers it an error if we finish without decoding the whole
509 // image, so we call "abort" rather than "finish".
510 jpeg_abort_decompress(decompress_struct_);
511 return LIBYUV_TRUE;
512 }
514 void MJpegDecoder::SetScanlinePointers(uint8** data) {
515 for (int i = 0; i < num_outbufs_; ++i) {
516 uint8* data_i = data[i];
517 for (int j = 0; j < scanlines_sizes_[i]; ++j) {
518 scanlines_[i][j] = data_i;
519 data_i += GetComponentStride(i);
520 }
521 }
522 }
524 inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() {
525 return (unsigned int)(GetImageScanlinesPerImcuRow()) ==
526 jpeg_read_raw_data(decompress_struct_,
527 scanlines_,
528 GetImageScanlinesPerImcuRow());
529 }
531 // The helper function which recognizes the jpeg sub-sampling type.
532 JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper(
533 int* subsample_x, int* subsample_y, int number_of_components) {
534 if (number_of_components == 3) { // Color images.
535 if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
536 subsample_x[1] == 2 && subsample_y[1] == 2 &&
537 subsample_x[2] == 2 && subsample_y[2] == 2) {
538 return kJpegYuv420;
539 } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
540 subsample_x[1] == 2 && subsample_y[1] == 1 &&
541 subsample_x[2] == 2 && subsample_y[2] == 1) {
542 return kJpegYuv422;
543 } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
544 subsample_x[1] == 1 && subsample_y[1] == 1 &&
545 subsample_x[2] == 1 && subsample_y[2] == 1) {
546 return kJpegYuv444;
547 }
548 } else if (number_of_components == 1) { // Grey-scale images.
549 if (subsample_x[0] == 1 && subsample_y[0] == 1) {
550 return kJpegYuv400;
551 }
552 }
553 return kJpegUnknown;
554 }
556 } // namespace libyuv
557 #endif // HAVE_JPEG