573 lines · 18.5 KB · C++
RawDownload
/* *  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 #if defined(_MSC_VER)// disable warning 4324: structure was padded due to __declspec(align())#pragma warning(disable : 4324)#endif #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);void OutputHandler(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;  error_mgr_->base.output_message = &OutputHandler;#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_t* 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_t*[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_t[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_t** 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) {  (void)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);} // Suppress fprintf warnings.void OutputHandler(j_common_ptr cinfo) {  (void)cinfo;} #endif  // HAVE_SETJMP 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_t**[num_outbufs];    scanlines_sizes_ = new int[num_outbufs];    databuf_ = new uint8_t*[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;

Lines 1–500 of 573.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t