mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
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:
parent
1f28832834
commit
8bf76fe0c8
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
567
src/dec/idec.c
Normal 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;
|
||||||
|
}
|
@ -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
|
||||||
|
112
src/dec/webp.c
112
src/dec/webp.c
@ -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,38 +327,49 @@ 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;
|
||||||
|
|
||||||
// initialize output buffer, now that dimensions are known.
|
if (!params->external_buffer) {
|
||||||
stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
|
int stride;
|
||||||
: (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
|
int uv_stride = 0;
|
||||||
: w;
|
int size;
|
||||||
size = stride * h;
|
int uv_size = 0;
|
||||||
|
uint8_t* output;
|
||||||
|
WEBP_CSP_MODE mode = params->mode;
|
||||||
|
|
||||||
if (mode == MODE_YUV) {
|
// initialize output buffer, now that dimensions are known.
|
||||||
uv_stride = (w + 1) / 2;
|
stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
|
||||||
uv_size = uv_stride * ((h + 1) / 2);
|
: (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
|
||||||
}
|
: w;
|
||||||
|
size = stride * h;
|
||||||
|
|
||||||
output = (uint8_t*)malloc(size + 2 * uv_size);
|
if (mode == MODE_YUV) {
|
||||||
if (!output) {
|
uv_stride = (w + 1) / 2;
|
||||||
return 0;
|
uv_size = uv_stride * ((h + 1) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
params->output = output;
|
output = (uint8_t*)malloc(size + 2 * uv_size);
|
||||||
params->stride = stride;
|
if (!output) {
|
||||||
if (mode == MODE_YUV) {
|
return 0;
|
||||||
params->u = output + size;
|
}
|
||||||
params->u_stride = uv_stride;
|
|
||||||
params->v = output + size + uv_size;
|
params->output = output;
|
||||||
params->v_stride = uv_stride;
|
params->stride = stride;
|
||||||
|
params->output_size = size;
|
||||||
|
if (mode == MODE_YUV) {
|
||||||
|
params->u = output + size;
|
||||||
|
params->u_stride = uv_stride;
|
||||||
|
params->output_u_size = uv_size;
|
||||||
|
params->v = output + size + uv_size;
|
||||||
|
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) {
|
||||||
free(params->output);
|
if (!params->external_buffer) {
|
||||||
|
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, ¶ms, 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, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ¶ms, 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, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ¶ms, 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, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ¶ms, 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, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ¶ms,
|
params.output_v_size = v_size;
|
||||||
luma_size, u_size, v_size);
|
return DecodeInto(MODE_YUV, data, data_size, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -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, ¶ms, size, uv_size, uv_size);
|
params.u_stride * ((*height + 1) / 2);
|
||||||
|
output = DecodeInto(mode, data, data_size, ¶ms);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
WebPClearDecParams(¶ms);
|
WebPClearDecParams(¶ms);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user