mjpeg_decoder.cc 19 KB

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