diff options
Diffstat (limited to 'src/3rdparty/libwebp/src/dec')
-rw-r--r-- | src/3rdparty/libwebp/src/dec/alpha.c | 2 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/buffer.c | 11 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/common.h | 54 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/frame.c | 358 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/idec.c | 45 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/io.c | 146 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/tree.c | 9 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/vp8.c | 47 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/vp8i.h | 51 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/vp8l.c | 622 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/vp8li.h | 6 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/webp.c | 33 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/dec/webpi.h | 7 |
13 files changed, 802 insertions, 589 deletions
diff --git a/src/3rdparty/libwebp/src/dec/alpha.c b/src/3rdparty/libwebp/src/dec/alpha.c index f23ba7d..52216fc 100644 --- a/src/3rdparty/libwebp/src/dec/alpha.c +++ b/src/3rdparty/libwebp/src/dec/alpha.c @@ -15,6 +15,7 @@ #include "./alphai.h" #include "./vp8i.h" #include "./vp8li.h" +#include "../dsp/dsp.h" #include "../utils/quant_levels_dec.h" #include "../utils/utils.h" #include "../webp/format_constants.h" @@ -78,6 +79,7 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION); ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output); } + VP8FiltersInit(); return ok; } diff --git a/src/3rdparty/libwebp/src/dec/buffer.c b/src/3rdparty/libwebp/src/dec/buffer.c index 2129312..9ed2b3f 100644 --- a/src/3rdparty/libwebp/src/dec/buffer.c +++ b/src/3rdparty/libwebp/src/dec/buffer.c @@ -189,11 +189,14 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h, h = ch; } if (options->use_scaling) { - if (options->scaled_width <= 0 || options->scaled_height <= 0) { + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (!WebPRescalerGetScaledDimensions( + w, h, &scaled_width, &scaled_height)) { return VP8_STATUS_INVALID_PARAM; } - w = options->scaled_width; - h = options->scaled_height; + w = scaled_width; + h = scaled_height; } } out->width = w; @@ -203,12 +206,10 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h, status = AllocateBuffer(out); if (status != VP8_STATUS_OK) return status; -#if WEBP_DECODER_ABI_VERSION > 0x0203 // Use the stride trick if vertical flip is needed. if (options != NULL && options->flip) { status = WebPFlipBuffer(out); } -#endif return status; } diff --git a/src/3rdparty/libwebp/src/dec/common.h b/src/3rdparty/libwebp/src/dec/common.h new file mode 100644 index 0000000..6961e22 --- /dev/null +++ b/src/3rdparty/libwebp/src/dec/common.h @@ -0,0 +1,54 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Definitions and macros common to encoding and decoding +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_DEC_COMMON_H_ +#define WEBP_DEC_COMMON_H_ + +// intra prediction modes +enum { B_DC_PRED = 0, // 4x4 modes + B_TM_PRED = 1, + B_VE_PRED = 2, + B_HE_PRED = 3, + B_RD_PRED = 4, + B_VR_PRED = 5, + B_LD_PRED = 6, + B_VL_PRED = 7, + B_HD_PRED = 8, + B_HU_PRED = 9, + NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10 + + // Luma16 or UV modes + DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, + H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, + B_PRED = NUM_BMODES, // refined I4x4 mode + NUM_PRED_MODES = 4, + + // special modes + B_DC_PRED_NOTOP = 4, + B_DC_PRED_NOLEFT = 5, + B_DC_PRED_NOTOPLEFT = 6, + NUM_B_DC_MODES = 7 }; + +enum { MB_FEATURE_TREE_PROBS = 3, + NUM_MB_SEGMENTS = 4, + NUM_REF_LF_DELTAS = 4, + NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT + MAX_NUM_PARTITIONS = 8, + // Probabilities + NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC + NUM_BANDS = 8, + NUM_CTX = 3, + NUM_PROBAS = 11 + }; + +#endif // WEBP_DEC_COMMON_H_ diff --git a/src/3rdparty/libwebp/src/dec/frame.c b/src/3rdparty/libwebp/src/dec/frame.c index 2359acc..b882133 100644 --- a/src/3rdparty/libwebp/src/dec/frame.c +++ b/src/3rdparty/libwebp/src/dec/frame.c @@ -15,10 +15,180 @@ #include "./vp8i.h" #include "../utils/utils.h" -#define ALIGN_MASK (32 - 1) +//------------------------------------------------------------------------------ +// Main reconstruction function. + +static const int kScan[16] = { + 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, + 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, + 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, + 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS +}; + +static int CheckMode(int mb_x, int mb_y, int mode) { + if (mode == B_DC_PRED) { + if (mb_x == 0) { + return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; + } else { + return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED; + } + } + return mode; +} + +static void Copy32b(uint8_t* const dst, const uint8_t* const src) { + memcpy(dst, src, 4); +} + +static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src, + uint8_t* const dst) { + switch (bits >> 30) { + case 3: + VP8Transform(src, dst, 0); + break; + case 2: + VP8TransformAC3(src, dst); + break; + case 1: + VP8TransformDC(src, dst); + break; + default: + break; + } +} + +static void DoUVTransform(uint32_t bits, const int16_t* const src, + uint8_t* const dst) { + if (bits & 0xff) { // any non-zero coeff at all? + if (bits & 0xaa) { // any non-zero AC coefficient? + VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V + } else { + VP8TransformDCUV(src, dst); + } + } +} static void ReconstructRow(const VP8Decoder* const dec, - const VP8ThreadContext* ctx); // TODO(skal): remove + const VP8ThreadContext* ctx) { + int j; + int mb_x; + const int mb_y = ctx->mb_y_; + const int cache_id = ctx->id_; + uint8_t* const y_dst = dec->yuv_b_ + Y_OFF; + uint8_t* const u_dst = dec->yuv_b_ + U_OFF; + uint8_t* const v_dst = dec->yuv_b_ + V_OFF; + + // Initialize left-most block. + for (j = 0; j < 16; ++j) { + y_dst[j * BPS - 1] = 129; + } + for (j = 0; j < 8; ++j) { + u_dst[j * BPS - 1] = 129; + v_dst[j * BPS - 1] = 129; + } + + // Init top-left sample on left column too. + if (mb_y > 0) { + y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129; + } else { + // we only need to do this init once at block (0,0). + // Afterward, it remains valid for the whole topmost row. + memset(y_dst - BPS - 1, 127, 16 + 4 + 1); + memset(u_dst - BPS - 1, 127, 8 + 1); + memset(v_dst - BPS - 1, 127, 8 + 1); + } + + // Reconstruct one row. + for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { + const VP8MBData* const block = ctx->mb_data_ + mb_x; + + // Rotate in the left samples from previously decoded block. We move four + // pixels at a time for alignment reason, and because of in-loop filter. + if (mb_x > 0) { + for (j = -1; j < 16; ++j) { + Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]); + } + for (j = -1; j < 8; ++j) { + Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]); + Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]); + } + } + { + // bring top samples into the cache + VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x; + const int16_t* const coeffs = block->coeffs_; + uint32_t bits = block->non_zero_y_; + int n; + + if (mb_y > 0) { + memcpy(y_dst - BPS, top_yuv[0].y, 16); + memcpy(u_dst - BPS, top_yuv[0].u, 8); + memcpy(v_dst - BPS, top_yuv[0].v, 8); + } + + // predict and add residuals + if (block->is_i4x4_) { // 4x4 + uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); + + if (mb_y > 0) { + if (mb_x >= dec->mb_w_ - 1) { // on rightmost border + memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); + } else { + memcpy(top_right, top_yuv[1].y, sizeof(*top_right)); + } + } + // replicate the top-right pixels below + top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; + + // predict and add residuals for all 4x4 blocks in turn. + for (n = 0; n < 16; ++n, bits <<= 2) { + uint8_t* const dst = y_dst + kScan[n]; + VP8PredLuma4[block->imodes_[n]](dst); + DoTransform(bits, coeffs + n * 16, dst); + } + } else { // 16x16 + const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]); + VP8PredLuma16[pred_func](y_dst); + if (bits != 0) { + for (n = 0; n < 16; ++n, bits <<= 2) { + DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]); + } + } + } + { + // Chroma + const uint32_t bits_uv = block->non_zero_uv_; + const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); + VP8PredChroma8[pred_func](u_dst); + VP8PredChroma8[pred_func](v_dst); + DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst); + DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst); + } + + // stash away top samples for next block + if (mb_y < dec->mb_h_ - 1) { + memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16); + memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8); + memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8); + } + } + // Transfer reconstructed samples from yuv_b_ cache to final destination. + { + const int y_offset = cache_id * 16 * dec->cache_y_stride_; + const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; + uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset; + uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset; + uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset; + for (j = 0; j < 16; ++j) { + memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16); + } + for (j = 0; j < 8; ++j) { + memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8); + memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8); + } + } + } +} //------------------------------------------------------------------------------ // Filtering @@ -112,7 +282,6 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) { VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; int level = base_level; if (hdr->use_lf_delta_) { - // TODO(skal): only CURRENT is handled for now. level += hdr->ref_lf_delta_[0]; if (i4x4) { level += hdr->mode_lf_delta_[0]; @@ -177,7 +346,6 @@ void VP8InitDithering(const WebPDecoderOptions* const options, dec->dither_ = 1; } } -#if WEBP_DECODER_ABI_VERSION > 0x0204 // potentially allow alpha dithering dec->alpha_dithering_ = options->alpha_dithering_strength; if (dec->alpha_dithering_ > 100) { @@ -185,7 +353,6 @@ void VP8InitDithering(const WebPDecoderOptions* const options, } else if (dec->alpha_dithering_ < 0) { dec->alpha_dithering_ = 0; } -#endif } } @@ -554,7 +721,7 @@ static int AllocateMemory(VP8Decoder* const dec) { const uint64_t needed = (uint64_t)intra_pred_mode_size + top_size + mb_info_size + f_info_size + yuv_size + mb_data_size - + cache_size + alpha_size + ALIGN_MASK; + + cache_size + alpha_size + WEBP_ALIGN_CST; uint8_t* mem; if (needed != (size_t)needed) return 0; // check for overflow @@ -591,8 +758,8 @@ static int AllocateMemory(VP8Decoder* const dec) { dec->thread_ctx_.f_info_ += mb_w; } - mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK); - assert((yuv_size & ALIGN_MASK) == 0); + mem = (uint8_t*)WEBP_ALIGN(mem); + assert((yuv_size & WEBP_ALIGN_CST) == 0); dec->yuv_b_ = (uint8_t*)mem; mem += yuv_size; @@ -644,7 +811,7 @@ static void InitIo(VP8Decoder* const dec, VP8Io* io) { io->a = NULL; } -int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { +int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) { if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_. if (!AllocateMemory(dec)) return 0; InitIo(dec, io); @@ -653,176 +820,3 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { } //------------------------------------------------------------------------------ -// Main reconstruction function. - -static const int kScan[16] = { - 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, - 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, - 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, - 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS -}; - -static int CheckMode(int mb_x, int mb_y, int mode) { - if (mode == B_DC_PRED) { - if (mb_x == 0) { - return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; - } else { - return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED; - } - } - return mode; -} - -static void Copy32b(uint8_t* dst, uint8_t* src) { - memcpy(dst, src, 4); -} - -static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src, - uint8_t* const dst) { - switch (bits >> 30) { - case 3: - VP8Transform(src, dst, 0); - break; - case 2: - VP8TransformAC3(src, dst); - break; - case 1: - VP8TransformDC(src, dst); - break; - default: - break; - } -} - -static void DoUVTransform(uint32_t bits, const int16_t* const src, - uint8_t* const dst) { - if (bits & 0xff) { // any non-zero coeff at all? - if (bits & 0xaa) { // any non-zero AC coefficient? - VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V - } else { - VP8TransformDCUV(src, dst); - } - } -} - -static void ReconstructRow(const VP8Decoder* const dec, - const VP8ThreadContext* ctx) { - int j; - int mb_x; - const int mb_y = ctx->mb_y_; - const int cache_id = ctx->id_; - uint8_t* const y_dst = dec->yuv_b_ + Y_OFF; - uint8_t* const u_dst = dec->yuv_b_ + U_OFF; - uint8_t* const v_dst = dec->yuv_b_ + V_OFF; - for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { - const VP8MBData* const block = ctx->mb_data_ + mb_x; - - // Rotate in the left samples from previously decoded block. We move four - // pixels at a time for alignment reason, and because of in-loop filter. - if (mb_x > 0) { - for (j = -1; j < 16; ++j) { - Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]); - } - for (j = -1; j < 8; ++j) { - Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]); - Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]); - } - } else { - for (j = 0; j < 16; ++j) { - y_dst[j * BPS - 1] = 129; - } - for (j = 0; j < 8; ++j) { - u_dst[j * BPS - 1] = 129; - v_dst[j * BPS - 1] = 129; - } - // Init top-left sample on left column too - if (mb_y > 0) { - y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129; - } - } - { - // bring top samples into the cache - VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x; - const int16_t* const coeffs = block->coeffs_; - uint32_t bits = block->non_zero_y_; - int n; - - if (mb_y > 0) { - memcpy(y_dst - BPS, top_yuv[0].y, 16); - memcpy(u_dst - BPS, top_yuv[0].u, 8); - memcpy(v_dst - BPS, top_yuv[0].v, 8); - } else if (mb_x == 0) { - // we only need to do this init once at block (0,0). - // Afterward, it remains valid for the whole topmost row. - memset(y_dst - BPS - 1, 127, 16 + 4 + 1); - memset(u_dst - BPS - 1, 127, 8 + 1); - memset(v_dst - BPS - 1, 127, 8 + 1); - } - - // predict and add residuals - if (block->is_i4x4_) { // 4x4 - uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); - - if (mb_y > 0) { - if (mb_x >= dec->mb_w_ - 1) { // on rightmost border - memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); - } else { - memcpy(top_right, top_yuv[1].y, sizeof(*top_right)); - } - } - // replicate the top-right pixels below - top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; - - // predict and add residuals for all 4x4 blocks in turn. - for (n = 0; n < 16; ++n, bits <<= 2) { - uint8_t* const dst = y_dst + kScan[n]; - VP8PredLuma4[block->imodes_[n]](dst); - DoTransform(bits, coeffs + n * 16, dst); - } - } else { // 16x16 - const int pred_func = CheckMode(mb_x, mb_y, - block->imodes_[0]); - VP8PredLuma16[pred_func](y_dst); - if (bits != 0) { - for (n = 0; n < 16; ++n, bits <<= 2) { - DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]); - } - } - } - { - // Chroma - const uint32_t bits_uv = block->non_zero_uv_; - const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); - VP8PredChroma8[pred_func](u_dst); - VP8PredChroma8[pred_func](v_dst); - DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst); - DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst); - } - - // stash away top samples for next block - if (mb_y < dec->mb_h_ - 1) { - memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16); - memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8); - memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8); - } - } - // Transfer reconstructed samples from yuv_b_ cache to final destination. - { - const int y_offset = cache_id * 16 * dec->cache_y_stride_; - const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; - uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset; - uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset; - uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset; - for (j = 0; j < 16; ++j) { - memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16); - } - for (j = 0; j < 8; ++j) { - memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8); - memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8); - } - } - } -} - -//------------------------------------------------------------------------------ - diff --git a/src/3rdparty/libwebp/src/dec/idec.c b/src/3rdparty/libwebp/src/dec/idec.c index e003851..e0cf0c9 100644 --- a/src/3rdparty/libwebp/src/dec/idec.c +++ b/src/3rdparty/libwebp/src/dec/idec.c @@ -130,8 +130,12 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { VP8RemapBitReader(&dec->br_, offset); } } - assert(last_part >= 0); - dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_; + { + const uint8_t* const last_start = dec->parts_[last_part].buf_; + assert(last_part >= 0); + VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start, + mem->buf_ + mem->end_ - last_start); + } if (NeedCompressedAlpha(idec)) { ALPHDecoder* const alph_dec = dec->alph_dec_; dec->alpha_data_ += offset; @@ -240,17 +244,15 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { // To be called last. static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { -#if WEBP_DECODER_ABI_VERSION > 0x0203 const WebPDecoderOptions* const options = idec->params_.options; WebPDecBuffer* const output = idec->params_.output; idec->state_ = STATE_DONE; if (options != NULL && options->flip) { return WebPFlipBuffer(output); + } else { + return VP8_STATUS_OK; } -#endif - idec->state_ = STATE_DONE; - return VP8_STATUS_OK; } //------------------------------------------------------------------------------ @@ -377,8 +379,7 @@ static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { } memcpy(part0_buf, br->buf_, part_size); mem->part0_buf_ = part0_buf; - br->buf_ = part0_buf; - br->buf_end_ = part0_buf + part_size; + VP8BitReaderSetBuffer(br, part0_buf, part_size); } else { // Else: just keep pointers to the partition #0's data in dec_->br_. } @@ -506,9 +507,15 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) { // Wait until there's enough data for decoding header. if (curr_size < (idec->chunk_size_ >> 3)) { - return VP8_STATUS_SUSPENDED; + dec->status_ = VP8_STATUS_SUSPENDED; + return ErrorStatusLossless(idec, dec->status_); } + if (!VP8LDecodeHeader(dec, io)) { + if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && + curr_size < idec->chunk_size_) { + dec->status_ = VP8_STATUS_SUSPENDED; + } return ErrorStatusLossless(idec, dec->status_); } // Allocate/verify output buffer now. @@ -527,23 +534,15 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { const size_t curr_size = MemDataSize(&idec->mem_); assert(idec->is_lossless_); - // At present Lossless decoder can't decode image incrementally. So wait till - // all the image data is aggregated before image can be decoded. - if (curr_size < idec->chunk_size_) { - return VP8_STATUS_SUSPENDED; - } + // Switch to incremental decoding if we don't have all the bytes available. + dec->incremental_ = (curr_size < idec->chunk_size_); if (!VP8LDecodeImage(dec)) { - // The decoding is called after all the data-bytes are aggregated. Change - // the error to VP8_BITSTREAM_ERROR in case lossless decoder fails to decode - // all the pixels (VP8_STATUS_SUSPENDED). - if (dec->status_ == VP8_STATUS_SUSPENDED) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - } return ErrorStatusLossless(idec, dec->status_); } - - return FinishDecoding(idec); + assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED); + return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_ + : FinishDecoding(idec); } // Main decoding loop @@ -793,7 +792,6 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, const WebPDecBuffer* const src = GetOutputBuffer(idec); if (left != NULL) *left = 0; if (top != NULL) *top = 0; - // TODO(skal): later include handling of rotations. if (src) { if (width != NULL) *width = src->width; if (height != NULL) *height = idec->params_.last_y; @@ -859,4 +857,3 @@ int WebPISetIOHooks(WebPIDecoder* const idec, return 1; } - diff --git a/src/3rdparty/libwebp/src/dec/io.c b/src/3rdparty/libwebp/src/dec/io.c index b2e72f0..13e469a 100644 --- a/src/3rdparty/libwebp/src/dec/io.c +++ b/src/3rdparty/libwebp/src/dec/io.c @@ -56,32 +56,6 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { } //------------------------------------------------------------------------------ -// YUV444 -> RGB conversion - -#if 0 // TODO(skal): this is for future rescaling. -static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) { - WebPDecBuffer* output = p->output; - const WebPRGBABuffer* const buf = &output->u.RGBA; - uint8_t* dst = buf->rgba + io->mb_y * buf->stride; - const uint8_t* y_src = io->y; - const uint8_t* u_src = io->u; - const uint8_t* v_src = io->v; - const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace]; - const int mb_w = io->mb_w; - const int last = io->mb_h; - int j; - for (j = 0; j < last; ++j) { - convert(y_src, u_src, v_src, dst, mb_w); - y_src += io->y_stride; - u_src += io->uv_stride; - v_src += io->uv_stride; - dst += buf->stride; - } - return io->mb_h; -} -#endif - -//------------------------------------------------------------------------------ // Fancy upsampling #ifdef FANCY_UPSAMPLING @@ -145,14 +119,16 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { //------------------------------------------------------------------------------ -static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { +static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { const uint8_t* alpha = io->a; const WebPYUVABuffer* const buf = &p->output->u.YUVA; const int mb_w = io->mb_w; const int mb_h = io->mb_h; uint8_t* dst = buf->a + io->mb_y * buf->a_stride; int j; - + (void)expected_num_lines_out; + assert(expected_num_lines_out == mb_h); if (alpha != NULL) { for (j = 0; j < mb_h; ++j) { memcpy(dst, alpha, mb_w * sizeof(*dst)); @@ -195,7 +171,8 @@ static int GetAlphaSourceRow(const VP8Io* const io, return start_y; } -static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { +static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { const uint8_t* alpha = io->a; if (alpha != NULL) { const int mb_w = io->mb_w; @@ -206,21 +183,13 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { int num_rows; const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; - uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); - uint32_t alpha_mask = 0xff; - int i, j; - - for (j = 0; j < num_rows; ++j) { - for (i = 0; i < mb_w; ++i) { - const uint32_t alpha_value = alpha[i]; - dst[4 * i] = alpha_value; - alpha_mask &= alpha_value; - } - alpha += io->width; - dst += buf->stride; - } - // alpha_mask is < 0xff if there's non-trivial alpha to premultiply with. - if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) { + uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3); + const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w, + num_rows, dst, buf->stride); + (void)expected_num_lines_out; + assert(expected_num_lines_out == num_rows); + // has_alpha is true if there's non-trivial alpha to premultiply with. + if (has_alpha && WebPIsPremultipliedMode(colorspace)) { WebPApplyAlphaMultiply(base_rgba, alpha_first, mb_w, num_rows, buf->stride); } @@ -228,7 +197,8 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { return 0; } -static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) { +static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { const uint8_t* alpha = io->a; if (alpha != NULL) { const int mb_w = io->mb_w; @@ -244,7 +214,6 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) { #endif uint32_t alpha_mask = 0x0f; int i, j; - for (j = 0; j < num_rows; ++j) { for (i = 0; i < mb_w; ++i) { // Fill in the alpha value (converted to 4 bits). @@ -255,6 +224,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) { alpha += io->width; alpha_dst += buf->stride; } + (void)expected_num_lines_out; + assert(expected_num_lines_out == num_rows); if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) { WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride); } @@ -296,12 +267,15 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { return num_lines_out; } -static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { +static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { if (io->a != NULL) { const WebPYUVABuffer* const buf = &p->output->u.YUVA; uint8_t* dst_y = buf->y + p->last_y * buf->y_stride; const uint8_t* src_a = buf->a + p->last_y * buf->a_stride; const int num_lines_out = Rescale(io->a, io->width, io->mb_h, &p->scaler_a); + (void)expected_num_lines_out; + assert(expected_num_lines_out == num_lines_out); if (num_lines_out > 0) { // unmultiply the Y WebPMultRows(dst_y, buf->y_stride, src_a, buf->a_stride, p->scaler_a.dst_width, num_lines_out, 1); @@ -361,13 +335,13 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) { const WebPYUV444Converter convert = WebPYUV444Converters[p->output->colorspace]; const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride; + uint8_t* dst = buf->rgba + y_pos * buf->stride; int num_lines_out = 0; // For RGB rescaling, because of the YUV420, current scan position // U/V can be +1/-1 line from the Y one. Hence the double test. while (WebPRescalerHasPendingOutput(&p->scaler_y) && WebPRescalerHasPendingOutput(&p->scaler_u)) { - assert(p->last_y + y_pos + num_lines_out < p->output->height); + assert(y_pos + num_lines_out < p->output->height); assert(p->scaler_u.y_accum == p->scaler_v.y_accum); WebPRescalerExportRow(&p->scaler_y); WebPRescalerExportRow(&p->scaler_u); @@ -389,55 +363,54 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { const int y_lines_in = WebPRescalerImport(&p->scaler_y, mb_h - j, io->y + j * io->y_stride, io->y_stride); - const int u_lines_in = - WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j, - io->u + uv_j * io->uv_stride, io->uv_stride); - const int v_lines_in = - WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j, - io->v + uv_j * io->uv_stride, io->uv_stride); - (void)v_lines_in; // remove a gcc warning - assert(u_lines_in == v_lines_in); j += y_lines_in; - uv_j += u_lines_in; - num_lines_out += ExportRGB(p, num_lines_out); + if (WebPRescaleNeededLines(&p->scaler_u, uv_mb_h - uv_j)) { + const int u_lines_in = + WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j, + io->u + uv_j * io->uv_stride, io->uv_stride); + const int v_lines_in = + WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j, + io->v + uv_j * io->uv_stride, io->uv_stride); + (void)v_lines_in; // remove a gcc warning + assert(u_lines_in == v_lines_in); + uv_j += u_lines_in; + } + num_lines_out += ExportRGB(p, p->last_y + num_lines_out); } return num_lines_out; } -static int ExportAlpha(WebPDecParams* const p, int y_pos) { +static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) { const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; + uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int alpha_first = (colorspace == MODE_ARGB || colorspace == MODE_Argb); uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); int num_lines_out = 0; const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); - uint32_t alpha_mask = 0xff; + uint32_t non_opaque = 0; const int width = p->scaler_a.dst_width; - while (WebPRescalerHasPendingOutput(&p->scaler_a)) { - int i; - assert(p->last_y + y_pos + num_lines_out < p->output->height); + while (WebPRescalerHasPendingOutput(&p->scaler_a) && + num_lines_out < max_lines_out) { + assert(y_pos + num_lines_out < p->output->height); WebPRescalerExportRow(&p->scaler_a); - for (i = 0; i < width; ++i) { - const uint32_t alpha_value = p->scaler_a.dst[i]; - dst[4 * i] = alpha_value; - alpha_mask &= alpha_value; - } + non_opaque |= WebPDispatchAlpha(p->scaler_a.dst, 0, width, 1, dst, 0); dst += buf->stride; ++num_lines_out; } - if (is_premult_alpha && alpha_mask != 0xff) { + if (is_premult_alpha && non_opaque) { WebPApplyAlphaMultiply(base_rgba, alpha_first, width, num_lines_out, buf->stride); } return num_lines_out; } -static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) { +static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos, + int max_lines_out) { const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; + uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; #ifdef WEBP_SWAP_16BIT_CSP uint8_t* alpha_dst = base_rgba; #else @@ -449,9 +422,10 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) { const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); uint32_t alpha_mask = 0x0f; - while (WebPRescalerHasPendingOutput(&p->scaler_a)) { + while (WebPRescalerHasPendingOutput(&p->scaler_a) && + num_lines_out < max_lines_out) { int i; - assert(p->last_y + y_pos + num_lines_out < p->output->height); + assert(y_pos + num_lines_out < p->output->height); WebPRescalerExportRow(&p->scaler_a); for (i = 0; i < width; ++i) { // Fill in the alpha value (converted to 4 bits). @@ -468,15 +442,17 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) { return num_lines_out; } -static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { +static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p, + int expected_num_out_lines) { if (io->a != NULL) { WebPRescaler* const scaler = &p->scaler_a; - int j = 0; - int pos = 0; - while (j < io->mb_h) { - j += WebPRescalerImport(scaler, io->mb_h - j, - io->a + j * io->width, io->width); - pos += p->emit_alpha_row(p, pos); + int lines_left = expected_num_out_lines; + const int y_end = p->last_y + lines_left; + while (lines_left > 0) { + const int row_offset = scaler->src_y - io->mb_y; + WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y, + io->a + row_offset * io->width, io->width); + lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left); } } return 0; @@ -516,6 +492,7 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { tmp + 2 * out_width, out_width, out_height, 0, 1, work + 2 * work_size); p->emit = EmitRescaledRGB; + WebPInitYUV444Converters(); if (has_alpha) { WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, @@ -559,6 +536,7 @@ static int CustomSetup(VP8Io* io) { } } else { if (is_rgb) { + WebPInitSamplers(); p->emit = EmitSampledRGB; // default if (io->fancy_upsampling) { #ifdef FANCY_UPSAMPLING @@ -573,8 +551,6 @@ static int CustomSetup(VP8Io* io) { p->emit = EmitFancyRGB; WebPInitUpsamplers(); #endif - } else { - WebPInitSamplers(); } } else { p->emit = EmitYUV; @@ -611,7 +587,7 @@ static int CustomPut(const VP8Io* io) { } num_lines_out = p->emit(io, p); if (p->emit_alpha != NULL) { - p->emit_alpha(io, p); + p->emit_alpha(io, p, num_lines_out); } p->last_y += num_lines_out; return 1; diff --git a/src/3rdparty/libwebp/src/dec/tree.c b/src/3rdparty/libwebp/src/dec/tree.c index 31208d9..c2007ea 100644 --- a/src/3rdparty/libwebp/src/dec/tree.c +++ b/src/3rdparty/libwebp/src/dec/tree.c @@ -494,6 +494,12 @@ static const uint8_t }; // Paragraph 9.9 + +static const int kBands[16 + 1] = { + 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 0 // extra entry as sentinel +}; + void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { VP8Proba* const proba = &dec->proba_; int t, b, c, p; @@ -507,6 +513,9 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { } } } + for (b = 0; b < 16 + 1; ++b) { + proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]]; + } } dec->use_skip_proba_ = VP8Get(br); if (dec->use_skip_proba_) { diff --git a/src/3rdparty/libwebp/src/dec/vp8.c b/src/3rdparty/libwebp/src/dec/vp8.c index 89d478a..d89eb1c 100644 --- a/src/3rdparty/libwebp/src/dec/vp8.c +++ b/src/3rdparty/libwebp/src/dec/vp8.c @@ -75,10 +75,7 @@ void VP8Delete(VP8Decoder* const dec) { int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, const char* const msg) { - // TODO This check would be unnecessary if alpha decompression was separated - // from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to - // something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression - // failure. + // The oldest error reported takes precedence over the new one. if (dec->status_ == VP8_STATUS_OK) { dec->status_ = error; dec->error_msg_ = msg; @@ -193,25 +190,27 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec, const uint8_t* sz = buf; const uint8_t* buf_end = buf + size; const uint8_t* part_start; - int last_part; - int p; + size_t size_left = size; + size_t last_part; + size_t p; dec->num_parts_ = 1 << VP8GetValue(br, 2); last_part = dec->num_parts_ - 1; - part_start = buf + last_part * 3; - if (buf_end < part_start) { + if (size < 3 * last_part) { // we can't even read the sizes with sz[]! That's a failure. return VP8_STATUS_NOT_ENOUGH_DATA; } + part_start = buf + last_part * 3; + size_left -= last_part * 3; for (p = 0; p < last_part; ++p) { - const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); - const uint8_t* part_end = part_start + psize; - if (part_end > buf_end) part_end = buf_end; - VP8InitBitReader(dec->parts_ + p, part_start, part_end); - part_start = part_end; + size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); + if (psize > size_left) psize = size_left; + VP8InitBitReader(dec->parts_ + p, part_start, psize); + part_start += psize; + size_left -= psize; sz += 3; } - VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end); + VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); return (part_start < buf_end) ? VP8_STATUS_OK : VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data } @@ -328,7 +327,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { } br = &dec->br_; - VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); + VP8InitBitReader(br, buf, frm_hdr->partition_length_); buf += frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_; @@ -371,11 +370,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { //------------------------------------------------------------------------------ // Residual decoding (Paragraph 13.2 / 13.3) -static const int kBands[16 + 1] = { - 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, - 0 // extra entry as sentinel -}; - static const uint8_t kCat3[] = { 173, 148, 140, 0 }; static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 }; static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 }; @@ -419,20 +413,19 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { } // Returns the position of the last non-zero coeff plus one -static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob, +static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob[], int ctx, const quant_t dq, int n, int16_t* out) { - // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'. - const uint8_t* p = prob[n].probas_[ctx]; + const uint8_t* p = prob[n]->probas_[ctx]; for (; n < 16; ++n) { if (!VP8GetBit(br, p[0])) { return n; // previous coeff was last non-zero coeff } while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs - p = prob[kBands[++n]].probas_[0]; + p = prob[++n]->probas_[0]; if (n == 16) return 16; } { // non zero coeff - const VP8ProbaArray* const p_ctx = &prob[kBands[n + 1]].probas_[0]; + const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; int v; if (!VP8GetBit(br, p[2])) { v = 1; @@ -455,8 +448,8 @@ static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { static int ParseResiduals(VP8Decoder* const dec, VP8MB* const mb, VP8BitReader* const token_br) { - VP8BandProbas (* const bands)[NUM_BANDS] = dec->proba_.bands_; - const VP8BandProbas* ac_proba; + const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_; + const VP8BandProbas* const * ac_proba; VP8MBData* const block = dec->mb_data_ + dec->mb_x_; const VP8QuantMatrix* const q = &dec->dqm_[block->segment_]; int16_t* dst = block->coeffs_; diff --git a/src/3rdparty/libwebp/src/dec/vp8i.h b/src/3rdparty/libwebp/src/dec/vp8i.h index 0e6c8f5..0104f25 100644 --- a/src/3rdparty/libwebp/src/dec/vp8i.h +++ b/src/3rdparty/libwebp/src/dec/vp8i.h @@ -15,6 +15,7 @@ #define WEBP_DEC_VP8I_H_ #include <string.h> // for memcpy() +#include "./common.h" #include "./vp8li.h" #include "../utils/bit_reader.h" #include "../utils/random.h" @@ -30,46 +31,10 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 0 -#define DEC_MIN_VERSION 4 -#define DEC_REV_VERSION 4 - -// intra prediction modes -enum { B_DC_PRED = 0, // 4x4 modes - B_TM_PRED, - B_VE_PRED, - B_HE_PRED, - B_RD_PRED, - B_VR_PRED, - B_LD_PRED, - B_VL_PRED, - B_HD_PRED, - B_HU_PRED, - NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10 - - // Luma16 or UV modes - DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, - H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, - B_PRED = NUM_BMODES, // refined I4x4 mode - - // special modes - B_DC_PRED_NOTOP = 4, - B_DC_PRED_NOLEFT = 5, - B_DC_PRED_NOTOPLEFT = 6, - NUM_B_DC_MODES = 7 }; - -enum { MB_FEATURE_TREE_PROBS = 3, - NUM_MB_SEGMENTS = 4, - NUM_REF_LF_DELTAS = 4, - NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT - MAX_NUM_PARTITIONS = 8, - // Probabilities - NUM_TYPES = 4, - NUM_BANDS = 8, - NUM_CTX = 3, - NUM_PROBAS = 11, - NUM_MV_PROBAS = 19 }; - -// YUV-cache parameters. +#define DEC_MIN_VERSION 5 +#define DEC_REV_VERSION 0 + +// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), // and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned, // in order to be SIMD-friendly. We also need to store the top, left and @@ -91,8 +56,6 @@ enum { MB_FEATURE_TREE_PROBS = 3, // 'y' = y-samples 'u' = u-samples 'v' = u-samples // '|' = left sample, '-' = top sample, '+' = top-left sample // 't' = extra top-right sample for 4x4 modes -// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size. -#define BPS 32 // this is the common stride used by yuv[] #define YUV_SIZE (BPS * 17 + BPS * 9) #define Y_SIZE (BPS * 17) #define Y_OFF (BPS * 1 + 8) @@ -130,7 +93,6 @@ typedef struct { int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments } VP8SegmentHeader; - // probas associated to one of the contexts typedef uint8_t VP8ProbaArray[NUM_PROBAS]; @@ -143,6 +105,7 @@ typedef struct { uint8_t segments_[MB_FEATURE_TREE_PROBS]; // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4 VP8BandProbas bands_[NUM_TYPES][NUM_BANDS]; + const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1]; } VP8Proba; // Filter parameters @@ -317,7 +280,7 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec); void VP8ParseQuant(VP8Decoder* const dec); // in frame.c -int VP8InitFrame(VP8Decoder* const dec, VP8Io* io); +int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); // Call io->setup() and finish setting up scan parameters. // After this call returns, one must always call VP8ExitCritical() with the // same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK diff --git a/src/3rdparty/libwebp/src/dec/vp8l.c b/src/3rdparty/libwebp/src/dec/vp8l.c index 2fa5f40..a76ad6a 100644 --- a/src/3rdparty/libwebp/src/dec/vp8l.c +++ b/src/3rdparty/libwebp/src/dec/vp8l.c @@ -19,6 +19,7 @@ #include "../dsp/dsp.h" #include "../dsp/lossless.h" #include "../dsp/yuv.h" +#include "../utils/endian_inl.h" #include "../utils/huffman.h" #include "../utils/utils.h" @@ -50,6 +51,9 @@ static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = { NUM_DISTANCE_CODES }; +static const uint8_t kLiteralMap[HUFFMAN_CODES_PER_META_CODE] = { + 0, 1, 1, 1, 0 +}; #define NUM_CODE_LENGTH_CODES 19 static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = { @@ -72,6 +76,30 @@ static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = { 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70 }; +// Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha +// and distance alphabets are constant (256 for red, blue and alpha, 40 for +// distance) and lookup table sizes for them in worst case are 630 and 410 +// respectively. Size of green alphabet depends on color cache size and is equal +// to 256 (green component values) + 24 (length prefix values) +// + color_cache_size (between 0 and 2048). +// All values computed for 8-bit first level lookup with Mark Adler's tool: +// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c +#define FIXED_TABLE_SIZE (630 * 3 + 410) +static const int kTableSize[12] = { + FIXED_TABLE_SIZE + 654, + FIXED_TABLE_SIZE + 656, + FIXED_TABLE_SIZE + 658, + FIXED_TABLE_SIZE + 662, + FIXED_TABLE_SIZE + 670, + FIXED_TABLE_SIZE + 686, + FIXED_TABLE_SIZE + 718, + FIXED_TABLE_SIZE + 782, + FIXED_TABLE_SIZE + 912, + FIXED_TABLE_SIZE + 1168, + FIXED_TABLE_SIZE + 1680, + FIXED_TABLE_SIZE + 2704 +}; + static int DecodeImageStream(int xsize, int ysize, int is_level0, VP8LDecoder* const dec, @@ -93,7 +121,7 @@ static int ReadImageInfo(VP8LBitReader* const br, *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; *has_alpha = VP8LReadBits(br, 1); if (VP8LReadBits(br, VP8L_VERSION_BITS) != 0) return 0; - return 1; + return !br->eos_; } int VP8LGetInfo(const uint8_t* data, size_t data_size, @@ -151,31 +179,69 @@ static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) { // Decodes the next Huffman code from bit-stream. // FillBitWindow(br) needs to be called at minimum every second call // to ReadSymbol, in order to pre-fetch enough bits. -static WEBP_INLINE int ReadSymbol(const HuffmanTree* tree, +static WEBP_INLINE int ReadSymbol(const HuffmanCode* table, VP8LBitReader* const br) { - const HuffmanTreeNode* node = tree->root_; - uint32_t bits = VP8LPrefetchBits(br); - int bitpos = br->bit_pos_; - // Check if we find the bit combination from the Huffman lookup table. - const int lut_ix = bits & (HUFF_LUT - 1); - const int lut_bits = tree->lut_bits_[lut_ix]; - if (lut_bits <= HUFF_LUT_BITS) { - VP8LSetBitPos(br, bitpos + lut_bits); - return tree->lut_symbol_[lut_ix]; - } - node += tree->lut_jump_[lut_ix]; - bitpos += HUFF_LUT_BITS; - bits >>= HUFF_LUT_BITS; - - // Decode the value from a binary tree. - assert(node != NULL); - do { - node = HuffmanTreeNextNode(node, bits & 1); - bits >>= 1; - ++bitpos; - } while (HuffmanTreeNodeIsNotLeaf(node)); - VP8LSetBitPos(br, bitpos); - return node->symbol_; + int nbits; + uint32_t val = VP8LPrefetchBits(br); + table += val & HUFFMAN_TABLE_MASK; + nbits = table->bits - HUFFMAN_TABLE_BITS; + if (nbits > 0) { + VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS); + val = VP8LPrefetchBits(br); + table += table->value; + table += val & ((1 << nbits) - 1); + } + VP8LSetBitPos(br, br->bit_pos_ + table->bits); + return table->value; +} + +// Reads packed symbol depending on GREEN channel +#define BITS_SPECIAL_MARKER 0x100 // something large enough (and a bit-mask) +#define PACKED_NON_LITERAL_CODE 0 // must be < NUM_LITERAL_CODES +static WEBP_INLINE int ReadPackedSymbols(const HTreeGroup* group, + VP8LBitReader* const br, + uint32_t* const dst) { + const uint32_t val = VP8LPrefetchBits(br) & (HUFFMAN_PACKED_TABLE_SIZE - 1); + const HuffmanCode32 code = group->packed_table[val]; + assert(group->use_packed_table); + if (code.bits < BITS_SPECIAL_MARKER) { + VP8LSetBitPos(br, br->bit_pos_ + code.bits); + *dst = code.value; + return PACKED_NON_LITERAL_CODE; + } else { + VP8LSetBitPos(br, br->bit_pos_ + code.bits - BITS_SPECIAL_MARKER); + assert(code.value >= NUM_LITERAL_CODES); + return code.value; + } +} + +static int AccumulateHCode(HuffmanCode hcode, int shift, + HuffmanCode32* const huff) { + huff->bits += hcode.bits; + huff->value |= (uint32_t)hcode.value << shift; + assert(huff->bits <= HUFFMAN_TABLE_BITS); + return hcode.bits; +} + +static void BuildPackedTable(HTreeGroup* const htree_group) { + uint32_t code; + for (code = 0; code < HUFFMAN_PACKED_TABLE_SIZE; ++code) { + uint32_t bits = code; + HuffmanCode32* const huff = &htree_group->packed_table[bits]; + HuffmanCode hcode = htree_group->htrees[GREEN][bits]; + if (hcode.value >= NUM_LITERAL_CODES) { + huff->bits = hcode.bits + BITS_SPECIAL_MARKER; + huff->value = hcode.value; + } else { + huff->bits = 0; + huff->value = 0; + bits >>= AccumulateHCode(hcode, 8, huff); + bits >>= AccumulateHCode(htree_group->htrees[RED][bits], 16, huff); + bits >>= AccumulateHCode(htree_group->htrees[BLUE][bits], 0, huff); + bits >>= AccumulateHCode(htree_group->htrees[ALPHA][bits], 24, huff); + (void)bits; + } + } } static int ReadHuffmanCodeLengths( @@ -186,20 +252,18 @@ static int ReadHuffmanCodeLengths( int symbol; int max_symbol; int prev_code_len = DEFAULT_CODE_LENGTH; - HuffmanTree tree; - int huff_codes[NUM_CODE_LENGTH_CODES] = { 0 }; + HuffmanCode table[1 << LENGTHS_TABLE_BITS]; - if (!VP8LHuffmanTreeBuildImplicit(&tree, code_length_code_lengths, - huff_codes, NUM_CODE_LENGTH_CODES)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; + if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS, + code_length_code_lengths, + NUM_CODE_LENGTH_CODES)) { + goto End; } if (VP8LReadBits(br, 1)) { // use length const int length_nbits = 2 + 2 * VP8LReadBits(br, 3); max_symbol = 2 + VP8LReadBits(br, length_nbits); if (max_symbol > num_symbols) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto End; } } else { @@ -208,10 +272,13 @@ static int ReadHuffmanCodeLengths( symbol = 0; while (symbol < num_symbols) { + const HuffmanCode* p; int code_len; if (max_symbol-- == 0) break; VP8LFillBitWindow(br); - code_len = ReadSymbol(&tree, br); + p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK]; + VP8LSetBitPos(br, br->bit_pos_ + p->bits); + code_len = p->value; if (code_len < kCodeLengthLiterals) { code_lengths[symbol++] = code_len; if (code_len != 0) prev_code_len = code_len; @@ -222,7 +289,6 @@ static int ReadHuffmanCodeLengths( const int repeat_offset = kCodeLengthRepeatOffsets[slot]; int repeat = VP8LReadBits(br, extra_bits) + repeat_offset; if (symbol + repeat > num_symbols) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto End; } else { const int length = use_prev ? prev_code_len : 0; @@ -233,7 +299,6 @@ static int ReadHuffmanCodeLengths( ok = 1; End: - VP8LHuffmanTreeFree(&tree); if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return ok; } @@ -241,29 +306,26 @@ static int ReadHuffmanCodeLengths( // 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman // tree. static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, - int* const code_lengths, int* const huff_codes, - HuffmanTree* const tree) { + int* const code_lengths, HuffmanCode* const table) { int ok = 0; + int size = 0; VP8LBitReader* const br = &dec->br_; const int simple_code = VP8LReadBits(br, 1); + memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); + if (simple_code) { // Read symbols, codes & code lengths directly. - int symbols[2]; - int codes[2]; const int num_symbols = VP8LReadBits(br, 1) + 1; const int first_symbol_len_code = VP8LReadBits(br, 1); // The first code is either 1 bit or 8 bit code. - symbols[0] = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); - codes[0] = 0; - code_lengths[0] = num_symbols - 1; + int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); + code_lengths[symbol] = 1; // The second code (if present), is always 8 bit long. if (num_symbols == 2) { - symbols[1] = VP8LReadBits(br, 8); - codes[1] = 1; - code_lengths[1] = num_symbols - 1; + symbol = VP8LReadBits(br, 8); + code_lengths[symbol] = 1; } - ok = VP8LHuffmanTreeBuildExplicit(tree, code_lengths, codes, symbols, - alphabet_size, num_symbols); + ok = 1; } else { // Decode Huffman-coded code lengths. int i; int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; @@ -273,22 +335,23 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, return 0; } - memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); - for (i = 0; i < num_codes; ++i) { code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); } ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, code_lengths); - ok = ok && VP8LHuffmanTreeBuildImplicit(tree, code_lengths, huff_codes, - alphabet_size); } - ok = ok && !br->error_; - if (!ok) { + + ok = ok && !br->eos_; + if (ok) { + size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, + code_lengths, alphabet_size); + } + if (!ok || size == 0) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return 0; } - return 1; + return size; } static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, @@ -298,10 +361,12 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, VP8LMetadata* const hdr = &dec->hdr_; uint32_t* huffman_image = NULL; HTreeGroup* htree_groups = NULL; + HuffmanCode* huffman_tables = NULL; + HuffmanCode* next = NULL; int num_htree_groups = 1; int max_alphabet_size = 0; int* code_lengths = NULL; - int* huff_codes = NULL; + const int table_size = kTableSize[color_cache_bits]; if (allow_recursion && VP8LReadBits(br, 1)) { // use meta Huffman codes. @@ -311,7 +376,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, const int huffman_pixs = huffman_xsize * huffman_ysize; if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, &huffman_image)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto Error; } hdr->huffman_subsample_bits_ = huffman_precision; @@ -325,7 +389,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, } } - if (br->error_) goto Error; + if (br->eos_) goto Error; // Find maximum alphabet size for the htree group. for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { @@ -338,45 +402,82 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, } } + huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size, + sizeof(*huffman_tables)); htree_groups = VP8LHtreeGroupsNew(num_htree_groups); - code_lengths = - (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, sizeof(*code_lengths)); - huff_codes = - (int*)WebPSafeMalloc((uint64_t)max_alphabet_size, sizeof(*huff_codes)); + code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, + sizeof(*code_lengths)); - if (htree_groups == NULL || code_lengths == NULL || huff_codes == NULL) { + if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) { dec->status_ = VP8_STATUS_OUT_OF_MEMORY; goto Error; } + next = huffman_tables; for (i = 0; i < num_htree_groups; ++i) { - HuffmanTree* const htrees = htree_groups[i].htrees_; + HTreeGroup* const htree_group = &htree_groups[i]; + HuffmanCode** const htrees = htree_group->htrees; + int size; + int total_size = 0; + int is_trivial_literal = 1; + int max_bits = 0; for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { int alphabet_size = kAlphabetSize[j]; - HuffmanTree* const htree = htrees + j; + htrees[j] = next; if (j == 0 && color_cache_bits > 0) { alphabet_size += 1 << color_cache_bits; } - if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, huff_codes, - htree)) { + size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next); + if (size == 0) { goto Error; } + if (is_trivial_literal && kLiteralMap[j] == 1) { + is_trivial_literal = (next->bits == 0); + } + total_size += next->bits; + next += size; + if (j <= ALPHA) { + int local_max_bits = code_lengths[0]; + int k; + for (k = 1; k < alphabet_size; ++k) { + if (code_lengths[k] > local_max_bits) { + local_max_bits = code_lengths[k]; + } + } + max_bits += local_max_bits; + } + } + htree_group->is_trivial_literal = is_trivial_literal; + htree_group->is_trivial_code = 0; + if (is_trivial_literal) { + const int red = htrees[RED][0].value; + const int blue = htrees[BLUE][0].value; + const int alpha = htrees[ALPHA][0].value; + htree_group->literal_arb = + ((uint32_t)alpha << 24) | (red << 16) | blue; + if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) { + htree_group->is_trivial_code = 1; + htree_group->literal_arb |= htrees[GREEN][0].value << 8; + } } + htree_group->use_packed_table = !htree_group->is_trivial_code && + (max_bits < HUFFMAN_PACKED_BITS); + if (htree_group->use_packed_table) BuildPackedTable(htree_group); } - WebPSafeFree(huff_codes); WebPSafeFree(code_lengths); // All OK. Finalize pointers and return. hdr->huffman_image_ = huffman_image; hdr->num_htree_groups_ = num_htree_groups; hdr->htree_groups_ = htree_groups; + hdr->huffman_tables_ = huffman_tables; return 1; Error: - WebPSafeFree(huff_codes); WebPSafeFree(code_lengths); WebPSafeFree(huffman_image); - VP8LHtreeGroupsFree(htree_groups, num_htree_groups); + WebPSafeFree(huffman_tables); + VP8LHtreeGroupsFree(htree_groups); return 0; } @@ -474,67 +575,29 @@ static int EmitRows(WEBP_CSP_MODE colorspace, //------------------------------------------------------------------------------ // Export to YUVA -// TODO(skal): should be in yuv.c static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, const WebPDecBuffer* const output) { const WebPYUVABuffer* const buf = &output->u.YUVA; + // first, the luma plane - { - int i; - uint8_t* const y = buf->y + y_pos * buf->y_stride; - for (i = 0; i < width; ++i) { - const uint32_t p = src[i]; - y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, - YUV_HALF); - } - } + WebPConvertARGBToY(src, buf->y + y_pos * buf->y_stride, width); // then U/V planes { uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride; uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride; - const int uv_width = width >> 1; - int i; - for (i = 0; i < uv_width; ++i) { - const uint32_t v0 = src[2 * i + 0]; - const uint32_t v1 = src[2 * i + 1]; - // VP8RGBToU/V expects four accumulated pixels. Hence we need to - // scale r/g/b value by a factor 2. We just shift v0/v1 one bit less. - const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe); - const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe); - const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe); - if (!(y_pos & 1)) { // even lines: store values - u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); - v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); - } else { // odd lines: average with previous values - const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); - const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); - // Approximated average-of-four. But it's an acceptable diff. - u[i] = (u[i] + tmp_u + 1) >> 1; - v[i] = (v[i] + tmp_v + 1) >> 1; - } - } - if (width & 1) { // last pixel - const uint32_t v0 = src[2 * i + 0]; - const int r = (v0 >> 14) & 0x3fc; - const int g = (v0 >> 6) & 0x3fc; - const int b = (v0 << 2) & 0x3fc; - if (!(y_pos & 1)) { // even lines - u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); - v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); - } else { // odd lines (note: we could just skip this) - const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); - const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); - u[i] = (u[i] + tmp_u + 1) >> 1; - v[i] = (v[i] + tmp_v + 1) >> 1; - } - } + // even lines: store values + // odd lines: average with previous values + WebPConvertARGBToUV(src, u, v, width, !(y_pos & 1)); } // Lastly, store alpha if needed. if (buf->a != NULL) { - int i; uint8_t* const a = buf->a + y_pos * buf->a_stride; - for (i = 0; i < width; ++i) a[i] = (src[i] >> 24); +#if defined(WORDS_BIGENDIAN) + WebPExtractAlpha((uint8_t*)src + 0, 0, width, 1, a, 0); +#else + WebPExtractAlpha((uint8_t*)src + 3, 0, width, 1, a, 0); +#endif } } @@ -683,7 +746,7 @@ static void ProcessRows(VP8LDecoder* const dec, int row) { // Nothing to output (this time). } else { const WebPDecBuffer* const output = dec->output_; - if (output->colorspace < MODE_YUV) { // convert to RGBA + if (WebPIsRGBMode(output->colorspace)) { // convert to RGBA const WebPRGBABuffer* const buf = &output->u.RGBA; uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; const int num_rows_out = io->use_scaling ? @@ -715,10 +778,10 @@ static int Is8bOptimizable(const VP8LMetadata* const hdr) { // When the Huffman tree contains only one symbol, we can skip the // call to ReadSymbol() for red/blue/alpha channels. for (i = 0; i < hdr->num_htree_groups_; ++i) { - const HuffmanTree* const htrees = hdr->htree_groups_[i].htrees_; - if (htrees[RED].num_nodes_ > 1) return 0; - if (htrees[BLUE].num_nodes_ > 1) return 0; - if (htrees[ALPHA].num_nodes_ > 1) return 0; + HuffmanCode** const htrees = hdr->htree_groups_[i].htrees; + if (htrees[RED][0].bits > 0) return 0; + if (htrees[BLUE][0].bits > 0) return 0; + if (htrees[ALPHA][0].bits > 0) return 0; } return 1; } @@ -733,6 +796,125 @@ static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int row) { dec->last_row_ = dec->last_out_row_ = row; } +//------------------------------------------------------------------------------ +// Helper functions for fast pattern copy (8b and 32b) + +// cyclic rotation of pattern word +static WEBP_INLINE uint32_t Rotate8b(uint32_t V) { +#if defined(WORDS_BIGENDIAN) + return ((V & 0xff000000u) >> 24) | (V << 8); +#else + return ((V & 0xffu) << 24) | (V >> 8); +#endif +} + +// copy 1, 2 or 4-bytes pattern +static WEBP_INLINE void CopySmallPattern8b(const uint8_t* src, uint8_t* dst, + int length, uint32_t pattern) { + int i; + // align 'dst' to 4-bytes boundary. Adjust the pattern along the way. + while ((uintptr_t)dst & 3) { + *dst++ = *src++; + pattern = Rotate8b(pattern); + --length; + } + // Copy the pattern 4 bytes at a time. + for (i = 0; i < (length >> 2); ++i) { + ((uint32_t*)dst)[i] = pattern; + } + // Finish with left-overs. 'pattern' is still correctly positioned, + // so no Rotate8b() call is needed. + for (i <<= 2; i < length; ++i) { + dst[i] = src[i]; + } +} + +static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) { + const uint8_t* src = dst - dist; + if (length >= 8) { + uint32_t pattern = 0; + switch (dist) { + case 1: + pattern = src[0]; +#if defined(__arm__) || defined(_M_ARM) // arm doesn't like multiply that much + pattern |= pattern << 8; + pattern |= pattern << 16; +#elif defined(WEBP_USE_MIPS_DSP_R2) + __asm__ volatile ("replv.qb %0, %0" : "+r"(pattern)); +#else + pattern = 0x01010101u * pattern; +#endif + break; + case 2: + memcpy(&pattern, src, sizeof(uint16_t)); +#if defined(__arm__) || defined(_M_ARM) + pattern |= pattern << 16; +#elif defined(WEBP_USE_MIPS_DSP_R2) + __asm__ volatile ("replv.ph %0, %0" : "+r"(pattern)); +#else + pattern = 0x00010001u * pattern; +#endif + break; + case 4: + memcpy(&pattern, src, sizeof(uint32_t)); + break; + default: + goto Copy; + break; + } + CopySmallPattern8b(src, dst, length, pattern); + return; + } + Copy: + if (dist >= length) { // no overlap -> use memcpy() + memcpy(dst, src, length * sizeof(*dst)); + } else { + int i; + for (i = 0; i < length; ++i) dst[i] = src[i]; + } +} + +// copy pattern of 1 or 2 uint32_t's +static WEBP_INLINE void CopySmallPattern32b(const uint32_t* src, + uint32_t* dst, + int length, uint64_t pattern) { + int i; + if ((uintptr_t)dst & 4) { // Align 'dst' to 8-bytes boundary. + *dst++ = *src++; + pattern = (pattern >> 32) | (pattern << 32); + --length; + } + assert(0 == ((uintptr_t)dst & 7)); + for (i = 0; i < (length >> 1); ++i) { + ((uint64_t*)dst)[i] = pattern; // Copy the pattern 8 bytes at a time. + } + if (length & 1) { // Finish with left-over. + dst[i << 1] = src[i << 1]; + } +} + +static WEBP_INLINE void CopyBlock32b(uint32_t* const dst, + int dist, int length) { + const uint32_t* const src = dst - dist; + if (dist <= 2 && length >= 4 && ((uintptr_t)dst & 3) == 0) { + uint64_t pattern; + if (dist == 1) { + pattern = (uint64_t)src[0]; + pattern |= pattern << 32; + } else { + memcpy(&pattern, src, sizeof(pattern)); + } + CopySmallPattern32b(src, dst, length, pattern); + } else if (dist >= length) { // no overlap + memcpy(dst, src, length * sizeof(*dst)); + } else { + int i; + for (i = 0; i < length; ++i) dst[i] = src[i]; + } +} + +//------------------------------------------------------------------------------ + static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, int width, int height, int last_row) { int ok = 1; @@ -758,7 +940,7 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, htree_group = GetHtreeGroupForPos(hdr, col, row); } VP8LFillBitWindow(br); - code = ReadSymbol(&htree_group->htrees_[GREEN], br); + code = ReadSymbol(htree_group->htrees[GREEN], br); if (code < NUM_LITERAL_CODES) { // Literal data[pos] = code; ++pos; @@ -774,13 +956,12 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, int dist_code, dist; const int length_sym = code - NUM_LITERAL_CODES; const int length = GetCopyLength(length_sym, br); - const int dist_symbol = ReadSymbol(&htree_group->htrees_[DIST], br); + const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); VP8LFillBitWindow(br); dist_code = GetCopyDistance(dist_symbol, br); dist = PlaneCodeToDistance(width, dist_code); if (pos >= dist && end - pos >= length) { - int i; - for (i = 0; i < length; ++i) data[pos + i] = data[pos + i - dist]; + CopyBlock8b(data + pos, dist, length); } else { ok = 0; goto End; @@ -802,28 +983,44 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, goto End; } assert(br->eos_ == VP8LIsEndOfStream(br)); - ok = !br->error_; - if (!ok) goto End; } // Process the remaining rows corresponding to last row-block. ExtractPalettedAlphaRows(dec, row); End: - if (br->error_ || !ok || (br->eos_ && pos < end)) { + if (!ok || (br->eos_ && pos < end)) { ok = 0; dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR; } else { - dec->last_pixel_ = (int)pos; - if (pos == end) dec->state_ = READ_DATA; + dec->last_pixel_ = pos; } return ok; } +static void SaveState(VP8LDecoder* const dec, int last_pixel) { + assert(dec->incremental_); + dec->saved_br_ = dec->br_; + dec->saved_last_pixel_ = last_pixel; + if (dec->hdr_.color_cache_size_ > 0) { + VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_); + } +} + +static void RestoreState(VP8LDecoder* const dec) { + assert(dec->br_.eos_); + dec->status_ = VP8_STATUS_SUSPENDED; + dec->br_ = dec->saved_br_; + dec->last_pixel_ = dec->saved_last_pixel_; + if (dec->hdr_.color_cache_size_ > 0) { + VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_); + } +} + +#define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, int width, int height, int last_row, ProcessRowsFunc process_func) { - int ok = 1; int row = dec->last_pixel_ / width; int col = dec->last_pixel_ % width; VP8LBitReader* const br = &dec->br_; @@ -835,6 +1032,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, uint32_t* const src_last = data + width * last_row; // Last pixel to decode const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; const int color_cache_limit = len_code_limit + hdr->color_cache_size_; + int next_sync_row = dec->incremental_ ? row : 1 << 24; VP8LColorCache* const color_cache = (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; const int mask = hdr->huffman_mask_; @@ -842,24 +1040,40 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, assert(src < src_end); assert(src_last <= src_end); - while (!br->eos_ && src < src_last) { + while (src < src_last) { int code; + if (row >= next_sync_row) { + SaveState(dec, (int)(src - data)); + next_sync_row = row + SYNC_EVERY_N_ROWS; + } // Only update when changing tile. Note we could use this test: // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed // but that's actually slower and needs storing the previous col/row. - if ((col & mask) == 0) { - htree_group = GetHtreeGroupForPos(hdr, col, row); + if ((col & mask) == 0) htree_group = GetHtreeGroupForPos(hdr, col, row); + if (htree_group->is_trivial_code) { + *src = htree_group->literal_arb; + goto AdvanceByOne; } VP8LFillBitWindow(br); - code = ReadSymbol(&htree_group->htrees_[GREEN], br); + if (htree_group->use_packed_table) { + code = ReadPackedSymbols(htree_group, br, src); + if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne; + } else { + code = ReadSymbol(htree_group->htrees[GREEN], br); + } + if (br->eos_) break; // early out if (code < NUM_LITERAL_CODES) { // Literal - int red, green, blue, alpha; - red = ReadSymbol(&htree_group->htrees_[RED], br); - green = code; - VP8LFillBitWindow(br); - blue = ReadSymbol(&htree_group->htrees_[BLUE], br); - alpha = ReadSymbol(&htree_group->htrees_[ALPHA], br); - *src = ((uint32_t)alpha << 24) | (red << 16) | (green << 8) | blue; + if (htree_group->is_trivial_literal) { + *src = htree_group->literal_arb | (code << 8); + } else { + int red, blue, alpha; + red = ReadSymbol(htree_group->htrees[RED], br); + VP8LFillBitWindow(br); + blue = ReadSymbol(htree_group->htrees[BLUE], br); + alpha = ReadSymbol(htree_group->htrees[ALPHA], br); + if (br->eos_) break; + *src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue; + } AdvanceByOne: ++src; ++col; @@ -879,18 +1093,17 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, int dist_code, dist; const int length_sym = code - NUM_LITERAL_CODES; const int length = GetCopyLength(length_sym, br); - const int dist_symbol = ReadSymbol(&htree_group->htrees_[DIST], br); + const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); VP8LFillBitWindow(br); dist_code = GetCopyDistance(dist_symbol, br); dist = PlaneCodeToDistance(width, dist_code); + if (br->eos_) break; if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) { - ok = 0; - goto End; + goto Error; } else { - int i; - for (i = 0; i < length; ++i) src[i] = src[i - dist]; - src += length; + CopyBlock32b(src, dist, length); } + src += length; col += length; while (col >= width) { col -= width; @@ -899,12 +1112,13 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, process_func(dec, row); } } - if (src < src_end) { - if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row); - if (color_cache != NULL) { - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } + // Because of the check done above (before 'src' was incremented by + // 'length'), the following holds true. + assert(src <= src_end); + if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row); + if (color_cache != NULL) { + while (last_cached < src) { + VP8LColorCacheInsert(color_cache, *last_cached++); } } } else if (code < color_cache_limit) { // Color cache @@ -916,26 +1130,30 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, *src = VP8LColorCacheLookup(color_cache, key); goto AdvanceByOne; } else { // Not reached - ok = 0; - goto End; + goto Error; } assert(br->eos_ == VP8LIsEndOfStream(br)); - ok = !br->error_; - if (!ok) goto End; } - // Process the remaining rows corresponding to last row-block. - if (process_func != NULL) process_func(dec, row); - End: - if (br->error_ || !ok || (br->eos_ && src < src_end)) { - ok = 0; - dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED - : VP8_STATUS_BITSTREAM_ERROR; + if (dec->incremental_ && br->eos_ && src < src_end) { + RestoreState(dec); + } else if (!br->eos_) { + // Process the remaining rows corresponding to last row-block. + if (process_func != NULL) { + process_func(dec, row); + } + dec->status_ = VP8_STATUS_OK; + dec->last_pixel_ = (int)(src - data); // end-of-scan marker } else { - dec->last_pixel_ = (int)(src - data); - if (src == src_end) dec->state_ = READ_DATA; + // if not incremental, and we are past the end of buffer (eos_=1), then this + // is a real bitstream error. + goto Error; } - return ok; + return 1; + + Error: + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + return 0; } // ----------------------------------------------------------------------------- @@ -1029,16 +1247,18 @@ static int ReadTransform(int* const xsize, int const* ysize, // VP8LMetadata static void InitMetadata(VP8LMetadata* const hdr) { - assert(hdr); + assert(hdr != NULL); memset(hdr, 0, sizeof(*hdr)); } static void ClearMetadata(VP8LMetadata* const hdr) { - assert(hdr); + assert(hdr != NULL); WebPSafeFree(hdr->huffman_image_); - VP8LHtreeGroupsFree(hdr->htree_groups_, hdr->num_htree_groups_); + WebPSafeFree(hdr->huffman_tables_); + VP8LHtreeGroupsFree(hdr->htree_groups_); VP8LColorCacheClear(&hdr->color_cache_); + VP8LColorCacheClear(&hdr->saved_color_cache_); InitMetadata(hdr); } @@ -1049,7 +1269,6 @@ VP8LDecoder* VP8LNew(void) { VP8LDecoder* const dec = (VP8LDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); if (dec == NULL) return NULL; dec->status_ = VP8_STATUS_OK; - dec->action_ = READ_DIM; dec->state_ = READ_DIM; VP8LDspInit(); // Init critical function pointers. @@ -1161,18 +1380,12 @@ static int DecodeImageStream(int xsize, int ysize, // Use the Huffman trees to decode the LZ77 encoded data. ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, transform_ysize, NULL); - ok = ok && !br->error_; + ok = ok && !br->eos_; End: - if (!ok) { WebPSafeFree(data); ClearMetadata(hdr); - // If not enough data (br.eos_) resulted in BIT_STREAM_ERROR, update the - // status appropriately. - if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && dec->br_.eos_) { - dec->status_ = VP8_STATUS_SUSPENDED; - } } else { if (decoded_data != NULL) { *decoded_data = data; @@ -1269,7 +1482,6 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, data, data_size); - dec->action_ = READ_HDR; if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) { goto Err; } @@ -1290,7 +1502,6 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, if (!ok) goto Err; - dec->action_ = READ_DATA; return 1; Err: @@ -1302,7 +1513,6 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { VP8LDecoder* const dec = alph_dec->vp8l_dec_; assert(dec != NULL); - assert(dec->action_ == READ_DATA); assert(last_row <= dec->height_); if (dec->last_pixel_ == dec->width_ * dec->height_) { @@ -1339,7 +1549,6 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { io->width = width; io->height = height; - dec->action_ = READ_HDR; if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; return 1; @@ -1356,7 +1565,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { // Sanity checks. if (dec == NULL) return 0; - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + assert(dec->hdr_.huffman_tables_ != NULL); assert(dec->hdr_.htree_groups_ != NULL); assert(dec->hdr_.num_htree_groups_ > 0); @@ -1364,34 +1573,49 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { assert(io != NULL); params = (WebPDecParams*)io->opaque; assert(params != NULL); - dec->output_ = params->output; - assert(dec->output_ != NULL); // Initialization. - if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - goto Err; - } + if (dec->state_ != READ_DATA) { + dec->output_ = params->output; + assert(dec->output_ != NULL); - if (!AllocateInternalBuffers32b(dec, io->width)) goto Err; + if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { + dec->status_ = VP8_STATUS_INVALID_PARAM; + goto Err; + } + + if (!AllocateInternalBuffers32b(dec, io->width)) goto Err; - if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; + if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; - if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { - // need the alpha-multiply functions for premultiplied output or rescaling - WebPInitAlphaProcessing(); + if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { + // need the alpha-multiply functions for premultiplied output or rescaling + WebPInitAlphaProcessing(); + } + if (!WebPIsRGBMode(dec->output_->colorspace)) { + WebPInitConvertARGBToYUV(); + if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing(); + } + if (dec->incremental_) { + if (dec->hdr_.color_cache_size_ > 0 && + dec->hdr_.saved_color_cache_.colors_ == NULL) { + if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_, + dec->hdr_.color_cache_.hash_bits_)) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + goto Err; + } + } + } + dec->state_ = READ_DATA; } // Decode. - dec->action_ = READ_DATA; if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, dec->height_, ProcessRows)) { goto Err; } - // Cleanup. params->last_y = dec->last_out_row_; - VP8LClear(dec); return 1; Err: diff --git a/src/3rdparty/libwebp/src/dec/vp8li.h b/src/3rdparty/libwebp/src/dec/vp8li.h index 21c593f..8886e47 100644 --- a/src/3rdparty/libwebp/src/dec/vp8li.h +++ b/src/3rdparty/libwebp/src/dec/vp8li.h @@ -43,6 +43,7 @@ struct VP8LTransform { typedef struct { int color_cache_size_; VP8LColorCache color_cache_; + VP8LColorCache saved_color_cache_; // for incremental int huffman_mask_; int huffman_subsample_bits_; @@ -50,12 +51,12 @@ typedef struct { uint32_t *huffman_image_; int num_htree_groups_; HTreeGroup *htree_groups_; + HuffmanCode *huffman_tables_; } VP8LMetadata; typedef struct VP8LDecoder VP8LDecoder; struct VP8LDecoder { VP8StatusCode status_; - VP8LDecodeState action_; VP8LDecodeState state_; VP8Io *io_; @@ -66,6 +67,9 @@ struct VP8LDecoder { uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage. VP8LBitReader br_; + int incremental_; // if true, incremental decoding is expected + VP8LBitReader saved_br_; // note: could be local variables too + int saved_last_pixel_; int width_; int height_; diff --git a/src/3rdparty/libwebp/src/dec/webp.c b/src/3rdparty/libwebp/src/dec/webp.c index 59e21a9..952178f 100644 --- a/src/3rdparty/libwebp/src/dec/webp.c +++ b/src/3rdparty/libwebp/src/dec/webp.c @@ -16,6 +16,7 @@ #include "./vp8i.h" #include "./vp8li.h" #include "./webpi.h" +#include "../utils/utils.h" #include "../webp/mux_types.h" // ALPHA_FLAG //------------------------------------------------------------------------------ @@ -43,14 +44,6 @@ // All sizes are in little-endian order. // Note: chunk data size must be padded to multiple of 2 when written. -static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) { - return data[0] | (data[1] << 8) | (data[2] << 16); -} - -static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) { - return (uint32_t)get_le24(data) | (data[3] << 24); -} - // Validates the RIFF container (if detected) and skips over it. // If a RIFF container is detected, returns: // VP8_STATUS_BITSTREAM_ERROR for invalid header, @@ -70,7 +63,7 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data, if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. } else { - const uint32_t size = get_le32(*data + TAG_SIZE); + const uint32_t size = GetLE32(*data + TAG_SIZE); // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; @@ -116,7 +109,7 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data, if (!memcmp(*data, "VP8X", TAG_SIZE)) { int width, height; uint32_t flags; - const uint32_t chunk_size = get_le32(*data + TAG_SIZE); + const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); if (chunk_size != VP8X_CHUNK_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. } @@ -125,9 +118,9 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data, if (*data_size < vp8x_size) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } - flags = get_le32(*data + 8); - width = 1 + get_le24(*data + 12); - height = 1 + get_le24(*data + 15); + flags = GetLE32(*data + 8); + width = 1 + GetLE24(*data + 12); + height = 1 + GetLE24(*data + 15); if (width * (uint64_t)height >= MAX_IMAGE_AREA) { return VP8_STATUS_BITSTREAM_ERROR; // image is too large } @@ -181,7 +174,7 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, return VP8_STATUS_NOT_ENOUGH_DATA; } - chunk_size = get_le32(buf + TAG_SIZE); + chunk_size = GetLE32(buf + TAG_SIZE); if (chunk_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. } @@ -247,7 +240,7 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, if (is_vp8 || is_vp8l) { // Bitstream contains VP8/VP8L header. - const uint32_t size = get_le32(data + TAG_SIZE); + const uint32_t size = GetLE32(data + TAG_SIZE); if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. } @@ -521,11 +514,9 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, WebPFreeDecBuffer(params->output); } -#if WEBP_DECODER_ABI_VERSION > 0x0203 if (params->options != NULL && params->options->flip) { status = WebPFlipBuffer(params->output); } -#endif return status; } @@ -808,11 +799,13 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, // Scaling io->use_scaling = (options != NULL) && (options->use_scaling > 0); if (io->use_scaling) { - if (options->scaled_width <= 0 || options->scaled_height <= 0) { + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) { return 0; } - io->scaled_width = options->scaled_width; - io->scaled_height = options->scaled_height; + io->scaled_width = scaled_width; + io->scaled_height = scaled_height; } // Filter diff --git a/src/3rdparty/libwebp/src/dec/webpi.h b/src/3rdparty/libwebp/src/dec/webpi.h index 457c72e..c75a2e4 100644 --- a/src/3rdparty/libwebp/src/dec/webpi.h +++ b/src/3rdparty/libwebp/src/dec/webpi.h @@ -26,7 +26,10 @@ extern "C" { typedef struct WebPDecParams WebPDecParams; typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); -typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos); +typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p, + int expected_num_out_lines); +typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos, + int max_out_lines); struct WebPDecParams { WebPDecBuffer* output; // output buffer. @@ -40,7 +43,7 @@ struct WebPDecParams { void* memory; // overall scratch memory for the output work. OutputFunc emit; // output RGB or YUV samples - OutputFunc emit_alpha; // output alpha channel + OutputAlphaFunc emit_alpha; // output alpha channel OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values }; |