add incremental decoding

The object WebPIDecoder is available to store the
decoding state. The flow is typically:

   WebPIDecoder* const idec = WebPINew(mode);
   while (has_more_data) {
     // ... (get additional data)
     status = WebPIAppend(idec, new_data, new_data_size);
     if (status != VP8_STATUS_SUSPENDED ||
       break;
     }

     // The above call decodes the current available buffer.
     // Part of the image can now be refreshed by calling to
     // WebPIDecGetRGB()/WebPIDecGetYUV() etc.
   }
   WebPIDelete(idec);

Doing so, one can try and decode new macroblocks everytime fresh
bytes are available.
There's two operating modes: either appending fresh bytes, or
updating the whole buffer with additional data in the end.
The latter requires less memcpy()'s

main patch by Somnath Banerjee (somnath at google.com)

Change-Id: Ie81cbd0b50f175743af06b1f964de838b9a10a4a
This commit is contained in:
Pascal Massimino 2011-03-23 18:03:53 -07:00
parent 1f28832834
commit 8bf76fe0c8
10 changed files with 748 additions and 79 deletions

View File

@ -44,7 +44,8 @@ OBJS = src/enc/webpenc.o src/enc/bit_writer.o src/enc/syntax.o \
src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \ src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \
src/enc/cost.o src/enc/picture.o src/enc/filter.o \ src/enc/cost.o src/enc/picture.o src/enc/filter.o \
src/dec/bits.o src/dec/dsp.o src/dec/frame.o src/dec/webp.o \ src/dec/bits.o src/dec/dsp.o src/dec/frame.o src/dec/webp.o \
src/dec/quant.o src/dec/tree.o src/dec/vp8.o src/dec/yuv.o src/dec/quant.o src/dec/tree.o src/dec/vp8.o src/dec/yuv.o \
src/dec/idec.o
HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/bit_writer.h \ HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/bit_writer.h \
src/enc/cost.h src/dec/bits.h src/dec/vp8i.h src/dec/yuv.h src/enc/cost.h src/dec/bits.h src/dec/vp8i.h src/dec/yuv.h
OUTPUT = examples/cwebp examples/dwebp src/libwebp.a OUTPUT = examples/cwebp examples/dwebp src/libwebp.a

View File

@ -1,7 +1,7 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \
quant.c tree.c vp8.c webp.c yuv.c quant.c tree.c vp8.c webp.c yuv.c idec.c
libwebpdecode_la_LDFLAGS = -version-info 0:0:0 libwebpdecode_la_LDFLAGS = -version-info 0:0:0
libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h
libwebpdecodeincludedir = $(includedir)/webp libwebpdecodeincludedir = $(includedir)/webp

View File

@ -49,7 +49,7 @@ CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES) LTLIBRARIES = $(noinst_LTLIBRARIES)
libwebpdecode_la_LIBADD = libwebpdecode_la_LIBADD =
am_libwebpdecode_la_OBJECTS = bits.lo dsp.lo frame.lo quant.lo tree.lo \ am_libwebpdecode_la_OBJECTS = bits.lo dsp.lo frame.lo quant.lo tree.lo \
vp8.lo webp.lo yuv.lo vp8.lo webp.lo yuv.lo idec.lo
libwebpdecode_la_OBJECTS = $(am_libwebpdecode_la_OBJECTS) libwebpdecode_la_OBJECTS = $(am_libwebpdecode_la_OBJECTS)
libwebpdecode_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libwebpdecode_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@ -213,7 +213,7 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \
quant.c tree.c vp8.c webp.c yuv.c quant.c tree.c vp8.c webp.c yuv.c idec.c
libwebpdecode_la_LDFLAGS = -version-info 0:0:0 libwebpdecode_la_LDFLAGS = -version-info 0:0:0
libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h
@ -275,6 +275,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frame.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frame.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idec.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quant.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quant.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vp8.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vp8.Plo@am__quote@

567
src/dec/idec.c Normal file
View File

@ -0,0 +1,567 @@
// Copyright 2011 Google Inc.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Incremental decoding
//
// Author: somnath@google.com (Somnath Banerjee)
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "webpi.h"
#include "vp8i.h"
#include "yuv.h"
#define RIFF_HEADER_SIZE 20
#define VP8_HEADER_SIZE 10
#define WEBP_HEADER_SIZE (RIFF_HEADER_SIZE + VP8_HEADER_SIZE)
#define CHUNK_SIZE 4096
#define MAX_MB_SIZE 4096
//------------------------------------------------------------------------------
// Data structures for memory and states
// Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
// If there is any error the decoder goes into state ERROR.
typedef enum { STATE_HEADER = 0, STATE_PARTS0 = 1,
STATE_DATA = 2, STATE_DONE = 3,
STATE_ERROR = 4
} DecState;
// Operating state for the MemBuffer
typedef enum { MEM_MODE_NONE = 0,
MEM_MODE_APPEND, MEM_MODE_MAP
} MemBufferMode;
// storage for partition #0 and partial data (in a rolling fashion)
typedef struct {
MemBufferMode mode_; // Operation mode
uint32_t start_; // start location of the data to be decoded
uint32_t end_; // end location
size_t buf_size_; // size of the allocated buffer
uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
size_t part0_size_; // size of partition #0
const uint8_t* part0_buf_; // buffer to store partition #0
} MemBuffer;
struct WebPIDecoder {
DecState state_; // current decoding state
int w_, h_; // width and height
WebPDecParams params_; // Params to store output info
VP8Decoder* dec_;
VP8Io io_;
MemBuffer mem_; // memory buffer
};
// MB context to restore in case VP8DecodeMB() fails
typedef struct {
VP8MB left_;
VP8MB info_;
uint8_t intra_t_[4];
uint8_t intra_l_[4];
VP8BitReader br_;
VP8BitReader token_br_;
} MBContext;
//------------------------------------------------------------------------------
// MemBuffer: incoming data handling
#define REMAP(PTR, OLD_BASE, NEW_BASE) (PTR) = (NEW_BASE) + ((PTR) - OLD_BASE)
static inline size_t MemDataSize(const MemBuffer* mem) {
return (mem->end_ - mem->start_);
}
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
// size if required and also updates VP8BitReader's if new memory is allocated.
static int AppendToMemBuffer(WebPIDecoder* const idec,
const uint8_t* const data, size_t data_size) {
MemBuffer* const mem = &idec->mem_;
VP8Decoder* const dec = idec->dec_;
const int last_part = dec->num_parts_ - 1;
assert(mem->mode_ == MEM_MODE_APPEND);
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
int p;
uint8_t* new_buf = NULL;
const int num_chunks = (MemDataSize(mem) + data_size + CHUNK_SIZE - 1)
/ CHUNK_SIZE;
const size_t new_size = num_chunks * CHUNK_SIZE;
const uint8_t* const base = mem->buf_ + mem->start_;
new_buf = (uint8_t*)malloc(new_size);
if (!new_buf) return 0;
memcpy(new_buf, base, MemDataSize(mem));
// adjust VP8BitReader pointers
for (p = 0; p <= last_part; ++p) {
if (dec->parts_[p].buf_) {
REMAP(dec->parts_[p].buf_, base, new_buf);
REMAP(dec->parts_[p].buf_end_, base, new_buf);
}
}
// adjust memory pointers
free(mem->buf_);
mem->buf_ = new_buf;
mem->buf_size_ = new_size;
mem->end_ = MemDataSize(mem);
mem->start_ = 0;
}
memcpy(mem->buf_ + mem->end_, data, data_size);
mem->end_ += data_size;
assert(mem->end_ <= mem->buf_size_);
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
// note: setting up idec->io_ is only really needed at the beginning
// of the decoding, till partition #0 is complete.
idec->io_.data = mem->buf_ + mem->start_;
idec->io_.data_size = MemDataSize(mem);
return 1;
}
static int RemapMemBuffer(WebPIDecoder* const idec,
const uint8_t* const data, size_t data_size) {
int p;
MemBuffer* const mem = &idec->mem_;
VP8Decoder* const dec = idec->dec_;
const int last_part = dec->num_parts_ - 1;
const uint8_t* base = mem->buf_;
assert(mem->mode_ == MEM_MODE_MAP);
if (data_size < mem->buf_size_) {
return 0; // we cannot remap to a shorter buffer!
}
for (p = 0; p <= last_part; ++p) {
if (dec->parts_[p].buf_) {
REMAP(dec->parts_[p].buf_, base, data);
REMAP(dec->parts_[p].buf_end_, base, data);
}
}
dec->parts_[last_part].buf_end_ = data + data_size;
// Remap partition #0 data pointer to new offset.
if (dec->br_.buf_) {
REMAP(dec->br_.buf_, base, data);
REMAP(dec->br_.buf_end_, base, data);
}
mem->buf_ = (uint8_t*)data;
mem->end_ = mem->buf_size_ = data_size;
idec->io_.data = data;
idec->io_.data_size = data_size;
return 1;
}
static void InitMemBuffer(MemBuffer* const mem) {
mem->mode_ = MEM_MODE_NONE;
mem->buf_ = 0;
mem->buf_size_ = 0;
mem->part0_buf_ = 0;
mem->part0_size_ = 0;
}
static void ClearMemBuffer(MemBuffer* const mem) {
assert(mem);
if (mem->mode_ == MEM_MODE_APPEND) {
free(mem->buf_);
free((void*)mem->part0_buf_);
}
}
static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
if (mem->mode_ == MEM_MODE_NONE) {
mem->mode_ = expected; // switch to the expected mode
} else if (mem->mode_ != expected) {
return 0; // we mixed the modes => error
}
assert(mem->mode_ == expected); // mode is ok
return 1;
}
#undef REMAP
//------------------------------------------------------------------------------
// Macroblock-decoding contexts
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
MBContext* const context) {
const VP8BitReader* const br = &dec->br_;
const VP8MB* const left = dec->mb_info_ - 1;
const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
context->left_ = *left;
context->info_ = *info;
context->br_ = *br;
context->token_br_ = *token_br;
memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
memcpy(context->intra_l_, dec->intra_l_, 4);
}
static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
VP8BitReader* const token_br) {
VP8BitReader* const br = &dec->br_;
VP8MB* const left = dec->mb_info_ - 1;
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
*left = context->left_;
*info = context->info_;
*br = context->br_;
*token_br = context->token_br_;
memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
memcpy(dec->intra_l_, context->intra_l_, 4);
}
//------------------------------------------------------------------------------
static VP8StatusCode IDecError(WebPIDecoder* idec, VP8StatusCode error) {
idec->state_ = STATE_ERROR;
return error;
}
// Header
static VP8StatusCode DecodeHeader(WebPIDecoder* const idec) {
int width, height;
uint32_t curr_size, riff_header_size, bits;
WebPDecParams* params = &idec->params_;
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
if (MemDataSize(&idec->mem_) < WEBP_HEADER_SIZE) {
return VP8_STATUS_SUSPENDED;
}
if (!WebPInitDecParams(data, idec->mem_.end_, &width, &height, params)) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
// Validate and Skip over RIFF header
curr_size = MemDataSize(&idec->mem_);
if (!WebPCheckRIFFHeader(&data, &curr_size)) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
riff_header_size = idec->mem_.end_ - curr_size;
bits = data[0] | (data[1] << 8) | (data[2] << 16);
idec->mem_.part0_size_ = (bits >> 5) + VP8_HEADER_SIZE;
idec->mem_.start_ += riff_header_size;
assert(idec->mem_.start_ <= idec->mem_.end_);
idec->w_ = width;
idec->h_ = height;
idec->io_.data_size -= riff_header_size;
idec->io_.data = data;
idec->state_ = STATE_PARTS0;
return VP8_STATUS_OK;
}
// Partition #0
static int CopyParts0Data(WebPIDecoder* idec) {
VP8BitReader* const br = &idec->dec_->br_;
const size_t psize = br->buf_end_ - br->buf_;
MemBuffer* const mem = &idec->mem_;
assert(!mem->part0_buf_);
assert(psize > 0);
assert(psize <= mem->part0_size_);
if (mem->mode_ == MEM_MODE_APPEND) {
// We copy and grab ownership of the partition #0 data.
uint8_t* const part0_buf = (uint8_t*)malloc(psize);
if (!part0_buf) {
return 0;
}
memcpy(part0_buf, br->buf_, psize);
mem->part0_buf_ = part0_buf;
mem->start_ += psize;
br->buf_ = part0_buf;
br->buf_end_ = part0_buf + psize;
} else {
// Else: just keep pointers to the partition #0's data in dec_->br_.
}
return 1;
}
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
VP8Decoder* const dec = idec->dec_;
VP8Io* const io = &idec->io_;
const WebPDecParams* const params = &idec->params_;
const WEBP_CSP_MODE mode = params->mode;
// Wait till we have enough data for the whole partition #0
if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
return VP8_STATUS_SUSPENDED;
}
io->opaque = &idec->params_;
if (!VP8GetHeaders(dec, io)) {
const VP8StatusCode status = dec->status_;
if (status == VP8_STATUS_SUSPENDED ||
status == VP8_STATUS_NOT_ENOUGH_DATA) {
// treating NOT_ENOUGH_DATA as SUSPENDED state
return VP8_STATUS_SUSPENDED;
}
return IDecError(idec, status);
}
if (!WebPCheckDecParams(io, params)) {
return IDecError(idec, VP8_STATUS_INVALID_PARAM);
}
if (mode != MODE_YUV) {
VP8YUVInit();
}
// allocate memory and prepare everything.
if (!VP8InitFrame(dec, io)) {
return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
}
if (io->setup && !io->setup(io)) {
return IDecError(idec, VP8_STATUS_USER_ABORT);
}
// disable filtering per user request (_after_ setup() is called)
if (io->bypass_filtering) dec->filter_type_ = 0;
if (!CopyParts0Data(idec)) {
return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
}
idec->state_ = STATE_DATA;
return VP8_STATUS_OK;
}
// Remaining partitions
static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
VP8BitReader* br;
VP8Decoder* const dec = idec->dec_;
VP8Io* const io = &idec->io_;
assert(dec->ready_);
br = &dec->br_;
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
if (dec->mb_x_ == 0) {
VP8MB* const left = dec->mb_info_ - 1;
left->nz_ = 0;
left->dc_nz_ = 0;
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
}
for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
MBContext context;
SaveContext(dec, token_br, &context);
if (!VP8DecodeMB(dec, token_br)) {
RestoreContext(&context, dec, token_br);
// We shouldn't fail when MAX_MB data was available
if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
return VP8_STATUS_SUSPENDED;
}
VP8ReconstructBlock(dec);
// Store data and save block's filtering params
VP8StoreBlock(dec);
// Release buffer only if there is only one partition
if (dec->num_parts_ == 1) {
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
assert(idec->mem_.start_ <= idec->mem_.end_);
}
}
if (!VP8FinishRow(dec, io)) {
return IDecError(idec, VP8_STATUS_USER_ABORT);
}
dec->mb_x_ = 0;
}
if (io->teardown) {
io->teardown(io);
}
dec->ready_ = 0;
idec->state_ = STATE_DONE;
return VP8_STATUS_OK;
}
// Main decoding loop
static VP8StatusCode IDecode(WebPIDecoder* idec) {
VP8StatusCode status = VP8_STATUS_SUSPENDED;
assert(idec->dec_);
if (idec->state_ == STATE_HEADER) {
status = DecodeHeader(idec);
}
if (idec->state_ == STATE_PARTS0) {
status = DecodePartition0(idec);
}
if (idec->state_ == STATE_DATA) {
return DecodeRemaining(idec);
}
return status;
}
//------------------------------------------------------------------------------
// Public functions
WebPIDecoder* WebPINew(WEBP_CSP_MODE mode) {
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(WebPIDecoder));
if (!idec) return NULL;
idec->dec_ = VP8New();
if (idec->dec_ == NULL) {
free(idec);
return NULL;
}
idec->state_ = STATE_HEADER;
idec->params_.mode = mode;
InitMemBuffer(&idec->mem_);
VP8InitIo(&idec->io_);
WebPInitCustomIo(&idec->io_);
return idec;
}
void WebPIDelete(WebPIDecoder* const idec) {
if (!idec) return;
VP8Delete(idec->dec_);
WebPClearDecParams(&idec->params_);
ClearMemBuffer(&idec->mem_);
free(idec);
}
//------------------------------------------------------------------------------
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
int output_buffer_size, int output_stride) {
WebPIDecoder* idec;
if (mode == MODE_YUV) return NULL;
idec = WebPINew(mode);
if (idec == NULL) return NULL;
idec->params_.output = output_buffer;
idec->params_.stride = output_stride;
idec->params_.output_size = output_buffer_size;
idec->params_.external_buffer = 1;
return idec;
}
WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride,
uint8_t* u, int u_size, int u_stride,
uint8_t* v, int v_size, int v_stride) {
WebPIDecoder* idec = WebPINew(MODE_YUV);
if (idec == NULL) return NULL;
idec->params_.output = luma;
idec->params_.stride = luma_stride;
idec->params_.output_size = luma_size;
idec->params_.u = u;
idec->params_.u_stride = u_stride;
idec->params_.output_u_size = u_size;
idec->params_.v = v;
idec->params_.v_stride = v_stride;
idec->params_.output_v_size = v_size;
idec->params_.external_buffer = 1;
return idec;
}
//------------------------------------------------------------------------------
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
assert(idec);
if (idec->dec_ == NULL) {
return VP8_STATUS_USER_ABORT;
}
if (idec->state_ == STATE_ERROR) {
return VP8_STATUS_BITSTREAM_ERROR;
}
if (idec->state_ == STATE_DONE) {
return VP8_STATUS_OK;
}
return VP8_STATUS_SUSPENDED;
}
VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data,
uint32_t data_size) {
if (idec == NULL || data == NULL) {
return VP8_STATUS_INVALID_PARAM;
}
const VP8StatusCode status = IDecCheckStatus(idec);
if (status != VP8_STATUS_SUSPENDED) {
return status;
}
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
return VP8_STATUS_INVALID_PARAM;
}
// Append data to memory buffer
if (!AppendToMemBuffer(idec, data, data_size)) {
return VP8_STATUS_OUT_OF_MEMORY;
}
return IDecode(idec);
}
VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data,
uint32_t data_size) {
if (idec == NULL || data == NULL) {
return VP8_STATUS_INVALID_PARAM;
}
const VP8StatusCode status = IDecCheckStatus(idec);
if (status != VP8_STATUS_SUSPENDED) {
return status;
}
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
return VP8_STATUS_INVALID_PARAM;
}
// Make the memory buffer point to the new buffer
if (!RemapMemBuffer(idec, data, data_size)) {
return VP8_STATUS_INVALID_PARAM;
}
return IDecode(idec);
}
//------------------------------------------------------------------------------
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int *last_y,
int* width, int* height, int* stride) {
if (!idec || !idec->dec_ || idec->params_.mode != MODE_RGB ||
idec->state_ <= STATE_PARTS0) {
return NULL;
}
if (last_y) *last_y = idec->params_.last_y;
if (width) *width = idec->w_;
if (height) *height = idec->h_;
if (stride) *stride = idec->params_.stride;
return idec->params_.output;
}
uint8_t* WebPIDecGetYUV(const WebPIDecoder* idec, int *last_y, uint8_t** u,
uint8_t** v, int* width, int* height, int *stride,
int* uv_stride) {
if (!idec || !idec->dec_ || idec->params_.mode != MODE_YUV ||
idec->state_ <= STATE_PARTS0) {
return NULL;
}
if (last_y) *last_y = idec->params_.last_y;
if (u) *u = idec->params_.u;
if (v) *v = idec->params_.v;
if (width) *width = idec->w_;
if (height) *height = idec->h_;
if (stride) *stride = idec->params_.stride;
if (uv_stride) *uv_stride = idec->params_.u_stride;
return idec->params_.output;
}

View File

@ -9,7 +9,6 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdio.h>
#include "vp8i.h" #include "vp8i.h"
#define USE_GENERIC_TREE #define USE_GENERIC_TREE

View File

@ -319,12 +319,7 @@ void WebPInitCustomIo(VP8Io* const io) {
int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
int* height, WebPDecParams* const params) { int* height, WebPDecParams* const params) {
int w, h, stride; int w, h;
int uv_size = 0;
int uv_stride = 0;
int size;
uint8_t* output;
WEBP_CSP_MODE mode = params->mode;
if (!WebPGetInfo(data, data_size, &w, &h)) { if (!WebPGetInfo(data, data_size, &w, &h)) {
return 0; return 0;
@ -332,6 +327,14 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
if (width) *width = w; if (width) *width = w;
if (height) *height = h; if (height) *height = h;
if (!params->external_buffer) {
int stride;
int uv_stride = 0;
int size;
int uv_size = 0;
uint8_t* output;
WEBP_CSP_MODE mode = params->mode;
// initialize output buffer, now that dimensions are known. // initialize output buffer, now that dimensions are known.
stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
: (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
@ -350,20 +353,23 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
params->output = output; params->output = output;
params->stride = stride; params->stride = stride;
params->output_size = size;
if (mode == MODE_YUV) { if (mode == MODE_YUV) {
params->u = output + size; params->u = output + size;
params->u_stride = uv_stride; params->u_stride = uv_stride;
params->output_u_size = uv_size;
params->v = output + size + uv_size; params->v = output + size + uv_size;
params->v_stride = uv_stride; params->v_stride = uv_stride;
params->output_v_size = uv_size;
}
} }
return 1; return 1;
} }
int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params) {
int output_size, int output_u_size, int output_v_size) {
int ok = 1; int ok = 1;
WEBP_CSP_MODE mode = params->mode; WEBP_CSP_MODE mode = params->mode;
ok &= (params->stride * io->height <= output_size); ok &= (params->stride * io->height <= params->output_size);
if (mode == MODE_RGB || mode == MODE_BGR) { if (mode == MODE_RGB || mode == MODE_BGR) {
ok &= (params->stride >= io->width * 3); ok &= (params->stride >= io->width * 3);
} else if (mode == MODE_RGBA || mode == MODE_BGRA) { } else if (mode == MODE_RGBA || mode == MODE_BGRA) {
@ -375,13 +381,16 @@ int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params,
ok &= (params->stride >= io->width); ok &= (params->stride >= io->width);
ok &= (params->u_stride >= (io->width + 1) / 2) && ok &= (params->u_stride >= (io->width + 1) / 2) &&
(params->v_stride >= (io->width + 1) / 2); (params->v_stride >= (io->width + 1) / 2);
ok &= (u_size <= output_u_size && v_size <= output_v_size); ok &= (u_size <= params->output_u_size &&
v_size <= params->output_v_size);
} }
return ok; return ok;
} }
void WebPClearDecParams(WebPDecParams* params) { void WebPClearDecParams(WebPDecParams* params) {
if (!params->external_buffer) {
free(params->output); free(params->output);
}
memset(params, 0, sizeof(*params)); memset(params, 0, sizeof(*params));
} }
@ -390,8 +399,7 @@ void WebPClearDecParams(WebPDecParams* params) {
static uint8_t* DecodeInto(WEBP_CSP_MODE mode, static uint8_t* DecodeInto(WEBP_CSP_MODE mode,
const uint8_t* data, uint32_t data_size, const uint8_t* data, uint32_t data_size,
WebPDecParams* params, int output_size, WebPDecParams* params) {
int output_u_size, int output_v_size) {
VP8Decoder* dec = VP8New(); VP8Decoder* dec = VP8New();
VP8Io io; VP8Io io;
int ok = 1; int ok = 1;
@ -414,8 +422,7 @@ static uint8_t* DecodeInto(WEBP_CSP_MODE mode,
} }
// check output buffers // check output buffers
ok = WebPCheckDecParams(&io, params, output_size, ok = WebPCheckDecParams(&io, params);
output_u_size, output_v_size);
if (!ok) { if (!ok) {
VP8Delete(dec); VP8Delete(dec);
return NULL; return NULL;
@ -441,7 +448,10 @@ uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size,
params.output = output; params.output = output;
params.stride = output_stride; params.stride = output_stride;
return DecodeInto(MODE_RGB, data, data_size, &params, output_size, 0, 0); params.output_size = output_size;
params.output_u_size = 0;
params.output_v_size = 0;
return DecodeInto(MODE_RGB, data, data_size, &params);
} }
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
@ -455,7 +465,10 @@ uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
params.output = output; params.output = output;
params.stride = output_stride; params.stride = output_stride;
return DecodeInto(MODE_RGBA, data, data_size, &params, output_size, 0, 0); params.output_size = output_size;
params.output_u_size = 0;
params.output_v_size = 0;
return DecodeInto(MODE_RGBA, data, data_size, &params);
} }
uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
@ -469,7 +482,10 @@ uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
params.output = output; params.output = output;
params.stride = output_stride; params.stride = output_stride;
return DecodeInto(MODE_BGR, data, data_size, &params, output_size, 0, 0); params.output_size = output_size;
params.output_u_size = 0;
params.output_v_size = 0;
return DecodeInto(MODE_BGR, data, data_size, &params);
} }
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
@ -483,7 +499,10 @@ uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
params.output = output; params.output = output;
params.stride = output_stride; params.stride = output_stride;
return DecodeInto(MODE_BGRA, data, data_size, &params, output_size, 0, 0); params.output_size = output_size;
params.output_u_size = 0;
params.output_v_size = 0;
return DecodeInto(MODE_BGRA, data, data_size, &params);
} }
uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
@ -498,12 +517,14 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
params.output = luma; params.output = luma;
params.stride = luma_stride; params.stride = luma_stride;
params.output_size = luma_size;
params.u = u; params.u = u;
params.u_stride = u_stride; params.u_stride = u_stride;
params.output_u_size = u_size;
params.v = v; params.v = v;
params.v_stride = v_stride; params.v_stride = v_stride;
return DecodeInto(MODE_YUV, data, data_size, &params, params.output_v_size = v_size;
luma_size, u_size, v_size); return DecodeInto(MODE_YUV, data, data_size, &params);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -511,8 +532,6 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
uint32_t data_size, int* width, int* height, uint32_t data_size, int* width, int* height,
WebPDecParams* params_out) { WebPDecParams* params_out) {
int size = 0;
int uv_size = 0;
uint8_t* output; uint8_t* output;
WebPDecParams params = { 0 }; WebPDecParams params = { 0 };
@ -521,9 +540,10 @@ static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
return NULL; return NULL;
} }
size = params.stride * (*height); params.output_size = params.stride * (*height);
uv_size = params.u_stride * ((*height + 1) / 2); params.output_u_size = params.output_v_size =
output = DecodeInto(mode, data, data_size, &params, size, uv_size, uv_size); params.u_stride * ((*height + 1) / 2);
output = DecodeInto(mode, data, data_size, &params);
if (!output) { if (!output) {
WebPClearDecParams(&params); WebPClearDecParams(&params);
} }

View File

@ -18,20 +18,20 @@ extern "C" {
#include "webp/decode_vp8.h" #include "webp/decode_vp8.h"
typedef enum { MODE_RGB = 0, MODE_RGBA = 1, // Decoding output parameters.
MODE_BGR = 2, MODE_BGRA = 3,
MODE_YUV = 4 } WEBP_CSP_MODE;
// Decoding output parameters.
typedef struct { typedef struct {
uint8_t* output; // rgb(a) or luma uint8_t* output; // rgb(a) or luma
uint8_t *u, *v; uint8_t *u, *v; // chroma u/v
uint8_t *top_y, *top_u, *top_v; uint8_t *top_y, *top_u, *top_v; // cache for the fancy upscaler
int stride; // rgb(a) stride or luma stride int stride; // rgb(a) stride or luma stride
int u_stride; int u_stride; // chroma-u stride
int v_stride; int v_stride; // chroma-v stride
WEBP_CSP_MODE mode; WEBP_CSP_MODE mode; // rgb(a) or yuv
int last_y; // coordinate of the line that was last output int last_y; // coordinate of the line that was last output
int output_size; // size of 'output' buffer
int output_u_size; // size of 'u' buffer
int output_v_size; // size of 'v' buffer
int external_buffer; // If true, the output buffers are externally owned
} WebPDecParams; } WebPDecParams;
// If a RIFF container is detected, validate it and skip over it. Returns // If a RIFF container is detected, validate it and skip over it. Returns
@ -50,8 +50,7 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
// Verifies various size configurations (e.g stride >= width, specified // Verifies various size configurations (e.g stride >= width, specified
// output size <= stride * height etc.). Returns 0 if checks fail. // output size <= stride * height etc.). Returns 0 if checks fail.
int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params);
int output_size, int output_u_size, int output_v_size);
// Deallocate memory allocated by WebPInitDecParams() and reset the // Deallocate memory allocated by WebPInitDecParams() and reset the
// WebPDecParams object. // WebPDecParams object.

View File

@ -92,6 +92,100 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Output colorspaces
typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
MODE_BGR = 2, MODE_BGRA = 3,
MODE_YUV = 4 } WEBP_CSP_MODE;
// Enumeration of the status codes
typedef enum {
VP8_STATUS_OK = 0,
VP8_STATUS_OUT_OF_MEMORY,
VP8_STATUS_INVALID_PARAM,
VP8_STATUS_BITSTREAM_ERROR,
VP8_STATUS_UNSUPPORTED_FEATURE,
VP8_STATUS_SUSPENDED,
VP8_STATUS_USER_ABORT,
VP8_STATUS_NOT_ENOUGH_DATA
} VP8StatusCode;
//-----------------------------------------------------------------------------
// Incremental decoding
//
// This API allows streamlined decoding of partial data.
// Picture can be incrementally decoded as data become available thanks to the
// WebPIDecoder object. This object can be left in a SUSPENDED state if the
// picture is only partially decoded, pending additional input.
// Code example:
//
// WebPIDecoder* const idec = WebPINew(mode);
// while (has_more_data) {
// // ... (get additional data)
// status = WebPIAppend(idec, new_data, new_data_size);
// if (status != VP8_STATUS_SUSPENDED ||
// break;
// }
//
// // The above call decodes the current available buffer.
// // Part of the image can now be refreshed by calling to
// // WebPIDecGetRGB()/WebPIDecGetYUV() etc.
// }
// WebPIDelete(idec);
typedef struct WebPIDecoder WebPIDecoder;
// Creates a WebPIDecoder object. Returns NULL in case of failure.
WebPIDecoder* WebPINew(WEBP_CSP_MODE mode);
// This function allocates and initializes an incremental-decoder object, which
// will output the r/g/b(/a) samples specified by 'mode' into a preallocated
// buffer 'output_buffer'. The size of this buffer is at least
// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
// is specified by 'output_stride'. Returns NULL if the allocation failed.
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
int output_buffer_size, int output_stride);
// This function allocates and initializes an incremental-decoder object, which
// will output the raw luma/chroma samples into a preallocated planes. The luma
// plane is specified by its pointer 'luma', its size 'luma_size' and its stride
// 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u',
// 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v', 'v_size'
// and 'v_size'.
// Returns NULL if the allocation failed.
WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride,
uint8_t* u, int u_size, int u_stride,
uint8_t* v, int v_size, int v_stride);
// Deletes the WebpBuffer object and associated memory. Must always be called
// if WebPINew, WebPINewRGB or WebPINewYUV succeeded.
void WebPIDelete(WebPIDecoder* const idec);
// Copies and decodes the next available data. Returns VP8_STATUS_OK when
// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
// data is expected. Returns error in other cases.
VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data,
uint32_t data_size);
// A variant of the above function to be used when data buffer contains
// partial data from the beginning. In this case data buffer is not copied
// to the internal memory.
VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data,
uint32_t data_size);
// Returns the RGB image decoded so far. Returns NULL if output params are not
// initialized yet. *last_y is the index of last decoded row in raster scan
// order. Some pointers (*last_y, *width etc.) can be NULL if corresponding
// information is not needed.
uint8_t* WebPIDecGetRGB(const WebPIDecoder* const idec, int *last_y,
int* width, int* height, int* stride);
// Same as above function to get YUV image. Returns pointer to the luma plane
// or NULL in case of error.
uint8_t* WebPIDecGetYUV(const WebPIDecoder* const idec, int* last_y,
uint8_t** u, uint8_t** v,
int* width, int* height, int* stride, int* uv_stride);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -12,7 +12,7 @@
#ifndef WEBP_WEBP_DECODE_VP8_H_ #ifndef WEBP_WEBP_DECODE_VP8_H_
#define WEBP_WEBP_DECODE_VP8_H_ #define WEBP_WEBP_DECODE_VP8_H_
#include "decode.h" #include "webp/decode.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -105,18 +105,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
// Returns false in case of error. // Returns false in case of error.
int VP8Decode(VP8Decoder* const dec, VP8Io* const io); int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
// Enumeration of the codes returned by VP8Status()
typedef enum {
VP8_STATUS_OK = 0,
VP8_STATUS_OUT_OF_MEMORY,
VP8_STATUS_INVALID_PARAM,
VP8_STATUS_BITSTREAM_ERROR,
VP8_STATUS_UNSUPPORTED_FEATURE,
VP8_STATUS_SUSPENDED,
VP8_STATUS_USER_ABORT,
VP8_STATUS_NOT_ENOUGH_DATA,
} VP8StatusCode;
// Return current status of the decoder: // Return current status of the decoder:
VP8StatusCode VP8Status(VP8Decoder* const dec); VP8StatusCode VP8Status(VP8Decoder* const dec);