| Index: source/libvpx/third_party/libyuv/source/mjpeg_decoder.cc
|
| ===================================================================
|
| --- source/libvpx/third_party/libyuv/source/mjpeg_decoder.cc (revision 0)
|
| +++ source/libvpx/third_party/libyuv/source/mjpeg_decoder.cc (revision 0)
|
| @@ -0,0 +1,566 @@
|
| +/*
|
| + * 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
|
| +#endif
|
| +struct FILE; // 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);
|
| +
|
| +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;
|
| +#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* src, size_t src_len) {
|
| + if (!ValidateJpeg(src, src_len)) {
|
| + return LIBYUV_FALSE;
|
| + }
|
| +
|
| + buf_.data = src;
|
| + buf_.len = (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* [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[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** 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 = (BufferVector*)(cinfo->client_data);
|
| + if (buf_vec->pos >= buf_vec->len) {
|
| + assert(0 && "No more data");
|
| + // 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
|
| + cinfo->src->next_input_byte += num_bytes;
|
| +}
|
| +
|
| +void term_source(j_decompress_ptr 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 = (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);
|
| +}
|
| +#endif
|
| +
|
| +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** [num_outbufs];
|
| + scanlines_sizes_ = new int[num_outbufs];
|
| + databuf_ = new uint8* [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** data) {
|
| + for (int i = 0; i < num_outbufs_; ++i) {
|
| + uint8* 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;
|
| + } else 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;
|
| + } else 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
|
| +
|
|
|