diff options
author | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 07:16:54 +0000 |
---|---|---|
committer | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 07:16:54 +0000 |
commit | a6e603e0d0e60c9ac7470871106e5c984fa25694 (patch) | |
tree | b507ca7a1858ca0c37a40322f37a4a5d59137853 /third_party/libwebp | |
parent | fe245ae465ec8fa8919d31545bb5ddae0b8bd79c (diff) | |
download | chromium_src-a6e603e0d0e60c9ac7470871106e5c984fa25694.zip chromium_src-a6e603e0d0e60c9ac7470871106e5c984fa25694.tar.gz chromium_src-a6e603e0d0e60c9ac7470871106e5c984fa25694.tar.bz2 |
Update libwebp to libwebp-0.1.2.
Patch originally by Pascal Massimino:
http://codereview.chromium.org/6760004/
Review URL: http://codereview.chromium.org/6793035
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80421 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/libwebp')
23 files changed, 921 insertions, 168 deletions
diff --git a/third_party/libwebp/README.chromium b/third_party/libwebp/README.chromium index 349ef7b..5e6b403 100644 --- a/third_party/libwebp/README.chromium +++ b/third_party/libwebp/README.chromium @@ -2,15 +2,15 @@ Name: libwebpdecode Short Name: libwebp URL: http://code.google.com/speed/webp Version: unknown -Revision: I943a335b92b5ee6c2980c2ba9d4092f0b79f9a6b +Revision: 0.1.2 +Included In Release: Yes Description: -Here is a copy of libwebp-decode, from the repository: - git://review.webmproject.org/libwebp.git -This code was pulled from MASTER on March 13th 2011, with the most recent -Change-Id: I68194cb510760dd82a2ae83359154bf622502991 "fix bigger-by-1 array" +Here is a copy of libwebp-decode, from the upstream project: + http://code.google.com/p/webp/downloads/detail?name=libwebp-0.1.2.tar.gz The project files do not include from the distribution: examples/ -Changed the include path from webp/ to ../webp/
\ No newline at end of file +Local changes: + * Removed from types.h the risky: #ifdef ANSI / #define inline /#endif diff --git a/third_party/libwebp/dec/bits.h b/third_party/libwebp/dec/bits.h index e0572e4..82e4c3a 100644 --- a/third_party/libwebp/dec/bits.h +++ b/third_party/libwebp/dec/bits.h @@ -13,7 +13,7 @@ #define WEBP_DEC_BITS_H_ #include <assert.h> -#include "../webp/decode_vp8.h" +#include "webp/decode_vp8.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/third_party/libwebp/dec/dsp.c b/third_party/libwebp/dec/dsp.c index 2737cb4..efde49d 100644 --- a/third_party/libwebp/dec/dsp.c +++ b/third_party/libwebp/dec/dsp.c @@ -28,9 +28,11 @@ static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127] static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15] static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255] -static int tables_ok = 0; +// We declare this variable 'volatile' to prevent instruction reordering +// and make sure it's set to true _last_ (so as to be thread-safe) +static volatile int tables_ok = 0; -void VP8DspInitTables() { +void VP8DspInitTables(void) { if (!tables_ok) { int i; for (i = -255; i <= 255; ++i) { @@ -685,7 +687,7 @@ void (*VP8SimpleHFilter16i)(uint8_t*, int, int) = SimpleHFilter16i; //----------------------------------------------------------------------------- -void VP8DspInit() { +void VP8DspInit(void) { // later we'll plug some SSE2 variant here } 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 diff --git a/third_party/libwebp/dec/tree.c b/third_party/libwebp/dec/tree.c index 9c7074b..ed6caad 100644 --- a/third_party/libwebp/dec/tree.c +++ b/third_party/libwebp/dec/tree.c @@ -9,7 +9,6 @@ // // Author: Skal (pascal.massimino@gmail.com) -#include <stdio.h> #include "vp8i.h" #define USE_GENERIC_TREE diff --git a/third_party/libwebp/dec/vp8.c b/third_party/libwebp/dec/vp8.c index 43319b5..43a0c35 100644 --- a/third_party/libwebp/dec/vp8.c +++ b/third_party/libwebp/dec/vp8.c @@ -17,6 +17,12 @@ extern "C" { #endif //----------------------------------------------------------------------------- + +int WebPGetDecoderVersion(void) { + return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; +} + +//----------------------------------------------------------------------------- // VP8Decoder static void SetOk(VP8Decoder* const dec) { @@ -33,7 +39,7 @@ int VP8InitIoInternal(VP8Io* const io, int version) { return 1; } -VP8Decoder* VP8New() { +VP8Decoder* VP8New(void) { VP8Decoder* dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder)); if (dec) { SetOk(dec); @@ -199,6 +205,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { VP8FrameHeader* frm_hdr; VP8PictureHeader* pic_hdr; VP8BitReader* br; + VP8StatusCode status; if (dec == NULL) { return 0; @@ -316,9 +323,9 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse filter header"); } - if (ParsePartitions(dec, buf, buf_size) != VP8_STATUS_OK) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "cannot parse partitions"); + status = ParsePartitions(dec, buf, buf_size); + if (status != VP8_STATUS_OK) { + return VP8SetError(dec, status, "cannot parse partitions"); } // quantizer change @@ -386,7 +393,8 @@ static const uint8_t kZigzag[16] = { typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting -// Returns 1 if there's non-zero coeffs, 0 otherwise +// Returns the position of the last non-zero coeff plus one +// (and 0 if there's no coeff at all) static int GetCoeffs(VP8BitReader* const br, ProbaArray prob, int ctx, const uint16_t dq[2], int n, int16_t* out) { const uint8_t* p = prob[kBands[n]][ctx]; @@ -433,14 +441,13 @@ static int GetCoeffs(VP8BitReader* const br, ProbaArray prob, j = kZigzag[n - 1]; out[j] = VP8GetSigned(br, v) * dq[j > 0]; if (n == 16 || !VP8GetBit(br, p[0])) { // EOB - return 1; + return n; } } if (n == 16) { - return 1; + return 16; } } - return 0; } // Alias-safe way of converting 4bytes to 32bits. @@ -483,8 +490,8 @@ static void ParseResiduals(VP8Decoder* const dec, int16_t dc[16] = { 0 }; const int ctx = mb->dc_nz_ + left_mb->dc_nz_; mb->dc_nz_ = left_mb->dc_nz_ = - GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1], - ctx, q->y2_mat_, 0, dc); + (GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1], + ctx, q->y2_mat_, 0, dc) > 0); first = 1; ac_prob = (ProbaArray)dec->proba_.coeffs_[0]; VP8TransformWHT(dc, dst); @@ -499,10 +506,11 @@ static void ParseResiduals(VP8Decoder* const dec, int l = lnz.i8[y]; for (x = 0; x < 4; ++x) { const int ctx = l + tnz.i8[x]; - l = GetCoeffs(token_br, ac_prob, ctx, - q->y1_mat_, first, dst); + const int nz = GetCoeffs(token_br, ac_prob, ctx, + q->y1_mat_, first, dst); + tnz.i8[x] = l = (nz > 0); nz_dc.i8[x] = (dst[0] != 0); - nz_ac.i8[x] = tnz.i8[x] = l; + nz_ac.i8[x] = (nz > 1); dst += 16; } lnz.i8[y] = l; @@ -519,10 +527,12 @@ static void ParseResiduals(VP8Decoder* const dec, int l = lnz.i8[ch + y]; for (x = 0; x < 2; ++x) { const int ctx = l + tnz.i8[ch + x]; - l = GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2], + const int nz = + GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2], ctx, q->uv_mat_, 0, dst); + tnz.i8[ch + x] = l = (nz > 0); nz_dc.i8[y * 2 + x] = (dst[0] != 0); - nz_ac.i8[y * 2 + x] = tnz.i8[ch + x] = l; + nz_ac.i8[y * 2 + x] = (nz > 1); dst += 16; } lnz.i8[ch + y] = l; diff --git a/third_party/libwebp/dec/vp8i.h b/third_party/libwebp/dec/vp8i.h index 9cfee04..b2ad9a3 100644 --- a/third_party/libwebp/dec/vp8i.h +++ b/third_party/libwebp/dec/vp8i.h @@ -22,6 +22,11 @@ extern "C" { //----------------------------------------------------------------------------- // Various defines and enums +// version numbers +#define DEC_MAJ_VERSION 0 +#define DEC_MIN_VERSION 1 +#define DEC_REV_VERSION 2 + #define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames // intra prediction modes @@ -284,8 +289,8 @@ extern VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES]; extern VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES]; extern VP8PredFunc VP8PredLuma4[NUM_BMODES]; -void VP8DspInit(); // must be called before anything using the above -void VP8DspInitTables(); // needs to be called no matter what. +void VP8DspInit(void); // must be called before anything using the above +void VP8DspInitTables(void); // needs to be called no matter what. // simple filter (only for luma) typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh); diff --git a/third_party/libwebp/dec/webp.c b/third_party/libwebp/dec/webp.c index 823eafe641..3bf6f55 100644 --- a/third_party/libwebp/dec/webp.c +++ b/third_party/libwebp/dec/webp.c @@ -186,6 +186,7 @@ static int CustomPut(const VP8Io* io) { return 0; } + p->last_y = io->mb_y + io->mb_h; // a priori guess if (p->mode == MODE_YUV) { uint8_t* const y_dst = p->output + io->mb_y * p->stride; uint8_t* const u_dst = p->u + (io->mb_y >> 1) * p->u_stride; @@ -237,6 +238,9 @@ static int CustomPut(const VP8Io* io) { memcpy(p->top_y, cur_y, w * sizeof(*p->top_y)); memcpy(p->top_u, cur_u, uv_w * sizeof(*p->top_u)); memcpy(p->top_v, cur_v, uv_w * sizeof(*p->top_v)); + // The fancy upscaler leaves a row unfinished behind + // (except for the very last row) + p->last_y -= 1; } else { // Process the very last row of even-sized picture if (!(y_end & 1)) { @@ -315,12 +319,7 @@ void WebPInitCustomIo(VP8Io* const io) { int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, int* height, WebPDecParams* const params) { - int w, h, stride; - int uv_size = 0; - int uv_stride = 0; - int size; - uint8_t* output; - WEBP_CSP_MODE mode = params->mode; + int w, h; if (!WebPGetInfo(data, data_size, &w, &h)) { return 0; @@ -328,38 +327,49 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, if (width) *width = w; if (height) *height = h; - // initialize output buffer, now that dimensions are known. - stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w - : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w - : w; - size = stride * h; - - if (mode == MODE_YUV) { - uv_stride = (w + 1) / 2; - uv_size = uv_stride * ((h + 1) / 2); - } + 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. + stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w + : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w + : w; + size = stride * h; + + if (mode == MODE_YUV) { + uv_stride = (w + 1) / 2; + uv_size = uv_stride * ((h + 1) / 2); + } - output = (uint8_t*)malloc(size + 2 * uv_size); - if (!output) { - return 0; - } + output = (uint8_t*)malloc(size + 2 * uv_size); + if (!output) { + return 0; + } - params->output = output; - params->stride = stride; - if (mode == MODE_YUV) { - params->u = output + size; - params->u_stride = uv_stride; - params->v = output + size + uv_size; - params->v_stride = uv_stride; + params->output = output; + 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; } -int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, - int output_size, int output_u_size, int output_v_size) { +int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params) { int ok = 1; 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) { ok &= (params->stride >= io->width * 3); } else if (mode == MODE_RGBA || mode == MODE_BGRA) { @@ -371,13 +381,16 @@ int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, ok &= (params->stride >= io->width); ok &= (params->u_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; } void WebPClearDecParams(WebPDecParams* params) { - free(params->output); + if (!params->external_buffer) { + free(params->output); + } memset(params, 0, sizeof(*params)); } @@ -386,8 +399,7 @@ void WebPClearDecParams(WebPDecParams* params) { static uint8_t* DecodeInto(WEBP_CSP_MODE mode, const uint8_t* data, uint32_t data_size, - WebPDecParams* params, int output_size, - int output_u_size, int output_v_size) { + WebPDecParams* params) { VP8Decoder* dec = VP8New(); VP8Io io; int ok = 1; @@ -410,8 +422,7 @@ static uint8_t* DecodeInto(WEBP_CSP_MODE mode, } // check output buffers - ok = WebPCheckDecParams(&io, params, output_size, - output_u_size, output_v_size); + ok = WebPCheckDecParams(&io, params); if (!ok) { VP8Delete(dec); return NULL; @@ -437,7 +448,10 @@ uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, params.output = output; 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, @@ -451,7 +465,10 @@ uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, params.output = output; 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, @@ -465,7 +482,10 @@ uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, params.output = output; 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, @@ -479,7 +499,10 @@ uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, params.output = output; 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, @@ -494,12 +517,14 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, params.output = luma; params.stride = luma_stride; + params.output_size = luma_size; params.u = u; params.u_stride = u_stride; + params.output_u_size = u_size; params.v = v; params.v_stride = v_stride; - return DecodeInto(MODE_YUV, data, data_size, ¶ms, - luma_size, u_size, v_size); + params.output_v_size = v_size; + return DecodeInto(MODE_YUV, data, data_size, ¶ms); } //----------------------------------------------------------------------------- @@ -507,22 +532,26 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, uint32_t data_size, int* width, int* height, WebPDecParams* params_out) { - int size = 0; - int uv_size = 0; - WebPDecParams params = { 0 }; + uint8_t* output; + WebPDecParams params; + memset(¶ms, 0, sizeof(params)); params.mode = mode; if (!WebPInitDecParams(data, data_size, width, height, ¶ms)) { return NULL; } - size = params.stride * (*height); - uv_size = params.u_stride * ((*height + 1) / 2); - if (!DecodeInto(mode, data, data_size, ¶ms, size, uv_size, uv_size)) { + params.output_size = params.stride * (*height); + params.output_u_size = params.output_v_size = + params.u_stride * ((*height + 1) / 2); + output = DecodeInto(mode, data, data_size, ¶ms); + if (!output) { WebPClearDecParams(¶ms); } - if (params_out) *params_out = params; - return params.output; + if (params_out) { + *params_out = params; + } + return output; } uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, diff --git a/third_party/libwebp/dec/webpi.h b/third_party/libwebp/dec/webpi.h index 7bbe4cf..cf5bc0e 100644 --- a/third_party/libwebp/dec/webpi.h +++ b/third_party/libwebp/dec/webpi.h @@ -16,21 +16,22 @@ extern "C" { #endif -#include "../webp/decode_vp8.h" +#include "webp/decode_vp8.h" -typedef enum { MODE_RGB = 0, MODE_RGBA = 1, - MODE_BGR = 2, MODE_BGRA = 3, - MODE_YUV = 4 } WEBP_CSP_MODE; - - // Decoding output parameters. +// Decoding output parameters. typedef struct { uint8_t* output; // rgb(a) or luma - uint8_t *u, *v; - uint8_t *top_y, *top_u, *top_v; + uint8_t *u, *v; // chroma u/v + uint8_t *top_y, *top_u, *top_v; // cache for the fancy upscaler int stride; // rgb(a) stride or luma stride - int u_stride; - int v_stride; - WEBP_CSP_MODE mode; + int u_stride; // chroma-u stride + int v_stride; // chroma-v stride + WEBP_CSP_MODE mode; // rgb(a) or yuv + 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; // If a RIFF container is detected, validate it and skip over it. Returns @@ -49,8 +50,7 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, // Verifies various size configurations (e.g stride >= width, specified // output size <= stride * height etc.). Returns 0 if checks fail. -int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, - int output_size, int output_u_size, int output_v_size); +int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params); // Deallocate memory allocated by WebPInitDecParams() and reset the // WebPDecParams object. diff --git a/third_party/libwebp/dec/yuv.c b/third_party/libwebp/dec/yuv.c index 30f59c1..ac448ee 100644 --- a/third_party/libwebp/dec/yuv.c +++ b/third_party/libwebp/dec/yuv.c @@ -23,7 +23,7 @@ uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; static int done = 0; -void VP8YUVInit() { +void VP8YUVInit(void) { int i; if (done) { return; diff --git a/third_party/libwebp/dec/yuv.h b/third_party/libwebp/dec/yuv.h index db31453..50e63f9 100644 --- a/third_party/libwebp/dec/yuv.h +++ b/third_party/libwebp/dec/yuv.h @@ -12,7 +12,7 @@ #ifndef WEBP_DEC_YUV_H_ #define WEBP_DEC_YUV_H_ -#include "../webp/decode_vp8.h" +#include "webp/decode_vp8.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -57,7 +57,7 @@ inline static void VP8YuvToBgra(int y, int u, int v, uint8_t* const bgra) { } // Must be called before everything, to initialize the tables. -void VP8YUVInit(); +void VP8YUVInit(void); #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" diff --git a/third_party/libwebp/enc/config.c b/third_party/libwebp/enc/config.c index e79ba08..86ef5ce2 100644 --- a/third_party/libwebp/enc/config.c +++ b/third_party/libwebp/enc/config.c @@ -10,7 +10,7 @@ // Author: Skal (pascal.massimino@gmail.com) #include <assert.h> -#include "../webp/encode.h" +#include "webp/encode.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/third_party/libwebp/enc/dsp.c b/third_party/libwebp/enc/dsp.c index 1365a23..45f977c 100644 --- a/third_party/libwebp/enc/dsp.c +++ b/third_party/libwebp/enc/dsp.c @@ -21,9 +21,11 @@ extern "C" { static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255] -static int tables_ok = 0; +// We declare this variable 'volatile' to prevent instruction reordering +// and make sure it's set to true _last_ (so as to be thread-safe) +static volatile int tables_ok = 0; -static void InitTables() { +static void InitTables(void) { if (!tables_ok) { int i; for (i = -255; i <= 255 + 255; ++i) { @@ -79,7 +81,7 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst) { } } -void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) { +static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) { int i; int tmp[16]; for (i = 0; i < 4; ++i, src += BPS, ref += BPS) { @@ -582,6 +584,38 @@ VP8WMetric VP8TDisto4x4 = Disto4x4; VP8WMetric VP8TDisto16x16 = Disto16x16; //----------------------------------------------------------------------------- +// Quantization +// + +// Simple quantization +static int QuantizeBlock(int16_t in[16], int16_t out[16], + int n, const VP8Matrix* const mtx) { + int last = -1; + for (; n < 16; ++n) { + const int j = VP8Zigzag[n]; + const int sign = (in[j] < 0); + int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; + if (coeff > 2047) coeff = 2047; + if (coeff > mtx->zthresh_[j]) { + const int Q = mtx->q_[j]; + const int iQ = mtx->iq_[j]; + const int B = mtx->bias_[j]; + out[n] = QUANTDIV(coeff, iQ, B); + if (sign) out[n] = -out[n]; + in[j] = out[n] * Q; + if (out[n]) last = n; + } else { + out[n] = 0; + in[j] = 0; + } + } + return (last >= 0); +} + +// default C implementation +VP8QuantizeBlock VP8EncQuantizeBlock = QuantizeBlock; + +//----------------------------------------------------------------------------- // Block copy static inline void Copy(const uint8_t* src, uint8_t* dst, int size) { @@ -604,9 +638,8 @@ VP8BlockCopy VP8Copy16x16 = Copy16x16; //----------------------------------------------------------------------------- -void VP8EncDspInit() { +void VP8EncDspInit(void) { InitTables(); - // later we'll plug some SSE2 variant here } #if defined(__cplusplus) || defined(c_plusplus) diff --git a/third_party/libwebp/enc/filter.c b/third_party/libwebp/enc/filter.c index 5a243b1..a0a42b0 100644 --- a/third_party/libwebp/enc/filter.c +++ b/third_party/libwebp/enc/filter.c @@ -25,7 +25,7 @@ static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255] static int tables_ok = 0; -static void InitTables() { +static void InitTables(void) { if (!tables_ok) { int i; for (i = -255; i <= 255; ++i) { diff --git a/third_party/libwebp/enc/iterator.c b/third_party/libwebp/enc/iterator.c index 7cd9f06..991644d 100644 --- a/third_party/libwebp/enc/iterator.c +++ b/third_party/libwebp/enc/iterator.c @@ -270,7 +270,7 @@ int VP8IteratorNext(VP8EncIterator* const it, //----------------------------------------------------------------------------- // Helper function to set mode properties -void VP8SetIntra16Mode(const VP8EncIterator* it, int mode) { +void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { int y; uint8_t* preds = it->preds_; for (y = 0; y < 4; ++y) { diff --git a/third_party/libwebp/enc/picture.c b/third_party/libwebp/enc/picture.c index 5b8d98b..6c12ea4 100644 --- a/third_party/libwebp/enc/picture.c +++ b/third_party/libwebp/enc/picture.c @@ -38,7 +38,7 @@ int WebPPictureAlloc(WebPPicture* const picture) { picture->y_stride = width; picture->uv_stride = uv_width; WebPPictureFree(picture); // erase previous buffer - picture->y = (uint8_t*)malloc(total_size); + picture->y = (uint8_t*)malloc((size_t)total_size); if (picture->y == NULL) return 0; picture->u = picture->y + y_size; picture->v = picture->u + uv_size; @@ -163,7 +163,7 @@ enum { YUV_FRAC = 16 }; static inline int clip_uv(int v) { v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2); - return ((v & ~0xff) == 0) ? v : (v < 0) ? 0u : 255u; + return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255; } static inline int rgb_to_y(int r, int g, int b) { diff --git a/third_party/libwebp/enc/quant.c b/third_party/libwebp/enc/quant.c index f3b99a0..e439919 100644 --- a/third_party/libwebp/enc/quant.c +++ b/third_party/libwebp/enc/quant.c @@ -39,7 +39,7 @@ static inline int clip(int v, int m, int M) { return v < m ? m : v > M ? M : v; } -static const uint8_t kZigzag[16] = { +const uint8_t VP8Zigzag[16] = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; @@ -100,8 +100,6 @@ static const uint16_t kAcTable2[128] = { 385, 393, 401, 409, 416, 424, 432, 440 }; -#define QFIX 17 -#define BIAS(b) ((b) << (QFIX - 8)) static const uint16_t kCoeffThresh[16] = { 0, 10, 20, 30, 10, 20, 30, 30, @@ -145,7 +143,7 @@ static int ExpandMatrix(VP8Matrix* const m, int type) { m->q_[i] = m->q_[1]; } for (i = 0; i < 16; ++i) { - const int j = kZigzag[i]; + const int j = VP8Zigzag[i]; const int bias = kBiasMatrices[type][j]; m->iq_[j] = (1 << QFIX) / m->q_[j]; m->bias_[j] = BIAS(bias); @@ -387,38 +385,7 @@ static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { } //----------------------------------------------------------------------------- -// Performs simple and trellis-optimized quantization. - -// Fun fact: this is the _only_ line where we're actually being lossy and -// discarding bits. -static int DIV(int n, int iQ, int B) { - return (n * iQ + B) >> QFIX; -} - -// Simple quantization -static int QuantizeBlock(int16_t in[16], int16_t out[16], - int n, const VP8Matrix* const mtx) { - int last = -1; - for (; n < 16; ++n) { - const int j = kZigzag[n]; - const int sign = (in[j] < 0); - int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; - if (coeff > 2047) coeff = 2047; - if (coeff > mtx->zthresh_[j]) { - const int Q = mtx->q_[j]; - const int iQ = mtx->iq_[j]; - const int B = mtx->bias_[j]; - out[n] = DIV(coeff, iQ, B); - if (sign) out[n] = -out[n]; - in[j] = out[n] * Q; - if (out[n]) last = n; - } else { - out[n] = 0; - in[j] = 0; - } - } - return (last >= 0); -} +// Performs trellis-optimized quantization. // Trellis @@ -473,7 +440,7 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, // compute maximal distortion. max_error = 0; for (n = first; n < 16; ++n) { - const int j = kZigzag[n]; + const int j = VP8Zigzag[n]; const int err = in[j] * in[j]; max_error += kWeightTrellis[j] * err; if (err > thresh) last = n; @@ -497,7 +464,7 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, // traverse trellis. for (n = first; n <= last; ++n) { - const int j = kZigzag[n]; + const int j = VP8Zigzag[n]; const int Q = mtx->q_[j]; const int iQ = mtx->iq_[j]; const int B = BIAS(0x00); // neutral bias @@ -508,7 +475,7 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, int level0; if (coeff0 > 2047) coeff0 = 2047; - level0 = DIV(coeff0, iQ, B); + level0 = QUANTDIV(coeff0, iQ, B); // test all alternate level values around level0. for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { Node* const cur = &NODE(n, m); @@ -593,7 +560,7 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, for (; n >= first; --n) { const Node* const node = &NODE(n, best_node); - const int j = kZigzag[n]; + const int j = VP8Zigzag[n]; out[n] = node->sign ? -node->level : node->level; nz |= (node->level != 0); in[j] = out[n] * mtx->q_[j]; @@ -625,7 +592,7 @@ static int ReconstructIntra16(VP8EncIterator* const it, VP8FTransform(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]); } VP8FTransformWHT(tmp[0], dc_tmp); - nz |= QuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &dqm->y2_) << 24; + nz |= VP8EncQuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &dqm->y2_) << 24; if (DO_TRELLIS_I16 && it->do_trellis_) { int x, y; @@ -642,7 +609,7 @@ static int ReconstructIntra16(VP8EncIterator* const it, } } else { for (n = 0; n < 16; ++n) { - nz |= QuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n; + nz |= VP8EncQuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n; } } @@ -673,7 +640,7 @@ static int ReconstructIntra4(VP8EncIterator* const it, nz = TrellisQuantizeBlock(it, tmp, levels, ctx, 3, &dqm->y1_, dqm->lambda_trellis_i4_); } else { - nz = QuantizeBlock(tmp, levels, 0, &dqm->y1_); + nz = VP8EncQuantizeBlock(tmp, levels, 0, &dqm->y1_); } VP8ITransform(ref, tmp, yuv_out); return nz; @@ -708,7 +675,7 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd, } } else { for (n = 0; n < 8; ++n) { - nz |= QuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n; + nz |= VP8EncQuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n; } } diff --git a/third_party/libwebp/enc/vp8enci.h b/third_party/libwebp/enc/vp8enci.h index b488922..b19450d 100644 --- a/third_party/libwebp/enc/vp8enci.h +++ b/third_party/libwebp/enc/vp8enci.h @@ -13,7 +13,7 @@ #define WEBP_ENC_VP8ENCI_H_ #include "string.h" // for memcpy() -#include "../webp/encode.h" +#include "webp/encode.h" #include "bit_writer.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -23,6 +23,11 @@ extern "C" { //----------------------------------------------------------------------------- // Various defines and enums +// version numbers +#define ENC_MAJ_VERSION 0 +#define ENC_MIN_VERSION 1 +#define ENC_REV_VERSION 2 + // intra prediction modes enum { B_DC_PRED = 0, // 4x4 modes B_TM_PRED = 1, @@ -144,6 +149,15 @@ extern const int VP8I4ModeOffsets[NUM_BMODES]; typedef int64_t score_t; // type used for scores, rate, distortion #define MAX_COST ((score_t)0x7fffffffffffffLL) +#define QFIX 17 +#define BIAS(b) ((b) << (QFIX - 8)) +// Fun fact: this is the _only_ line where we're actually being lossy and +// discarding bits. +static inline int QUANTDIV(int n, int iQ, int B) { + return (n * iQ + B) >> QFIX; +} +extern const uint8_t VP8Zigzag[16]; + //----------------------------------------------------------------------------- // Headers @@ -428,8 +442,20 @@ typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst); extern VP8BlockCopy VP8Copy4x4; extern VP8BlockCopy VP8Copy8x8; extern VP8BlockCopy VP8Copy16x16; - -void VP8EncDspInit(); // must be called before using anything from the above. +// Quantization +typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16], + int n, const VP8Matrix* const mtx); +extern VP8QuantizeBlock VP8EncQuantizeBlock; + +typedef enum { + kSSE2, + kSSE3 +} CPUFeature; +// returns true if the CPU supports the feature. +typedef int (*VP8CPUInfo)(CPUFeature feature); +extern VP8CPUInfo CPUInfo; + +void VP8EncDspInit(void); // must be called before using any of the above // in filter.c extern void VP8InitFilter(VP8EncIterator* const it); diff --git a/third_party/libwebp/enc/webpenc.c b/third_party/libwebp/enc/webpenc.c index 5ca445e..59221d7 100644 --- a/third_party/libwebp/enc/webpenc.c +++ b/third_party/libwebp/enc/webpenc.c @@ -28,11 +28,21 @@ extern "C" { #define MAX_DIMENSION 16384 // maximum width/height allowed by the spec //----------------------------------------------------------------------------- + +int WebPGetEncoderVersion(void) { + return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION; +} + +//----------------------------------------------------------------------------- // WebPPicture //----------------------------------------------------------------------------- static int DummyWriter(const uint8_t* data, size_t data_size, const WebPPicture* const picture) { + // The following are to prevent 'unused variable' error message. + (void)data; + (void)data_size; + (void)picture; return 1; } diff --git a/third_party/libwebp/libwebp.gyp b/third_party/libwebp/libwebp.gyp index 7a50332..a43f79d 100644 --- a/third_party/libwebp/libwebp.gyp +++ b/third_party/libwebp/libwebp.gyp @@ -12,6 +12,7 @@ { 'target_name': 'libwebp_enc', 'type': '<(library)', + 'include_dirs': ['.'], 'sources': [ 'enc/analysis.c', 'enc/bit_writer.c', @@ -31,10 +32,12 @@ { 'target_name': 'libwebp_dec', 'type': '<(library)', + 'include_dirs': ['.'], 'sources': [ 'dec/bits.c', 'dec/dsp.c', 'dec/frame.c', + 'dec/idec.c', 'dec/quant.c', 'dec/tree.c', 'dec/vp8.c', diff --git a/third_party/libwebp/webp/decode.h b/third_party/libwebp/webp/decode.h index 78b9016..6c63d54 100644 --- a/third_party/libwebp/webp/decode.h +++ b/third_party/libwebp/webp/decode.h @@ -12,12 +12,16 @@ #ifndef WEBP_WEBP_DECODE_H_ #define WEBP_WEBP_DECODE_H_ -#include "../webp/types.h" +#include "webp/types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif +// Return the decoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +int WebPGetDecoderVersion(void); + // Retrieve basic header information: width, height. // This function will also validate the header and return 0 in // case of formatting error. @@ -92,6 +96,102 @@ 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. +// Note that the value of the 'data' pointer can change between calls to +// WebPIUpdate, for instance when the data buffer is resized to fit larger data. +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) } // extern "C" #endif diff --git a/third_party/libwebp/webp/decode_vp8.h b/third_party/libwebp/webp/decode_vp8.h index 08c6f69..153a4c5 100644 --- a/third_party/libwebp/webp/decode_vp8.h +++ b/third_party/libwebp/webp/decode_vp8.h @@ -12,7 +12,7 @@ #ifndef WEBP_WEBP_DECODE_VP8_H_ #define WEBP_WEBP_DECODE_VP8_H_ -#include "decode.h" +#include "webp/decode.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -83,13 +83,13 @@ struct VP8Io { }; // Internal, version-checked, entry point -extern int VP8InitIoInternal(VP8Io* const, int); +int VP8InitIoInternal(VP8Io* const, int); // Main decoding object. This is an opaque structure. typedef struct VP8Decoder VP8Decoder; // Create a new decoder object. -VP8Decoder* VP8New(); +VP8Decoder* VP8New(void); // Must be called to make sure 'io' is initialized properly. // Returns false in case of version mismatch. Upon such failure, no other @@ -105,18 +105,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); // Returns false in case of error. 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: VP8StatusCode VP8Status(VP8Decoder* const dec); diff --git a/third_party/libwebp/webp/encode.h b/third_party/libwebp/webp/encode.h index 8fb8ff4..e0cc5dc 100644 --- a/third_party/libwebp/webp/encode.h +++ b/third_party/libwebp/webp/encode.h @@ -14,7 +14,7 @@ #include <stdlib.h> -#include "../webp/types.h" +#include "webp/types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -22,6 +22,10 @@ extern "C" { #define WEBP_ENCODER_ABI_VERSION 0x0001 +// Return the encoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +int WebPGetEncoderVersion(void); + //----------------------------------------------------------------------------- // One-stop-shop call! No questions asked: |