Commit 69fe6bd1 authored by fbarchard@google.com's avatar fbarchard@google.com

mjpeg class for low level interface. higher level will be MJPGToI420

BUG=none
TEST=none
Review URL: https://webrtc-codereview.appspot.com/400002

git-svn-id: http://libyuv.googlecode.com/svn/trunk@177 16f28f9a-4ce2-e073-06de-1de4eb20be90
parent f1b6063f
vars = {
"libyuv_trunk" : "https://libyuv.googlecode.com/svn/trunk",
"chromium_trunk" : "http://src.chromium.org/svn/trunk",
"chromium_revision": "95033",
"chromium_revision": "119959",
# Use this googlecode_url variable only if there is an internal mirror for it.
# If you do not know, use the full path while defining your new deps entry.
"googlecode_url": "http://%s.googlecode.com/svn",
......@@ -17,9 +17,13 @@ deps = {
"trunk/testing/gtest":
(Var("googlecode_url") % "googletest") + "/trunk@573",
"trunk/tools/gyp":
"trunk/tools/gyp":
(Var("googlecode_url") % "gyp") + "/trunk@985",
# Dependencies used by libjpeg-turbo
"trunk/third_party/libjpeg_turbo/":
Var("chromium_trunk") + "/src/third_party/libjpeg_turbo@" + Var("chromium_revision"),
"trunk/third_party/yasm/":
Var("chromium_trunk") + "/src/third_party/yasm@" + Var("chromium_revision"),
}
......
Name: libyuv
URL: http://code.google.com/p/libyuv/
Version: 176
Version: 177
License: BSD
License File: LICENSE
......
/*
* Copyright (c) 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.
*/
#ifndef LIBYUV_MJPEG_DECODER_H_
#define LIBYUV_MJPEG_DECODER_H_
#include "libyuv/basic_types.h"
#include "libyuv/scoped_ptr.h"
struct jpeg_common_struct;
struct jpeg_decompress_struct;
struct jpeg_source_mgr;
namespace libyuv {
enum JpegSubsamplingType {
kJpegYuv420,
kJpegYuv422,
kJpegYuv411, // TODO(fbarchard): Test 411
kJpegYuv444,
kJpegYuv400,
kJpegUnknown
};
struct SetJmpErrorMgr;
// MJPEG ("Motion JPEG") is a pseudo-standard video codec where the frames are
// simply independent JPEG images with a fixed huffman table (which is omitted).
// It is rarely used in video transmission, but is common as a camera capture
// format, especially in Logitech devices. This class implements a decoder for
// MJPEG frames.
//
// See http://tools.ietf.org/html/rfc2435
class MJpegDecoder {
public:
typedef void (*CallbackFunction)(void* opaque,
const uint8* const * data,
const int* strides,
int rows);
static const int kColorSpaceUnknown;
static const int kColorSpaceGrayscale;
static const int kColorSpaceRgb;
static const int kColorSpaceYCbCr;
static const int kColorSpaceCMYK;
static const int kColorSpaceYCCK;
MJpegDecoder();
~MJpegDecoder();
// Loads a new frame, reads its headers, and determines the uncompressed
// image format. Returns true if image looks valid and format is supported.
// If return value is true, then the values for all the following getters
// are populated.
// src_len is the size of the compressed mjpeg frame in bytes.
bool LoadFrame(const uint8* src, size_t src_len);
// Returns width of the last loaded frame in pixels.
int GetWidth();
// Returns height of the last loaded frame in pixels.
int GetHeight();
// Returns format of the last loaded frame. The return value is one of the
// kColorSpace* constants.
int GetColorSpace();
// Number of color components in the color space.
int GetNumComponents();
// Sample factors of the n-th component.
int GetHorizSampFactor(int component);
int GetVertSampFactor(int component);
int GetHorizSubSampFactor(int component);
int GetVertSubSampFactor(int component);
// Public for testability
int GetImageScanlinesPerImcuRow();
// Public for testability
int GetComponentScanlinesPerImcuRow(int component);
// Width of a component in bytes.
int GetComponentWidth(int component);
// Height of a component.
int GetComponentHeight(int component);
// Width of a component in bytes with padding for DCTSIZE. Public for testing.
int GetComponentStride(int component);
// Size of a component in bytes.
int GetComponentSize(int component);
// Call this after LoadFrame() if you decide you don't want to decode it
// after all.
bool UnloadFrame();
// Decodes the entire image into a one-buffer-per-color-component format.
// dst_width must match exactly. dst_height must be <= to image height; if
// less, the image is cropped. "planes" must have size equal to at least
// GetNumComponents() and they must point to non-overlapping buffers of size
// at least GetComponentSize(i). The pointers in planes are incremented
// to point to after the end of the written data.
// TODO(fbarchard): Add dst_x, dst_y to allow specific rect to be decoded.
bool DecodeToBuffers(uint8** planes, int dst_width, int dst_height);
// Decodes the entire image and passes the data via repeated calls to a
// callback function. Each call will get the data for a whole number of
// image scanlines.
// TODO(fbarchard): Add dst_x, dst_y to allow specific rect to be decoded.
bool DecodeToCallback(CallbackFunction fn, void* opaque,
int dst_width, int dst_height);
// The helper function which recognizes the jpeg sub-sampling type.
static JpegSubsamplingType JpegSubsamplingTypeHelper(
int* subsample_x, int* subsample_y, int number_of_components);
private:
struct Buffer {
const uint8* data;
int len;
};
struct BufferVector {
Buffer *buffers;
int len;
int pos;
};
// Methods that are passed to jpeglib.
static int fill_input_buffer(jpeg_decompress_struct *cinfo);
static void init_source(jpeg_decompress_struct *cinfo);
static void skip_input_data(jpeg_decompress_struct *cinfo, long num_bytes);
static void term_source(jpeg_decompress_struct *cinfo);
static void ErrorHandler(jpeg_common_struct *cinfo);
void AllocOutputBuffers(int num_outbufs);
void DestroyOutputBuffers();
bool StartDecode();
bool FinishDecode();
void SetScanlinePointers(uint8** data);
bool DecodeImcuRow();
int GetComponentScanlinePadding(int component);
// A buffer holding the input data for a frame.
Buffer buf_;
BufferVector buf_vec_;
libyuv::scoped_ptr<jpeg_decompress_struct> decompress_struct_;
libyuv::scoped_ptr<jpeg_source_mgr> source_mgr_;
libyuv::scoped_ptr<SetJmpErrorMgr> error_mgr_;
// true iff at least one component has scanline padding. (i.e.,
// GetComponentScanlinePadding() != 0.)
bool has_scanline_padding_;
// Temporaries used to point to scanline outputs.
int num_outbufs_; // Outermost size of all arrays below.
uint8** *scanlines_;
int* scanlines_sizes_;
// Temporary buffer used for decoding when we can't decode directly to the
// output buffers. Large enough for just one iMCU row.
uint8** databuf_;
int* databuf_strides_;
};
} // namespace libyuv
#endif // LIBYUV_MJPEG_DECODER_H_
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
// Copyright (c) 2001, 2002 Peter Dimov
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
//
// scoped_ptr mimics a built-in pointer except that it guarantees deletion
// of the object pointed to, either on destruction of the scoped_ptr or via
// an explicit reset(). scoped_ptr is a simple solution for simple needs;
// use shared_ptr or std::auto_ptr if your needs are more complex.
// scoped_ptr_malloc added in by Google. When one of
// these goes out of scope, instead of doing a delete or delete[], it
// calls free(). scoped_ptr_malloc<char> is likely to see much more
// use than any other specializations.
// release() added in by Google. Use this to conditionally
// transfer ownership of a heap-allocated object to the caller, usually on
// method success.
// TODO(fbarchard): move into source as implementation detail
#ifndef LIBYUV_SCOPED_PTR_H__
#define LIBYUV_SCOPED_PTR_H__
#include <cstddef> // for std::ptrdiff_t
#include <stdlib.h> // for free() decl
#ifdef _WIN32
namespace std { using ::ptrdiff_t; };
#endif // _WIN32
namespace libyuv {
template <typename T>
class scoped_ptr {
private:
T* ptr;
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
public:
typedef T element_type;
explicit scoped_ptr(T* p = NULL): ptr(p) {}
~scoped_ptr() {
typedef char type_must_be_complete[sizeof(T)];
delete ptr;
}
void reset(T* p = NULL) {
typedef char type_must_be_complete[sizeof(T)];
if (ptr != p) {
T* obj = ptr;
ptr = p;
// Delete last, in case obj destructor indirectly results in ~scoped_ptr
delete obj;
}
}
T& operator*() const {
return *ptr;
}
T* operator->() const {
return ptr;
}
T* get() const {
return ptr;
}
void swap(scoped_ptr & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = NULL;
return tmp;
}
T** accept() {
if (ptr) {
delete ptr;
ptr = NULL;
}
return &ptr;
}
T** use() {
return &ptr;
}
};
template<typename T> inline
void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
a.swap(b);
}
// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
// is guaranteed, either on destruction of the scoped_array or via an explicit
// reset(). Use shared_array or std::vector if your needs are more complex.
template<typename T>
class scoped_array {
private:
T* ptr;
scoped_array(scoped_array const &);
scoped_array & operator=(scoped_array const &);
public:
typedef T element_type;
explicit scoped_array(T* p = NULL) : ptr(p) {}
~scoped_array() {
typedef char type_must_be_complete[sizeof(T)];
delete[] ptr;
}
void reset(T* p = NULL) {
typedef char type_must_be_complete[sizeof(T)];
if (ptr != p) {
T* arr = ptr;
ptr = p;
// Delete last, in case arr destructor indirectly results in ~scoped_array
delete [] arr;
}
}
T& operator[](std::ptrdiff_t i) const {
return ptr[i];
}
T* get() const {
return ptr;
}
void swap(scoped_array & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = NULL;
return tmp;
}
T** accept() {
if (ptr) {
delete [] ptr;
ptr = NULL;
}
return &ptr;
}
};
template<class T> inline
void swap(scoped_array<T>& a, scoped_array<T>& b) {
a.swap(b);
}
// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
// second template argument, the function used to free the object.
template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc {
private:
T* ptr;
scoped_ptr_malloc(scoped_ptr_malloc const &);
scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
public:
typedef T element_type;
explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
~scoped_ptr_malloc() {
FF(static_cast<void*>(ptr));
}
void reset(T* p = 0) {
if (ptr != p) {
FF(static_cast<void*>(ptr));
ptr = p;
}
}
T& operator*() const {
return *ptr;
}
T* operator->() const {
return ptr;
}
T* get() const {
return ptr;
}
void swap(scoped_ptr_malloc & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = 0;
return tmp;
}
T** accept() {
if (ptr) {
FF(static_cast<void*>(ptr));
ptr = 0;
}
return &ptr;
}
};
template<typename T, void (*FF)(void*)> inline
void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) {
a.swap(b);
}
} // namespace libyuv
#endif // #ifndef LIBYUV_SCOPED_PTR_H__
......@@ -11,7 +11,7 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 175
#define LIBYUV_VERSION 177
#endif // INCLUDE_LIBYUV_VERSION_H_
/*
* Copyright (c) 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"
// Must be included before jpeglib
#include <assert.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <climits>
#include <cstring>
#ifdef WIN32
// jpeglib defines INT32 with a definition that conflicts with the one
// in the Vista platforms SDK. By defining XMD_H, it skips its definition of
// INT16 and INT32 and we can define them ourself the Windows way.
#define XMD_H
typedef signed short INT16;
typedef signed int INT32;
#endif
extern "C" {
// TODO(fbarchard): Port libjpeg_turbo to arm/neon
#if defined(__arm__)
// On ARM, require a system-installed libjpeg if JPEG support is enabled.
#include <jpeglib.h>
#else
#include "third_party/libjpeg_turbo/src/jpeglib.h"
#endif
}
namespace libyuv {
struct SetJmpErrorMgr {
jpeg_error_mgr base; // Must be at the top
jmp_buf setjmp_buffer;
};
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;
MJpegDecoder::MJpegDecoder()
: has_scanline_padding_(false),
num_outbufs_(0),
scanlines_(NULL),
scanlines_sizes_(NULL),
databuf_(NULL),
databuf_strides_(NULL) {
decompress_struct_.reset(new jpeg_decompress_struct);
source_mgr_.reset(new jpeg_source_mgr);
error_mgr_.reset(new SetJmpErrorMgr);
decompress_struct_->err = jpeg_std_error(&error_mgr_->base);
// Override standard exit()-based error handler.
error_mgr_->base.error_exit = &ErrorHandler;
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_.get());
decompress_struct_->src = source_mgr_.get();
buf_vec_.buffers = &buf_;
buf_vec_.len = 1;
}
MJpegDecoder::~MJpegDecoder() {
jpeg_destroy_decompress(decompress_struct_.get());
DestroyOutputBuffers();
}
// Helper function to validate the jpeg looks ok.
// TODO(fbarchard): Improve performance. Scan backward for EOI?
bool ValidateJpeg(const uint8* sample, size_t sample_size) {
if (sample_size < 64) {
// ERROR: Invalid jpeg size: sample_size
return false;
}
if (sample[0] != 0xff || sample[1] != 0xd8) {
// ERROR: Invalid jpeg initial start code
return false;
}
bool soi = true;
int total_eoi = 0;
for (int i = 2; i < static_cast<int>(sample_size) - 1; ++i) {
if (sample[i] == 0xff) {
if (sample[i + 1] == 0xd8) { // Start Of Image
soi = true;
} else if (sample[i + 1] == 0xd9) { // End Of Image
if (soi) {
++total_eoi;
}
soi = false;
}
}
}
if (!total_eoi) {
// ERROR: Invalid jpeg end code not found. Size sample_size
return false;
}
return true;
}
bool MJpegDecoder::LoadFrame(const uint8* src, size_t src_len) {
if (!ValidateJpeg(src, src_len)) {
return false;
}
buf_.data = src;
buf_.len = src_len;
buf_vec_.pos = 0;
decompress_struct_->client_data = &buf_vec_;
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 false;
}
if (jpeg_read_header(decompress_struct_.get(), TRUE) != JPEG_HEADER_OK) {
// ERROR: Bad MJPEG header
return 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_ = true;
}
}
return 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);
}
bool MJpegDecoder::UnloadFrame() {
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 false;
}
jpeg_abort_decompress(decompress_struct_.get());
return true;
}
static void CopyRows(uint8* source, int source_stride,
uint8* dest, int pixels, int numrows) {
for (int i = 0; i < numrows; ++i) {
memcpy(dest, source, pixels);
dest += pixels;
source += source_stride;
}
}
// TODO(fbarchard): Allow rectangle to be specified: x, y, width, height.
bool MJpegDecoder::DecodeToBuffers(
uint8** planes, int dst_width, int dst_height) {
if (dst_width != GetWidth() ||
dst_height > GetHeight()) {
// ERROR: Bad dimensions
return false;
}
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 false;
}
if (!StartDecode()) {
return 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 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 false;
}
for (int i = 0; i < num_outbufs_; ++i) {
// TODO(fbarchard): Compute skip to avoid this
assert(skip % GetVertSubSampFactor(i) == 0);
int scanlines_to_skip =
DivideAndRoundDown(skip, GetVertSubSampFactor(i));
int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) -
scanlines_to_skip;
int data_to_skip = scanlines_to_skip * GetComponentStride(i);
CopyRows(databuf_[i] + data_to_skip, GetComponentStride(i),
planes[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 false;
}
for (int i = 0; i < num_outbufs_; ++i) {
int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i);
CopyRows(databuf_[i], GetComponentStride(i),
planes[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 false;
}
for (int i = 0; i < num_outbufs_; ++i) {
int scanlines_to_copy =
DivideAndRoundUp(lines_left, GetVertSubSampFactor(i));
CopyRows(databuf_[i], GetComponentStride(i),
planes[i], GetComponentWidth(i), scanlines_to_copy);
planes[i] += scanlines_to_copy * GetComponentWidth(i);
}
}
return FinishDecode();
}
bool MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque,
int dst_width, int dst_height) {
if (dst_width != GetWidth() ||
dst_height > GetHeight()) {
// ERROR: Bad dimensions
return false;
}
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 false;
}
if (!StartDecode()) {
return 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 false;
}
skip -= GetImageScanlinesPerImcuRow();
}
if (skip > 0) {
// Have a partial iMCU row left over to skip.
if (!DecodeImcuRow()) {
FinishDecode();
return false;
}
for (int i = 0; i < num_outbufs_; ++i) {
// TODO(fbarchard): Compute skip to avoid this
assert(skip % GetVertSubSampFactor(i) == 0);
int scanlines_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
int data_to_skip = scanlines_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 scanlines_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
int data_to_skip = scanlines_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 false;
}
(*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow());
}
if (lines_left > 0) {
// Have a partial iMCU row left over to decode.
if (!DecodeImcuRow()) {
FinishDecode();
return false;
}
(*fn)(opaque, databuf_, databuf_strides_, lines_left);
}
return FinishDecode();
}
void MJpegDecoder::init_source(j_decompress_ptr cinfo) {
fill_input_buffer(cinfo);
}
boolean MJpegDecoder::fill_input_buffer(j_decompress_ptr cinfo) {
BufferVector *buf_vec = static_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 MJpegDecoder::skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
cinfo->src->next_input_byte += num_bytes;
}
void MJpegDecoder::term_source(j_decompress_ptr cinfo) {
// Nothing to do.
}
void MJpegDecoder::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.
char buf[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buf);
// ERROR: Error in jpeglib: buf
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** [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.
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;
decompress_struct_->do_fancy_upsampling = false; // Not applicable to 'raw'
decompress_struct_->enable_2pass_quant = false; // Only for buffered mode
decompress_struct_->do_block_smoothing = false; // blocky but fast
if (!jpeg_start_decompress(decompress_struct_.get())) {
// ERROR: Couldn't start JPEG decompressor";
return false;
}
return true;
}
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_.get());
return 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 bool MJpegDecoder::DecodeImcuRow() {
return static_cast<unsigned int>(GetImageScanlinesPerImcuRow()) ==
jpeg_read_raw_data(decompress_struct_.get(),
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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment