blob: adba832f53f0244221a804f32a80cc2de9baec0b [file] [log] [blame]
James Zern3a7d4672014-08-10 16:15:18 -07001/*
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 */
10
11#include "libyuv/mjpeg_decoder.h"
12
13#ifdef HAVE_JPEG
14#include <assert.h>
15
James Zernb644eb92014-08-22 10:31:01 -070016#if !defined(__pnacl__) && !defined(__CLR_VER) && \
17 !defined(COVERAGE_ENABLED) && !defined(TARGET_IPHONE_SIMULATOR)
James Zern3a7d4672014-08-10 16:15:18 -070018// Must be included before jpeglib.
19#include <setjmp.h>
20#define HAVE_SETJMP
James Zernfcb42532015-07-24 16:54:51 -070021
22#if defined(_MSC_VER)
23// disable warning 4324: structure was padded due to __declspec(align())
Christopher Degawa96703f22020-12-12 18:16:19 +000024#pragma warning(disable : 4324)
James Zernfcb42532015-07-24 16:54:51 -070025#endif
26
James Zern3a7d4672014-08-10 16:15:18 -070027#endif
Christopher Degawa96703f22020-12-12 18:16:19 +000028
29#include <stdio.h> // For jpeglib.h.
James Zern3a7d4672014-08-10 16:15:18 -070030
31// C++ build requires extern C for jpeg internals.
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36#include <jpeglib.h>
37
38#ifdef __cplusplus
39} // extern "C"
40#endif
41
42#include "libyuv/planar_functions.h" // For CopyPlane().
43
44namespace libyuv {
45
46#ifdef HAVE_SETJMP
47struct SetJmpErrorMgr {
48 jpeg_error_mgr base; // Must be at the top
49 jmp_buf setjmp_buffer;
50};
51#endif
52
53const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN;
54const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE;
55const int MJpegDecoder::kColorSpaceRgb = JCS_RGB;
56const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr;
57const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK;
58const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK;
59
60// Methods that are passed to jpeglib.
61boolean fill_input_buffer(jpeg_decompress_struct* cinfo);
62void init_source(jpeg_decompress_struct* cinfo);
Christopher Degawa96703f22020-12-12 18:16:19 +000063void skip_input_data(jpeg_decompress_struct* cinfo, long num_bytes); // NOLINT
James Zern3a7d4672014-08-10 16:15:18 -070064void term_source(jpeg_decompress_struct* cinfo);
65void ErrorHandler(jpeg_common_struct* cinfo);
Christopher Degawa96703f22020-12-12 18:16:19 +000066void OutputHandler(jpeg_common_struct* cinfo);
James Zern3a7d4672014-08-10 16:15:18 -070067
68MJpegDecoder::MJpegDecoder()
69 : has_scanline_padding_(LIBYUV_FALSE),
70 num_outbufs_(0),
71 scanlines_(NULL),
72 scanlines_sizes_(NULL),
73 databuf_(NULL),
74 databuf_strides_(NULL) {
75 decompress_struct_ = new jpeg_decompress_struct;
76 source_mgr_ = new jpeg_source_mgr;
77#ifdef HAVE_SETJMP
78 error_mgr_ = new SetJmpErrorMgr;
79 decompress_struct_->err = jpeg_std_error(&error_mgr_->base);
80 // Override standard exit()-based error handler.
81 error_mgr_->base.error_exit = &ErrorHandler;
Christopher Degawa96703f22020-12-12 18:16:19 +000082 error_mgr_->base.output_message = &OutputHandler;
James Zern3a7d4672014-08-10 16:15:18 -070083#endif
84 decompress_struct_->client_data = NULL;
85 source_mgr_->init_source = &init_source;
86 source_mgr_->fill_input_buffer = &fill_input_buffer;
87 source_mgr_->skip_input_data = &skip_input_data;
88 source_mgr_->resync_to_restart = &jpeg_resync_to_restart;
89 source_mgr_->term_source = &term_source;
90 jpeg_create_decompress(decompress_struct_);
91 decompress_struct_->src = source_mgr_;
92 buf_vec_.buffers = &buf_;
93 buf_vec_.len = 1;
94}
95
96MJpegDecoder::~MJpegDecoder() {
97 jpeg_destroy_decompress(decompress_struct_);
98 delete decompress_struct_;
99 delete source_mgr_;
100#ifdef HAVE_SETJMP
101 delete error_mgr_;
102#endif
103 DestroyOutputBuffers();
104}
105
Christopher Degawa96703f22020-12-12 18:16:19 +0000106LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8_t* src, size_t src_len) {
James Zern3a7d4672014-08-10 16:15:18 -0700107 if (!ValidateJpeg(src, src_len)) {
108 return LIBYUV_FALSE;
109 }
110
111 buf_.data = src;
James Zernb644eb92014-08-22 10:31:01 -0700112 buf_.len = static_cast<int>(src_len);
James Zern3a7d4672014-08-10 16:15:18 -0700113 buf_vec_.pos = 0;
114 decompress_struct_->client_data = &buf_vec_;
115#ifdef HAVE_SETJMP
116 if (setjmp(error_mgr_->setjmp_buffer)) {
117 // We called jpeg_read_header, it experienced an error, and we called
118 // longjmp() and rewound the stack to here. Return error.
119 return LIBYUV_FALSE;
120 }
121#endif
122 if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) {
123 // ERROR: Bad MJPEG header
124 return LIBYUV_FALSE;
125 }
126 AllocOutputBuffers(GetNumComponents());
127 for (int i = 0; i < num_outbufs_; ++i) {
128 int scanlines_size = GetComponentScanlinesPerImcuRow(i);
129 if (scanlines_sizes_[i] != scanlines_size) {
130 if (scanlines_[i]) {
131 delete scanlines_[i];
132 }
Christopher Degawa96703f22020-12-12 18:16:19 +0000133 scanlines_[i] = new uint8_t*[scanlines_size];
James Zern3a7d4672014-08-10 16:15:18 -0700134 scanlines_sizes_[i] = scanlines_size;
135 }
136
137 // We allocate padding for the final scanline to pad it up to DCTSIZE bytes
138 // to avoid memory errors, since jpeglib only reads full MCUs blocks. For
139 // the preceding scanlines, the padding is not needed/wanted because the
140 // following addresses will already be valid (they are the initial bytes of
141 // the next scanline) and will be overwritten when jpeglib writes out that
142 // next scanline.
143 int databuf_stride = GetComponentStride(i);
144 int databuf_size = scanlines_size * databuf_stride;
145 if (databuf_strides_[i] != databuf_stride) {
146 if (databuf_[i]) {
147 delete databuf_[i];
148 }
Christopher Degawa96703f22020-12-12 18:16:19 +0000149 databuf_[i] = new uint8_t[databuf_size];
James Zern3a7d4672014-08-10 16:15:18 -0700150 databuf_strides_[i] = databuf_stride;
151 }
152
153 if (GetComponentStride(i) != GetComponentWidth(i)) {
154 has_scanline_padding_ = LIBYUV_TRUE;
155 }
156 }
157 return LIBYUV_TRUE;
158}
159
160static int DivideAndRoundUp(int numerator, int denominator) {
161 return (numerator + denominator - 1) / denominator;
162}
163
164static int DivideAndRoundDown(int numerator, int denominator) {
165 return numerator / denominator;
166}
167
168// Returns width of the last loaded frame.
169int MJpegDecoder::GetWidth() {
170 return decompress_struct_->image_width;
171}
172
173// Returns height of the last loaded frame.
174int MJpegDecoder::GetHeight() {
175 return decompress_struct_->image_height;
176}
177
178// Returns format of the last loaded frame. The return value is one of the
179// kColorSpace* constants.
180int MJpegDecoder::GetColorSpace() {
181 return decompress_struct_->jpeg_color_space;
182}
183
184// Number of color components in the color space.
185int MJpegDecoder::GetNumComponents() {
186 return decompress_struct_->num_components;
187}
188
189// Sample factors of the n-th component.
190int MJpegDecoder::GetHorizSampFactor(int component) {
191 return decompress_struct_->comp_info[component].h_samp_factor;
192}
193
194int MJpegDecoder::GetVertSampFactor(int component) {
195 return decompress_struct_->comp_info[component].v_samp_factor;
196}
197
198int MJpegDecoder::GetHorizSubSampFactor(int component) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000199 return decompress_struct_->max_h_samp_factor / GetHorizSampFactor(component);
James Zern3a7d4672014-08-10 16:15:18 -0700200}
201
202int MJpegDecoder::GetVertSubSampFactor(int component) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000203 return decompress_struct_->max_v_samp_factor / GetVertSampFactor(component);
James Zern3a7d4672014-08-10 16:15:18 -0700204}
205
206int MJpegDecoder::GetImageScanlinesPerImcuRow() {
207 return decompress_struct_->max_v_samp_factor * DCTSIZE;
208}
209
210int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) {
211 int vs = GetVertSubSampFactor(component);
212 return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs);
213}
214
215int MJpegDecoder::GetComponentWidth(int component) {
216 int hs = GetHorizSubSampFactor(component);
217 return DivideAndRoundUp(GetWidth(), hs);
218}
219
220int MJpegDecoder::GetComponentHeight(int component) {
221 int vs = GetVertSubSampFactor(component);
222 return DivideAndRoundUp(GetHeight(), vs);
223}
224
225// Get width in bytes padded out to a multiple of DCTSIZE
226int MJpegDecoder::GetComponentStride(int component) {
227 return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1);
228}
229
230int MJpegDecoder::GetComponentSize(int component) {
231 return GetComponentWidth(component) * GetComponentHeight(component);
232}
233
234LIBYUV_BOOL MJpegDecoder::UnloadFrame() {
235#ifdef HAVE_SETJMP
236 if (setjmp(error_mgr_->setjmp_buffer)) {
237 // We called jpeg_abort_decompress, it experienced an error, and we called
238 // longjmp() and rewound the stack to here. Return error.
239 return LIBYUV_FALSE;
240 }
241#endif
242 jpeg_abort_decompress(decompress_struct_);
243 return LIBYUV_TRUE;
244}
245
246// TODO(fbarchard): Allow rectangle to be specified: x, y, width, height.
Christopher Degawa96703f22020-12-12 18:16:19 +0000247LIBYUV_BOOL MJpegDecoder::DecodeToBuffers(uint8_t** planes,
248 int dst_width,
249 int dst_height) {
250 if (dst_width != GetWidth() || dst_height > GetHeight()) {
James Zern3a7d4672014-08-10 16:15:18 -0700251 // ERROR: Bad dimensions
252 return LIBYUV_FALSE;
253 }
254#ifdef HAVE_SETJMP
255 if (setjmp(error_mgr_->setjmp_buffer)) {
256 // We called into jpeglib, it experienced an error sometime during this
257 // function call, and we called longjmp() and rewound the stack to here.
258 // Return error.
259 return LIBYUV_FALSE;
260 }
261#endif
262 if (!StartDecode()) {
263 return LIBYUV_FALSE;
264 }
265 SetScanlinePointers(databuf_);
266 int lines_left = dst_height;
267 // Compute amount of lines to skip to implement vertical crop.
268 // TODO(fbarchard): Ensure skip is a multiple of maximum component
269 // subsample. ie 2
270 int skip = (GetHeight() - dst_height) / 2;
271 if (skip > 0) {
272 // There is no API to skip lines in the output data, so we read them
273 // into the temp buffer.
274 while (skip >= GetImageScanlinesPerImcuRow()) {
275 if (!DecodeImcuRow()) {
276 FinishDecode();
277 return LIBYUV_FALSE;
278 }
279 skip -= GetImageScanlinesPerImcuRow();
280 }
281 if (skip > 0) {
282 // Have a partial iMCU row left over to skip. Must read it and then
283 // copy the parts we want into the destination.
284 if (!DecodeImcuRow()) {
285 FinishDecode();
286 return LIBYUV_FALSE;
287 }
288 for (int i = 0; i < num_outbufs_; ++i) {
289 // TODO(fbarchard): Compute skip to avoid this
290 assert(skip % GetVertSubSampFactor(i) == 0);
Christopher Degawa96703f22020-12-12 18:16:19 +0000291 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
292 int scanlines_to_copy =
293 GetComponentScanlinesPerImcuRow(i) - rows_to_skip;
James Zern3a7d4672014-08-10 16:15:18 -0700294 int data_to_skip = rows_to_skip * GetComponentStride(i);
Christopher Degawa96703f22020-12-12 18:16:19 +0000295 CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i), planes[i],
296 GetComponentWidth(i), GetComponentWidth(i),
297 scanlines_to_copy);
James Zern3a7d4672014-08-10 16:15:18 -0700298 planes[i] += scanlines_to_copy * GetComponentWidth(i);
299 }
300 lines_left -= (GetImageScanlinesPerImcuRow() - skip);
301 }
302 }
303
304 // Read full MCUs but cropped horizontally
305 for (; lines_left > GetImageScanlinesPerImcuRow();
Christopher Degawa96703f22020-12-12 18:16:19 +0000306 lines_left -= GetImageScanlinesPerImcuRow()) {
James Zern3a7d4672014-08-10 16:15:18 -0700307 if (!DecodeImcuRow()) {
308 FinishDecode();
309 return LIBYUV_FALSE;
310 }
311 for (int i = 0; i < num_outbufs_; ++i) {
312 int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i);
Christopher Degawa96703f22020-12-12 18:16:19 +0000313 CopyPlane(databuf_[i], GetComponentStride(i), planes[i],
314 GetComponentWidth(i), GetComponentWidth(i), scanlines_to_copy);
James Zern3a7d4672014-08-10 16:15:18 -0700315 planes[i] += scanlines_to_copy * GetComponentWidth(i);
316 }
317 }
318
319 if (lines_left > 0) {
320 // Have a partial iMCU row left over to decode.
321 if (!DecodeImcuRow()) {
322 FinishDecode();
323 return LIBYUV_FALSE;
324 }
325 for (int i = 0; i < num_outbufs_; ++i) {
326 int scanlines_to_copy =
327 DivideAndRoundUp(lines_left, GetVertSubSampFactor(i));
Christopher Degawa96703f22020-12-12 18:16:19 +0000328 CopyPlane(databuf_[i], GetComponentStride(i), planes[i],
329 GetComponentWidth(i), GetComponentWidth(i), scanlines_to_copy);
James Zern3a7d4672014-08-10 16:15:18 -0700330 planes[i] += scanlines_to_copy * GetComponentWidth(i);
331 }
332 }
333 return FinishDecode();
334}
335
Christopher Degawa96703f22020-12-12 18:16:19 +0000336LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn,
337 void* opaque,
338 int dst_width,
339 int dst_height) {
340 if (dst_width != GetWidth() || dst_height > GetHeight()) {
James Zern3a7d4672014-08-10 16:15:18 -0700341 // ERROR: Bad dimensions
342 return LIBYUV_FALSE;
343 }
344#ifdef HAVE_SETJMP
345 if (setjmp(error_mgr_->setjmp_buffer)) {
346 // We called into jpeglib, it experienced an error sometime during this
347 // function call, and we called longjmp() and rewound the stack to here.
348 // Return error.
349 return LIBYUV_FALSE;
350 }
351#endif
352 if (!StartDecode()) {
353 return LIBYUV_FALSE;
354 }
355 SetScanlinePointers(databuf_);
356 int lines_left = dst_height;
357 // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop
358 int skip = (GetHeight() - dst_height) / 2;
359 if (skip > 0) {
360 while (skip >= GetImageScanlinesPerImcuRow()) {
361 if (!DecodeImcuRow()) {
362 FinishDecode();
363 return LIBYUV_FALSE;
364 }
365 skip -= GetImageScanlinesPerImcuRow();
366 }
367 if (skip > 0) {
368 // Have a partial iMCU row left over to skip.
369 if (!DecodeImcuRow()) {
370 FinishDecode();
371 return LIBYUV_FALSE;
372 }
373 for (int i = 0; i < num_outbufs_; ++i) {
374 // TODO(fbarchard): Compute skip to avoid this
375 assert(skip % GetVertSubSampFactor(i) == 0);
376 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
377 int data_to_skip = rows_to_skip * GetComponentStride(i);
378 // Change our own data buffer pointers so we can pass them to the
379 // callback.
380 databuf_[i] += data_to_skip;
381 }
382 int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip;
383 (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy);
384 // Now change them back.
385 for (int i = 0; i < num_outbufs_; ++i) {
386 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
387 int data_to_skip = rows_to_skip * GetComponentStride(i);
388 databuf_[i] -= data_to_skip;
389 }
390 lines_left -= scanlines_to_copy;
391 }
392 }
393 // Read full MCUs until we get to the crop point.
394 for (; lines_left >= GetImageScanlinesPerImcuRow();
Christopher Degawa96703f22020-12-12 18:16:19 +0000395 lines_left -= GetImageScanlinesPerImcuRow()) {
James Zern3a7d4672014-08-10 16:15:18 -0700396 if (!DecodeImcuRow()) {
397 FinishDecode();
398 return LIBYUV_FALSE;
399 }
400 (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow());
401 }
402 if (lines_left > 0) {
403 // Have a partial iMCU row left over to decode.
404 if (!DecodeImcuRow()) {
405 FinishDecode();
406 return LIBYUV_FALSE;
407 }
408 (*fn)(opaque, databuf_, databuf_strides_, lines_left);
409 }
410 return FinishDecode();
411}
412
413void init_source(j_decompress_ptr cinfo) {
414 fill_input_buffer(cinfo);
415}
416
417boolean fill_input_buffer(j_decompress_ptr cinfo) {
James Zernb644eb92014-08-22 10:31:01 -0700418 BufferVector* buf_vec = reinterpret_cast<BufferVector*>(cinfo->client_data);
James Zern3a7d4672014-08-10 16:15:18 -0700419 if (buf_vec->pos >= buf_vec->len) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000420 // Don't assert-fail when fuzzing.
421#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
James Zern3a7d4672014-08-10 16:15:18 -0700422 assert(0 && "No more data");
Christopher Degawa96703f22020-12-12 18:16:19 +0000423#endif
James Zern3a7d4672014-08-10 16:15:18 -0700424 // ERROR: No more data
425 return FALSE;
426 }
427 cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data;
428 cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len;
429 ++buf_vec->pos;
430 return TRUE;
431}
432
Christopher Degawa96703f22020-12-12 18:16:19 +0000433void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { // NOLINT
434 jpeg_source_mgr* src = cinfo->src;
435 size_t bytes = static_cast<size_t>(num_bytes);
436 if (bytes > src->bytes_in_buffer) {
437 src->next_input_byte = nullptr;
438 src->bytes_in_buffer = 0;
439 } else {
440 src->next_input_byte += bytes;
441 src->bytes_in_buffer -= bytes;
442 }
James Zern3a7d4672014-08-10 16:15:18 -0700443}
444
445void term_source(j_decompress_ptr cinfo) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000446 (void)cinfo; // Nothing to do.
James Zern3a7d4672014-08-10 16:15:18 -0700447}
448
449#ifdef HAVE_SETJMP
450void ErrorHandler(j_common_ptr cinfo) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000451// This is called when a jpeglib command experiences an error. Unfortunately
452// jpeglib's error handling model is not very flexible, because it expects the
453// error handler to not return--i.e., it wants the program to terminate. To
454// recover from errors we use setjmp() as shown in their example. setjmp() is
455// C's implementation for the "call with current continuation" functionality
456// seen in some functional programming languages.
457// A formatted message can be output, but is unsafe for release.
James Zern3a7d4672014-08-10 16:15:18 -0700458#ifdef DEBUG
459 char buf[JMSG_LENGTH_MAX];
460 (*cinfo->err->format_message)(cinfo, buf);
Christopher Degawa96703f22020-12-12 18:16:19 +0000461// ERROR: Error in jpeglib: buf
James Zern3a7d4672014-08-10 16:15:18 -0700462#endif
463
James Zernb644eb92014-08-22 10:31:01 -0700464 SetJmpErrorMgr* mgr = reinterpret_cast<SetJmpErrorMgr*>(cinfo->err);
James Zern3a7d4672014-08-10 16:15:18 -0700465 // This rewinds the call stack to the point of the corresponding setjmp()
466 // and causes it to return (for a second time) with value 1.
467 longjmp(mgr->setjmp_buffer, 1);
468}
Christopher Degawa96703f22020-12-12 18:16:19 +0000469
470// Suppress fprintf warnings.
471void OutputHandler(j_common_ptr cinfo) {
472 (void)cinfo;
473}
474
475#endif // HAVE_SETJMP
James Zern3a7d4672014-08-10 16:15:18 -0700476
477void MJpegDecoder::AllocOutputBuffers(int num_outbufs) {
478 if (num_outbufs != num_outbufs_) {
479 // We could perhaps optimize this case to resize the output buffers without
480 // necessarily having to delete and recreate each one, but it's not worth
481 // it.
482 DestroyOutputBuffers();
483
Christopher Degawa96703f22020-12-12 18:16:19 +0000484 scanlines_ = new uint8_t**[num_outbufs];
James Zern3a7d4672014-08-10 16:15:18 -0700485 scanlines_sizes_ = new int[num_outbufs];
Christopher Degawa96703f22020-12-12 18:16:19 +0000486 databuf_ = new uint8_t*[num_outbufs];
James Zern3a7d4672014-08-10 16:15:18 -0700487 databuf_strides_ = new int[num_outbufs];
488
489 for (int i = 0; i < num_outbufs; ++i) {
490 scanlines_[i] = NULL;
491 scanlines_sizes_[i] = 0;
492 databuf_[i] = NULL;
493 databuf_strides_[i] = 0;
494 }
495
496 num_outbufs_ = num_outbufs;
497 }
498}
499
500void MJpegDecoder::DestroyOutputBuffers() {
501 for (int i = 0; i < num_outbufs_; ++i) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000502 delete[] scanlines_[i];
503 delete[] databuf_[i];
James Zern3a7d4672014-08-10 16:15:18 -0700504 }
Christopher Degawa96703f22020-12-12 18:16:19 +0000505 delete[] scanlines_;
506 delete[] databuf_;
507 delete[] scanlines_sizes_;
508 delete[] databuf_strides_;
James Zern3a7d4672014-08-10 16:15:18 -0700509 scanlines_ = NULL;
510 databuf_ = NULL;
511 scanlines_sizes_ = NULL;
512 databuf_strides_ = NULL;
513 num_outbufs_ = 0;
514}
515
516// JDCT_IFAST and do_block_smoothing improve performance substantially.
517LIBYUV_BOOL MJpegDecoder::StartDecode() {
518 decompress_struct_->raw_data_out = TRUE;
519 decompress_struct_->dct_method = JDCT_IFAST; // JDCT_ISLOW is default
520 decompress_struct_->dither_mode = JDITHER_NONE;
521 // Not applicable to 'raw':
522 decompress_struct_->do_fancy_upsampling = (boolean)(LIBYUV_FALSE);
523 // Only for buffered mode:
524 decompress_struct_->enable_2pass_quant = (boolean)(LIBYUV_FALSE);
525 // Blocky but fast:
526 decompress_struct_->do_block_smoothing = (boolean)(LIBYUV_FALSE);
527
528 if (!jpeg_start_decompress(decompress_struct_)) {
529 // ERROR: Couldn't start JPEG decompressor";
530 return LIBYUV_FALSE;
531 }
532 return LIBYUV_TRUE;
533}
534
535LIBYUV_BOOL MJpegDecoder::FinishDecode() {
536 // jpeglib considers it an error if we finish without decoding the whole
537 // image, so we call "abort" rather than "finish".
538 jpeg_abort_decompress(decompress_struct_);
539 return LIBYUV_TRUE;
540}
541
Christopher Degawa96703f22020-12-12 18:16:19 +0000542void MJpegDecoder::SetScanlinePointers(uint8_t** data) {
James Zern3a7d4672014-08-10 16:15:18 -0700543 for (int i = 0; i < num_outbufs_; ++i) {
Christopher Degawa96703f22020-12-12 18:16:19 +0000544 uint8_t* data_i = data[i];
James Zern3a7d4672014-08-10 16:15:18 -0700545 for (int j = 0; j < scanlines_sizes_[i]; ++j) {
546 scanlines_[i][j] = data_i;
547 data_i += GetComponentStride(i);
548 }
549 }
550}
551
552inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() {
553 return (unsigned int)(GetImageScanlinesPerImcuRow()) ==
Christopher Degawa96703f22020-12-12 18:16:19 +0000554 jpeg_read_raw_data(decompress_struct_, scanlines_,
555 GetImageScanlinesPerImcuRow());
James Zern3a7d4672014-08-10 16:15:18 -0700556}
557
558// The helper function which recognizes the jpeg sub-sampling type.
559JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper(
Christopher Degawa96703f22020-12-12 18:16:19 +0000560 int* subsample_x,
561 int* subsample_y,
562 int number_of_components) {
James Zern3a7d4672014-08-10 16:15:18 -0700563 if (number_of_components == 3) { // Color images.
Christopher Degawa96703f22020-12-12 18:16:19 +0000564 if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 2 &&
565 subsample_y[1] == 2 && subsample_x[2] == 2 && subsample_y[2] == 2) {
James Zern3a7d4672014-08-10 16:15:18 -0700566 return kJpegYuv420;
Christopher Degawa96703f22020-12-12 18:16:19 +0000567 }
568 if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 2 &&
569 subsample_y[1] == 1 && subsample_x[2] == 2 && subsample_y[2] == 1) {
James Zern3a7d4672014-08-10 16:15:18 -0700570 return kJpegYuv422;
Christopher Degawa96703f22020-12-12 18:16:19 +0000571 }
572 if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 1 &&
573 subsample_y[1] == 1 && subsample_x[2] == 1 && subsample_y[2] == 1) {
James Zern3a7d4672014-08-10 16:15:18 -0700574 return kJpegYuv444;
575 }
576 } else if (number_of_components == 1) { // Grey-scale images.
577 if (subsample_x[0] == 1 && subsample_y[0] == 1) {
578 return kJpegYuv400;
579 }
580 }
581 return kJpegUnknown;
582}
583
584} // namespace libyuv
585#endif // HAVE_JPEG