/* * 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) // Must be included before jpeglib. #include <setjmp.h> #define HAVE_SETJMP
#ifdefined(_MSC_VER) // disable warning 4324: structure was padded due to __declspec(align()) #pragma warning(disable : 4324) #endif
#endif
#include <stdio.h> // 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
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_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;
}
// 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_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 term_source(j_decompress_ptr cinfo) {
(void)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);
}
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_ = newint[num_outbufs];
databuf_ = new uint8_t*[num_outbufs];
databuf_strides_ = newint[num_outbufs];
for (int i = 0; i < num_outbufs; ++i) {
scanlines_[i] = NULL;
scanlines_sizes_[i] = 0;
databuf_[i] = NULL;
databuf_strides_[i] = 0;
}
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_t** data) { for (int i = 0; i < num_outbufs_; ++i) {
uint8_t* data_i = data[i]; for (int j = 0; j < scanlines_sizes_[i]; ++j) {
scanlines_[i][j] = data_i;
data_i += GetComponentStride(i);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.