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