summaryrefslogtreecommitdiffstats
path: root/third_party/libwebp/dec/idec.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebp/dec/idec.c')
-rw-r--r--third_party/libwebp/dec/idec.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/third_party/libwebp/dec/idec.c b/third_party/libwebp/dec/idec.c
new file mode 100644
index 0000000..d49ceb0
--- /dev/null
+++ b/third_party/libwebp/dec/idec.c
@@ -0,0 +1,577 @@
+// 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"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#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) {
+ VP8StatusCode status;
+ if (idec == NULL || data == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ 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) {
+ VP8StatusCode status;
+ if (idec == NULL || data == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ 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* const 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* const 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;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif