| /* | 
 |  *  Copyright 2012 The LibYuv Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS. All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "libyuv/mjpeg_decoder.h" | 
 |  | 
 | #ifdef HAVE_JPEG | 
 | #include <assert.h> | 
 |  | 
 | #if !defined(__pnacl__) && !defined(__CLR_VER) && \ | 
 |     !defined(COVERAGE_ENABLED) && !defined(TARGET_IPHONE_SIMULATOR) | 
 | // Must be included before jpeglib. | 
 | #include <setjmp.h> | 
 | #define HAVE_SETJMP | 
 |  | 
 | #if defined(_MSC_VER) | 
 | // disable warning 4324: structure was padded due to __declspec(align()) | 
 | #pragma warning(disable : 4324) | 
 | #endif | 
 |  | 
 | #endif | 
 |  | 
 | #include <stdio.h>  // For jpeglib.h. | 
 |  | 
 | // C++ build requires extern C for jpeg internals. | 
 | #ifdef __cplusplus | 
 | extern "C" { | 
 | #endif | 
 |  | 
 | #include <jpeglib.h> | 
 |  | 
 | #ifdef __cplusplus | 
 | }  // extern "C" | 
 | #endif | 
 |  | 
 | #include "libyuv/planar_functions.h"  // For CopyPlane(). | 
 |  | 
 | namespace libyuv { | 
 |  | 
 | #ifdef HAVE_SETJMP | 
 | struct SetJmpErrorMgr { | 
 |   jpeg_error_mgr base;  // Must be at the top | 
 |   jmp_buf setjmp_buffer; | 
 | }; | 
 | #endif | 
 |  | 
 | const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN; | 
 | const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE; | 
 | const int MJpegDecoder::kColorSpaceRgb = JCS_RGB; | 
 | const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr; | 
 | const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK; | 
 | const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK; | 
 |  | 
 | // Methods that are passed to jpeglib. | 
 | boolean fill_input_buffer(jpeg_decompress_struct* cinfo); | 
 | void init_source(jpeg_decompress_struct* cinfo); | 
 | void skip_input_data(jpeg_decompress_struct* cinfo, long num_bytes);  // NOLINT | 
 | void term_source(jpeg_decompress_struct* cinfo); | 
 | void ErrorHandler(jpeg_common_struct* cinfo); | 
 | void OutputHandler(jpeg_common_struct* cinfo); | 
 |  | 
 | MJpegDecoder::MJpegDecoder() | 
 |     : has_scanline_padding_(LIBYUV_FALSE), | 
 |       num_outbufs_(0), | 
 |       scanlines_(NULL), | 
 |       scanlines_sizes_(NULL), | 
 |       databuf_(NULL), | 
 |       databuf_strides_(NULL) { | 
 |   decompress_struct_ = new jpeg_decompress_struct; | 
 |   source_mgr_ = new jpeg_source_mgr; | 
 | #ifdef HAVE_SETJMP | 
 |   error_mgr_ = new SetJmpErrorMgr; | 
 |   decompress_struct_->err = jpeg_std_error(&error_mgr_->base); | 
 |   // Override standard exit()-based error handler. | 
 |   error_mgr_->base.error_exit = &ErrorHandler; | 
 |   error_mgr_->base.output_message = &OutputHandler; | 
 | #endif | 
 |   decompress_struct_->client_data = NULL; | 
 |   source_mgr_->init_source = &init_source; | 
 |   source_mgr_->fill_input_buffer = &fill_input_buffer; | 
 |   source_mgr_->skip_input_data = &skip_input_data; | 
 |   source_mgr_->resync_to_restart = &jpeg_resync_to_restart; | 
 |   source_mgr_->term_source = &term_source; | 
 |   jpeg_create_decompress(decompress_struct_); | 
 |   decompress_struct_->src = source_mgr_; | 
 |   buf_vec_.buffers = &buf_; | 
 |   buf_vec_.len = 1; | 
 | } | 
 |  | 
 | MJpegDecoder::~MJpegDecoder() { | 
 |   jpeg_destroy_decompress(decompress_struct_); | 
 |   delete decompress_struct_; | 
 |   delete source_mgr_; | 
 | #ifdef HAVE_SETJMP | 
 |   delete error_mgr_; | 
 | #endif | 
 |   DestroyOutputBuffers(); | 
 | } | 
 |  | 
 | LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8_t* src, size_t src_len) { | 
 |   if (!ValidateJpeg(src, src_len)) { | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 |  | 
 |   buf_.data = src; | 
 |   buf_.len = static_cast<int>(src_len); | 
 |   buf_vec_.pos = 0; | 
 |   decompress_struct_->client_data = &buf_vec_; | 
 | #ifdef HAVE_SETJMP | 
 |   if (setjmp(error_mgr_->setjmp_buffer)) { | 
 |     // We called jpeg_read_header, it experienced an error, and we called | 
 |     // longjmp() and rewound the stack to here. Return error. | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #endif | 
 |   if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) { | 
 |     // ERROR: Bad MJPEG header | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 |   AllocOutputBuffers(GetNumComponents()); | 
 |   for (int i = 0; i < num_outbufs_; ++i) { | 
 |     int scanlines_size = GetComponentScanlinesPerImcuRow(i); | 
 |     if (scanlines_sizes_[i] != scanlines_size) { | 
 |       if (scanlines_[i]) { | 
 |         delete scanlines_[i]; | 
 |       } | 
 |       scanlines_[i] = new uint8_t*[scanlines_size]; | 
 |       scanlines_sizes_[i] = scanlines_size; | 
 |     } | 
 |  | 
 |     // We allocate padding for the final scanline to pad it up to DCTSIZE bytes | 
 |     // to avoid memory errors, since jpeglib only reads full MCUs blocks. For | 
 |     // the preceding scanlines, the padding is not needed/wanted because the | 
 |     // following addresses will already be valid (they are the initial bytes of | 
 |     // the next scanline) and will be overwritten when jpeglib writes out that | 
 |     // next scanline. | 
 |     int databuf_stride = GetComponentStride(i); | 
 |     int databuf_size = scanlines_size * databuf_stride; | 
 |     if (databuf_strides_[i] != databuf_stride) { | 
 |       if (databuf_[i]) { | 
 |         delete databuf_[i]; | 
 |       } | 
 |       databuf_[i] = new uint8_t[databuf_size]; | 
 |       databuf_strides_[i] = databuf_stride; | 
 |     } | 
 |  | 
 |     if (GetComponentStride(i) != GetComponentWidth(i)) { | 
 |       has_scanline_padding_ = LIBYUV_TRUE; | 
 |     } | 
 |   } | 
 |   return LIBYUV_TRUE; | 
 | } | 
 |  | 
 | static int DivideAndRoundUp(int numerator, int denominator) { | 
 |   return (numerator + denominator - 1) / denominator; | 
 | } | 
 |  | 
 | static int DivideAndRoundDown(int numerator, int denominator) { | 
 |   return numerator / denominator; | 
 | } | 
 |  | 
 | // Returns width of the last loaded frame. | 
 | int MJpegDecoder::GetWidth() { | 
 |   return decompress_struct_->image_width; | 
 | } | 
 |  | 
 | // Returns height of the last loaded frame. | 
 | int MJpegDecoder::GetHeight() { | 
 |   return decompress_struct_->image_height; | 
 | } | 
 |  | 
 | // Returns format of the last loaded frame. The return value is one of the | 
 | // kColorSpace* constants. | 
 | int MJpegDecoder::GetColorSpace() { | 
 |   return decompress_struct_->jpeg_color_space; | 
 | } | 
 |  | 
 | // Number of color components in the color space. | 
 | int MJpegDecoder::GetNumComponents() { | 
 |   return decompress_struct_->num_components; | 
 | } | 
 |  | 
 | // Sample factors of the n-th component. | 
 | int MJpegDecoder::GetHorizSampFactor(int component) { | 
 |   return decompress_struct_->comp_info[component].h_samp_factor; | 
 | } | 
 |  | 
 | int MJpegDecoder::GetVertSampFactor(int component) { | 
 |   return decompress_struct_->comp_info[component].v_samp_factor; | 
 | } | 
 |  | 
 | int MJpegDecoder::GetHorizSubSampFactor(int component) { | 
 |   return decompress_struct_->max_h_samp_factor / GetHorizSampFactor(component); | 
 | } | 
 |  | 
 | int MJpegDecoder::GetVertSubSampFactor(int component) { | 
 |   return decompress_struct_->max_v_samp_factor / GetVertSampFactor(component); | 
 | } | 
 |  | 
 | int MJpegDecoder::GetImageScanlinesPerImcuRow() { | 
 |   return decompress_struct_->max_v_samp_factor * DCTSIZE; | 
 | } | 
 |  | 
 | int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) { | 
 |   int vs = GetVertSubSampFactor(component); | 
 |   return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs); | 
 | } | 
 |  | 
 | int MJpegDecoder::GetComponentWidth(int component) { | 
 |   int hs = GetHorizSubSampFactor(component); | 
 |   return DivideAndRoundUp(GetWidth(), hs); | 
 | } | 
 |  | 
 | int MJpegDecoder::GetComponentHeight(int component) { | 
 |   int vs = GetVertSubSampFactor(component); | 
 |   return DivideAndRoundUp(GetHeight(), vs); | 
 | } | 
 |  | 
 | // Get width in bytes padded out to a multiple of DCTSIZE | 
 | int MJpegDecoder::GetComponentStride(int component) { | 
 |   return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1); | 
 | } | 
 |  | 
 | int MJpegDecoder::GetComponentSize(int component) { | 
 |   return GetComponentWidth(component) * GetComponentHeight(component); | 
 | } | 
 |  | 
 | LIBYUV_BOOL MJpegDecoder::UnloadFrame() { | 
 | #ifdef HAVE_SETJMP | 
 |   if (setjmp(error_mgr_->setjmp_buffer)) { | 
 |     // We called jpeg_abort_decompress, it experienced an error, and we called | 
 |     // longjmp() and rewound the stack to here. Return error. | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #endif | 
 |   jpeg_abort_decompress(decompress_struct_); | 
 |   return LIBYUV_TRUE; | 
 | } | 
 |  | 
 | // TODO(fbarchard): Allow rectangle to be specified: x, y, width, height. | 
 | LIBYUV_BOOL MJpegDecoder::DecodeToBuffers(uint8_t** planes, | 
 |                                           int dst_width, | 
 |                                           int dst_height) { | 
 |   if (dst_width != GetWidth() || dst_height > GetHeight()) { | 
 |     // ERROR: Bad dimensions | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #ifdef HAVE_SETJMP | 
 |   if (setjmp(error_mgr_->setjmp_buffer)) { | 
 |     // We called into jpeglib, it experienced an error sometime during this | 
 |     // function call, and we called longjmp() and rewound the stack to here. | 
 |     // Return error. | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #endif | 
 |   if (!StartDecode()) { | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 |   SetScanlinePointers(databuf_); | 
 |   int lines_left = dst_height; | 
 |   // Compute amount of lines to skip to implement vertical crop. | 
 |   // TODO(fbarchard): Ensure skip is a multiple of maximum component | 
 |   // subsample. ie 2 | 
 |   int skip = (GetHeight() - dst_height) / 2; | 
 |   if (skip > 0) { | 
 |     // There is no API to skip lines in the output data, so we read them | 
 |     // into the temp buffer. | 
 |     while (skip >= GetImageScanlinesPerImcuRow()) { | 
 |       if (!DecodeImcuRow()) { | 
 |         FinishDecode(); | 
 |         return LIBYUV_FALSE; | 
 |       } | 
 |       skip -= GetImageScanlinesPerImcuRow(); | 
 |     } | 
 |     if (skip > 0) { | 
 |       // Have a partial iMCU row left over to skip. Must read it and then | 
 |       // copy the parts we want into the destination. | 
 |       if (!DecodeImcuRow()) { | 
 |         FinishDecode(); | 
 |         return LIBYUV_FALSE; | 
 |       } | 
 |       for (int i = 0; i < num_outbufs_; ++i) { | 
 |         // TODO(fbarchard): Compute skip to avoid this | 
 |         assert(skip % GetVertSubSampFactor(i) == 0); | 
 |         int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); | 
 |         int scanlines_to_copy = | 
 |             GetComponentScanlinesPerImcuRow(i) - rows_to_skip; | 
 |         int data_to_skip = rows_to_skip * GetComponentStride(i); | 
 |         CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i), planes[i], | 
 |                   GetComponentWidth(i), GetComponentWidth(i), | 
 |                   scanlines_to_copy); | 
 |         planes[i] += scanlines_to_copy * GetComponentWidth(i); | 
 |       } | 
 |       lines_left -= (GetImageScanlinesPerImcuRow() - skip); | 
 |     } | 
 |   } | 
 |  | 
 |   // Read full MCUs but cropped horizontally | 
 |   for (; lines_left > GetImageScanlinesPerImcuRow(); | 
 |        lines_left -= GetImageScanlinesPerImcuRow()) { | 
 |     if (!DecodeImcuRow()) { | 
 |       FinishDecode(); | 
 |       return LIBYUV_FALSE; | 
 |     } | 
 |     for (int i = 0; i < num_outbufs_; ++i) { | 
 |       int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i); | 
 |       CopyPlane(databuf_[i], GetComponentStride(i), planes[i], | 
 |                 GetComponentWidth(i), GetComponentWidth(i), scanlines_to_copy); | 
 |       planes[i] += scanlines_to_copy * GetComponentWidth(i); | 
 |     } | 
 |   } | 
 |  | 
 |   if (lines_left > 0) { | 
 |     // Have a partial iMCU row left over to decode. | 
 |     if (!DecodeImcuRow()) { | 
 |       FinishDecode(); | 
 |       return LIBYUV_FALSE; | 
 |     } | 
 |     for (int i = 0; i < num_outbufs_; ++i) { | 
 |       int scanlines_to_copy = | 
 |           DivideAndRoundUp(lines_left, GetVertSubSampFactor(i)); | 
 |       CopyPlane(databuf_[i], GetComponentStride(i), planes[i], | 
 |                 GetComponentWidth(i), GetComponentWidth(i), scanlines_to_copy); | 
 |       planes[i] += scanlines_to_copy * GetComponentWidth(i); | 
 |     } | 
 |   } | 
 |   return FinishDecode(); | 
 | } | 
 |  | 
 | LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, | 
 |                                            void* opaque, | 
 |                                            int dst_width, | 
 |                                            int dst_height) { | 
 |   if (dst_width != GetWidth() || dst_height > GetHeight()) { | 
 |     // ERROR: Bad dimensions | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #ifdef HAVE_SETJMP | 
 |   if (setjmp(error_mgr_->setjmp_buffer)) { | 
 |     // We called into jpeglib, it experienced an error sometime during this | 
 |     // function call, and we called longjmp() and rewound the stack to here. | 
 |     // Return error. | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 | #endif | 
 |   if (!StartDecode()) { | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 |   SetScanlinePointers(databuf_); | 
 |   int lines_left = dst_height; | 
 |   // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop | 
 |   int skip = (GetHeight() - dst_height) / 2; | 
 |   if (skip > 0) { | 
 |     while (skip >= GetImageScanlinesPerImcuRow()) { | 
 |       if (!DecodeImcuRow()) { | 
 |         FinishDecode(); | 
 |         return LIBYUV_FALSE; | 
 |       } | 
 |       skip -= GetImageScanlinesPerImcuRow(); | 
 |     } | 
 |     if (skip > 0) { | 
 |       // Have a partial iMCU row left over to skip. | 
 |       if (!DecodeImcuRow()) { | 
 |         FinishDecode(); | 
 |         return LIBYUV_FALSE; | 
 |       } | 
 |       for (int i = 0; i < num_outbufs_; ++i) { | 
 |         // TODO(fbarchard): Compute skip to avoid this | 
 |         assert(skip % GetVertSubSampFactor(i) == 0); | 
 |         int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); | 
 |         int data_to_skip = rows_to_skip * GetComponentStride(i); | 
 |         // Change our own data buffer pointers so we can pass them to the | 
 |         // callback. | 
 |         databuf_[i] += data_to_skip; | 
 |       } | 
 |       int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip; | 
 |       (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy); | 
 |       // Now change them back. | 
 |       for (int i = 0; i < num_outbufs_; ++i) { | 
 |         int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); | 
 |         int data_to_skip = rows_to_skip * GetComponentStride(i); | 
 |         databuf_[i] -= data_to_skip; | 
 |       } | 
 |       lines_left -= scanlines_to_copy; | 
 |     } | 
 |   } | 
 |   // Read full MCUs until we get to the crop point. | 
 |   for (; lines_left >= GetImageScanlinesPerImcuRow(); | 
 |        lines_left -= GetImageScanlinesPerImcuRow()) { | 
 |     if (!DecodeImcuRow()) { | 
 |       FinishDecode(); | 
 |       return LIBYUV_FALSE; | 
 |     } | 
 |     (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow()); | 
 |   } | 
 |   if (lines_left > 0) { | 
 |     // Have a partial iMCU row left over to decode. | 
 |     if (!DecodeImcuRow()) { | 
 |       FinishDecode(); | 
 |       return LIBYUV_FALSE; | 
 |     } | 
 |     (*fn)(opaque, databuf_, databuf_strides_, lines_left); | 
 |   } | 
 |   return FinishDecode(); | 
 | } | 
 |  | 
 | void init_source(j_decompress_ptr cinfo) { | 
 |   fill_input_buffer(cinfo); | 
 | } | 
 |  | 
 | boolean fill_input_buffer(j_decompress_ptr cinfo) { | 
 |   BufferVector* buf_vec = reinterpret_cast<BufferVector*>(cinfo->client_data); | 
 |   if (buf_vec->pos >= buf_vec->len) { | 
 |     // Don't assert-fail when fuzzing. | 
 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | 
 |     assert(0 && "No more data"); | 
 | #endif | 
 |     // ERROR: No more data | 
 |     return FALSE; | 
 |   } | 
 |   cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data; | 
 |   cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len; | 
 |   ++buf_vec->pos; | 
 |   return TRUE; | 
 | } | 
 |  | 
 | void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {  // NOLINT | 
 |   jpeg_source_mgr* src = cinfo->src; | 
 |   size_t bytes = static_cast<size_t>(num_bytes); | 
 |   if (bytes > src->bytes_in_buffer) { | 
 |     src->next_input_byte = nullptr; | 
 |     src->bytes_in_buffer = 0; | 
 |   } else { | 
 |     src->next_input_byte += bytes; | 
 |     src->bytes_in_buffer -= bytes; | 
 |   } | 
 | } | 
 |  | 
 | void term_source(j_decompress_ptr cinfo) { | 
 |   (void)cinfo;  // Nothing to do. | 
 | } | 
 |  | 
 | #ifdef HAVE_SETJMP | 
 | void ErrorHandler(j_common_ptr cinfo) { | 
 | // This is called when a jpeglib command experiences an error. Unfortunately | 
 | // jpeglib's error handling model is not very flexible, because it expects the | 
 | // error handler to not return--i.e., it wants the program to terminate. To | 
 | // recover from errors we use setjmp() as shown in their example. setjmp() is | 
 | // C's implementation for the "call with current continuation" functionality | 
 | // seen in some functional programming languages. | 
 | // A formatted message can be output, but is unsafe for release. | 
 | #ifdef DEBUG | 
 |   char buf[JMSG_LENGTH_MAX]; | 
 |   (*cinfo->err->format_message)(cinfo, buf); | 
 | // ERROR: Error in jpeglib: buf | 
 | #endif | 
 |  | 
 |   SetJmpErrorMgr* mgr = reinterpret_cast<SetJmpErrorMgr*>(cinfo->err); | 
 |   // This rewinds the call stack to the point of the corresponding setjmp() | 
 |   // and causes it to return (for a second time) with value 1. | 
 |   longjmp(mgr->setjmp_buffer, 1); | 
 | } | 
 |  | 
 | // Suppress fprintf warnings. | 
 | void OutputHandler(j_common_ptr cinfo) { | 
 |   (void)cinfo; | 
 | } | 
 |  | 
 | #endif  // HAVE_SETJMP | 
 |  | 
 | void MJpegDecoder::AllocOutputBuffers(int num_outbufs) { | 
 |   if (num_outbufs != num_outbufs_) { | 
 |     // We could perhaps optimize this case to resize the output buffers without | 
 |     // necessarily having to delete and recreate each one, but it's not worth | 
 |     // it. | 
 |     DestroyOutputBuffers(); | 
 |  | 
 |     scanlines_ = new uint8_t**[num_outbufs]; | 
 |     scanlines_sizes_ = new int[num_outbufs]; | 
 |     databuf_ = new uint8_t*[num_outbufs]; | 
 |     databuf_strides_ = new int[num_outbufs]; | 
 |  | 
 |     for (int i = 0; i < num_outbufs; ++i) { | 
 |       scanlines_[i] = NULL; | 
 |       scanlines_sizes_[i] = 0; | 
 |       databuf_[i] = NULL; | 
 |       databuf_strides_[i] = 0; | 
 |     } | 
 |  | 
 |     num_outbufs_ = num_outbufs; | 
 |   } | 
 | } | 
 |  | 
 | void MJpegDecoder::DestroyOutputBuffers() { | 
 |   for (int i = 0; i < num_outbufs_; ++i) { | 
 |     delete[] scanlines_[i]; | 
 |     delete[] databuf_[i]; | 
 |   } | 
 |   delete[] scanlines_; | 
 |   delete[] databuf_; | 
 |   delete[] scanlines_sizes_; | 
 |   delete[] databuf_strides_; | 
 |   scanlines_ = NULL; | 
 |   databuf_ = NULL; | 
 |   scanlines_sizes_ = NULL; | 
 |   databuf_strides_ = NULL; | 
 |   num_outbufs_ = 0; | 
 | } | 
 |  | 
 | // JDCT_IFAST and do_block_smoothing improve performance substantially. | 
 | LIBYUV_BOOL MJpegDecoder::StartDecode() { | 
 |   decompress_struct_->raw_data_out = TRUE; | 
 |   decompress_struct_->dct_method = JDCT_IFAST;  // JDCT_ISLOW is default | 
 |   decompress_struct_->dither_mode = JDITHER_NONE; | 
 |   // Not applicable to 'raw': | 
 |   decompress_struct_->do_fancy_upsampling = (boolean)(LIBYUV_FALSE); | 
 |   // Only for buffered mode: | 
 |   decompress_struct_->enable_2pass_quant = (boolean)(LIBYUV_FALSE); | 
 |   // Blocky but fast: | 
 |   decompress_struct_->do_block_smoothing = (boolean)(LIBYUV_FALSE); | 
 |  | 
 |   if (!jpeg_start_decompress(decompress_struct_)) { | 
 |     // ERROR: Couldn't start JPEG decompressor"; | 
 |     return LIBYUV_FALSE; | 
 |   } | 
 |   return LIBYUV_TRUE; | 
 | } | 
 |  | 
 | LIBYUV_BOOL MJpegDecoder::FinishDecode() { | 
 |   // jpeglib considers it an error if we finish without decoding the whole | 
 |   // image, so we call "abort" rather than "finish". | 
 |   jpeg_abort_decompress(decompress_struct_); | 
 |   return LIBYUV_TRUE; | 
 | } | 
 |  | 
 | void MJpegDecoder::SetScanlinePointers(uint8_t** data) { | 
 |   for (int i = 0; i < num_outbufs_; ++i) { | 
 |     uint8_t* data_i = data[i]; | 
 |     for (int j = 0; j < scanlines_sizes_[i]; ++j) { | 
 |       scanlines_[i][j] = data_i; | 
 |       data_i += GetComponentStride(i); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() { | 
 |   return (unsigned int)(GetImageScanlinesPerImcuRow()) == | 
 |          jpeg_read_raw_data(decompress_struct_, scanlines_, | 
 |                             GetImageScanlinesPerImcuRow()); | 
 | } | 
 |  | 
 | // The helper function which recognizes the jpeg sub-sampling type. | 
 | JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper( | 
 |     int* subsample_x, | 
 |     int* subsample_y, | 
 |     int number_of_components) { | 
 |   if (number_of_components == 3) {  // Color images. | 
 |     if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 2 && | 
 |         subsample_y[1] == 2 && subsample_x[2] == 2 && subsample_y[2] == 2) { | 
 |       return kJpegYuv420; | 
 |     } | 
 |     if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 2 && | 
 |         subsample_y[1] == 1 && subsample_x[2] == 2 && subsample_y[2] == 1) { | 
 |       return kJpegYuv422; | 
 |     } | 
 |     if (subsample_x[0] == 1 && subsample_y[0] == 1 && subsample_x[1] == 1 && | 
 |         subsample_y[1] == 1 && subsample_x[2] == 1 && subsample_y[2] == 1) { | 
 |       return kJpegYuv444; | 
 |     } | 
 |   } else if (number_of_components == 1) {  // Grey-scale images. | 
 |     if (subsample_x[0] == 1 && subsample_y[0] == 1) { | 
 |       return kJpegYuv400; | 
 |     } | 
 |   } | 
 |   return kJpegUnknown; | 
 | } | 
 |  | 
 | }  // namespace libyuv | 
 | #endif  // HAVE_JPEG |