/* * 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#endifstruct FILE; // For jpeglib.h. // C++ build requires extern C for jpeg internals.#ifdef __cplusplusextern "C" {#endif #include <jpeglib.h> #ifdef __cplusplus} // extern "C"#endif #include "libyuv/planar_functions.h" // For CopyPlane(). namespace libyuv { #ifdef HAVE_SETJMPstruct 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); // NOLINTvoid 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 = 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* [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 DCTSIZEint 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 = reinterpret_cast<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_SETJMPvoid 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);}#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;Lines 1–500 of 566. Load more
/* * 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 = 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* [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 = reinterpret_cast<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 = 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); } #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;