mjpeg_decoder.cc 18 KB

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