| Index: third_party/libwebp/dec/vp8l.c
|
| diff --git a/third_party/libwebp/dec/vp8l.c b/third_party/libwebp/dec/vp8l.c
|
| index 2fa5f4046b3803b8ced855569cf34134f41da721..a76ad6a13ac8e3bb76b76feed410d94836af6370 100644
|
| --- a/third_party/libwebp/dec/vp8l.c
|
| +++ b/third_party/libwebp/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:
|
|
|