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