---
doc/general.texi | 2 +-
libavcodec/webp.c | 1173 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 1164 insertions(+), 11 deletions(-)
diff --git a/doc/general.texi b/doc/general.texi
index 18e425a..aeda8ea 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -413,7 +413,7 @@ following image formats are supported:
@item Truevision Targa @tab X @tab X
@tab Targa (.TGA) image format
@item WebP @tab @tab X
- @tab WebP image format (lossy only)
+ @tab WebP image format
@item XBM @tab X @tab
@tab X BitMap image format
@item XWD @tab X @tab X
diff --git a/libavcodec/webp.c b/libavcodec/webp.c
index 33b242f..04313b4 100644
--- a/libavcodec/webp.c
+++ b/libavcodec/webp.c
@@ -1,6 +1,7 @@
/*
* WebP (.webp) image decoder
* Copyright (c) 2013 Aneesh Dogra <[email protected]>
+ * Copyright (c) 2013 Justin Ruggles <[email protected]>
*
* This file is part of Libav.
*
@@ -19,9 +20,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#define BITSTREAM_READER_LE
#include "avcodec.h"
#include "bytestream.h"
#include "internal.h"
+#include "get_bits.h"
#include "vp8.h"
#define VP8X_FLAG_ANIMATION 0x2
@@ -30,33 +33,1164 @@
#define VP8X_FLAG_ALPHA 0x10
#define VP8X_FLAG_ICC 0x20
+#define MAX_PALETTE_SIZE 256
+#define MAX_CACHE_BITS 11
+#define NUM_CODE_LENGTH_CODES 19
+#define HUFFMAN_CODES_PER_META_CODE 5
+#define NUM_LITERAL_CODES 256
+#define NUM_LENGTH_CODES 24
+#define NUM_DISTANCE_CODES 40
+#define NUM_SHORT_DISTANCES 120
+#define MAX_HUFFMAN_CODE_LENGTH 15
+
+static const uint16_t alphabet_sizes[HUFFMAN_CODES_PER_META_CODE] = {
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES,
+ NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES,
+ NUM_DISTANCE_CODES
+};
+
+static const uint8_t code_length_code_order[NUM_CODE_LENGTH_CODES] = {
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+static const int8_t lz77_distance_offsets[NUM_SHORT_DISTANCES][2] = {
+ { 0, 1 }, { 1, 0 }, { 1, 1 }, { -1, 1 }, { 0, 2 }, { 2, 0 }, { 1, 2
}, { -1, 2 },
+ { 2, 1 }, { -2, 1 }, { 2, 2 }, { -2, 2 }, { 0, 3 }, { 3, 0 }, { 1, 3
}, { -1, 3 },
+ { 3, 1 }, { -3, 1 }, { 2, 3 }, { -2, 3 }, { 3, 2 }, { -3, 2 }, { 0, 4
}, { 4, 0 },
+ { 1, 4 }, { -1, 4 }, { 4, 1 }, { -4, 1 }, { 3, 3 }, { -3, 3 }, { 2, 4
}, { -2, 4 },
+ { 4, 2 }, { -4, 2 }, { 0, 5 }, { 3, 4 }, { -3, 4 }, { 4, 3 }, { -4, 3
}, { 5, 0 },
+ { 1, 5 }, { -1, 5 }, { 5, 1 }, { -5, 1 }, { 2, 5 }, { -2, 5 }, { 5, 2
}, { -5, 2 },
+ { 4, 4 }, { -4, 4 }, { 3, 5 }, { -3, 5 }, { 5, 3 }, { -5, 3 }, { 0, 6
}, { 6, 0 },
+ { 1, 6 }, { -1, 6 }, { 6, 1 }, { -6, 1 }, { 2, 6 }, { -2, 6 }, { 6, 2
}, { -6, 2 },
+ { 4, 5 }, { -4, 5 }, { 5, 4 }, { -5, 4 }, { 3, 6 }, { -3, 6 }, { 6, 3
}, { -6, 3 },
+ { 0, 7 }, { 7, 0 }, { 1, 7 }, { -1, 7 }, { 5, 5 }, { -5, 5 }, { 7, 1
}, { -7, 1 },
+ { 4, 6 }, { -4, 6 }, { 6, 4 }, { -6, 4 }, { 2, 7 }, { -2, 7 }, { 7, 2
}, { -7, 2 },
+ { 3, 7 }, { -3, 7 }, { 7, 3 }, { -7, 3 }, { 5, 6 }, { -5, 6 }, { 6, 5
}, { -6, 5 },
+ { 8, 0 }, { 4, 7 }, { -4, 7 }, { 7, 4 }, { -7, 4 }, { 8, 1 }, { 8, 2
}, { 6, 6 },
+ { -6, 6 }, { 8, 3 }, { 5, 7 }, { -5, 7 }, { 7, 5 }, { -7, 5 }, { 8, 4
}, { 6, 7 },
+ { -6, 7 }, { 7, 6 }, { -7, 6 }, { 8, 5 }, { 7, 7 }, { -7, 7 }, { 8, 6
}, { 8, 7 }
+};
+
+enum TransformType {
+ PREDICTOR_TRANSFORM = 0,
+ COLOR_TRANSFORM = 1,
+ SUBTRACT_GREEN = 2,
+ COLOR_INDEXING_TRANSFORM = 3,
+};
+
+enum PredictionMode {
+ PRED_MODE_BLACK,
+ PRED_MODE_L,
+ PRED_MODE_T,
+ PRED_MODE_TR,
+ PRED_MODE_TL,
+ PRED_MODE_AVG_T_AVG_L_TR,
+ PRED_MODE_AVG_L_TL,
+ PRED_MODE_AVG_L_T,
+ PRED_MODE_AVG_TL_T,
+ PRED_MODE_AVG_T_TR,
+ PRED_MODE_AVG_AVG_L_TL_AVG_T_TR,
+ PRED_MODE_SELECT,
+ PRED_MODE_ADD_SUBTRACT_FULL,
+ PRED_MODE_ADD_SUBTRACT_HALF,
+};
+
+enum HuffmanIndex {
+ HUFF_IDX_GREEN = 0,
+ HUFF_IDX_RED = 1,
+ HUFF_IDX_BLUE = 2,
+ HUFF_IDX_ALPHA = 3,
+ HUFF_IDX_DIST = 4
+};
+
+enum ImageRole {
+ /* Stores the actual pixels of the image. */
+ IMAGE_ROLE_ARGB,
+
+ /* Stores the meta Huffman codes. The red and green components of a pixel
+ define the meta Huffman code used in a particular block of the ARGB
+ image. */
+ IMAGE_ROLE_ENTROPY,
+
+ /* Stores the metadata for Predictor Transform. The green component of a
+ pixel defines which of the 14 predictors is used within a particular
+ block of the ARGB image. */
+ IMAGE_ROLE_PREDICTOR,
+
+ /* It is created by ColorTransformElement values for different blocks of
+ the image. Each ColorTransformElement is treated as a pixel whose alpha
+ component is 255, red component is cte.red_to_blue, green component is
+ cte.green_to_blue and blue component is cte.green_to_red. */
+ IMAGE_ROLE_COLOR_TRANSFORM,
+
+ /* Color indexing image: An array of of size color_table_size (up to 256
+ ARGB values) storing the metadata for the Color Indexing Transform. This
+ is stored as an image of width color_table_size and height 1. */
+ IMAGE_ROLE_COLOR_INDEXING,
+
+ IMAGE_ROLE_NB,
+};
+
+typedef struct HuffReader {
+ VLC vlc;
+ int simple;
+ int nb_symbols;
+ uint16_t simple_symbols[2];
+} HuffReader;
+
+typedef struct HuffmanGroup {
+ HuffReader huffman_code[HUFFMAN_CODES_PER_META_CODE];
+} HuffmanGroup;
+
+typedef struct ImageContext {
+ enum ImageRole role;
+ AVFrame *frame;
+ int color_cache_bits;
+ uint8_t *color_cache;
+ int num_huffman_groups;
+ HuffmanGroup *huffman_groups;
+} ImageContext;
+
+typedef union {
+ uint8_t u8;
+ int8_t s8;
+} av_alias alias8;
+
typedef struct WebPContext {
VP8Context v;
+ AVCodecContext *avctx;
int initialized;
+ GetBitContext gb;
GetByteContext alpha_gb;
uint8_t has_alpha;
int width;
int height;
+
+ int lossless;
+ ImageContext image[IMAGE_ROLE_NB];
+ enum TransformType transforms[4];
+ int nb_transforms;
+ int subtract_green;
+ int predictor_block_size;
+ int color_block_size;
+ int color_index_width_bits;
+ int reduced_width;
+ int use_entropy_image;
+ int entropy_size;
} WebPContext;
+static av_always_inline uint8_t *get_pixel(AVFrame *frame, int x, int y)
+{
+ if (x < 0 || y < 0)
+ return NULL;
+ if (x >= frame->width) {
+ x = 0;
+ y++;
+ }
+ if (y >= frame->height)
+ return NULL;
+
+ return frame->data[0] + y * frame->linesize[0] + 4 * x;
+}
+
+static av_always_inline uint8_t add_comp(int a, int b)
+{
+ return (a + b) & 0xFF;
+}
+
+static av_always_inline uint8_t avg2_comp(int a, int b)
+{
+ return (a + b) / 2;
+}
+
+static av_always_inline uint8_t clamp_add_subtract_full(int a, int b, int c)
+{
+ return av_clip_uint8(a + b - c);
+}
+
+static av_always_inline uint8_t clamp_add_subtract_half(int a, int b, int c)
+{
+ int d = avg2_comp(a, b);
+ return av_clip_uint8(d + (d - c) / 2);
+}
+
+static void image_ctx_free(ImageContext *img)
+{
+ int i, j;
+
+ if (img->color_cache)
+ av_free(img->color_cache);
+ if (img->role != IMAGE_ROLE_ARGB)
+ av_frame_free(&img->frame);
+ if (img->huffman_groups && img->num_huffman_groups > 0) {
+ for (i = 0; i < img->num_huffman_groups; i++) {
+ for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; j++) {
+ if (!img->huffman_groups[i].huffman_code[j].simple)
+ ff_free_vlc(&img->huffman_groups[i].huffman_code[j].vlc);
+ }
+ }
+ av_free(img->huffman_groups);
+ }
+ memset(img, 0, sizeof(*img));
+}
+
+
+/* Differs from get_vlc2() in the following ways:
+ * - codes are bit-reversed
+ * - assumes 8-bit table to make reversal simpler
+ * - assumes max depth of 2 since the max code length for WebP is 15
+ */
+static av_always_inline int webp_get_vlc(GetBitContext *gb, VLC_TYPE
(*table)[2])
+{
+ int n, nb_bits;
+ unsigned int index;
+ int code;
+
+ OPEN_READER(re, gb);
+ UPDATE_CACHE(re, gb);
+
+ index = SHOW_UBITS(re, gb, 8);
+ index = ff_reverse[index];
+ code = table[index][0];
+ n = table[index][1];
+
+ if (n < 0) {
+ LAST_SKIP_BITS(re, gb, 8);
+ UPDATE_CACHE(re, gb);
+
+ nb_bits = -n;
+
+ index = SHOW_UBITS(re, gb, nb_bits);
+ index = (ff_reverse[index] >> (8 - nb_bits)) + code;
+ code = table[index][0];
+ n = table[index][1];
+ }
+ SKIP_BITS(re, gb, n);
+
+ CLOSE_READER(re, gb);
+
+ return code;
+}
+
+static int huff_reader_get_symbol(HuffReader *r, GetBitContext *gb)
+{
+ if (!r->nb_symbols)
+ return AVERROR_BUG;
+
+ if (r->simple) {
+ if (r->nb_symbols == 1) {
+ return r->simple_symbols[0];
+ } else {
+ return r->simple_symbols[get_bits1(gb)];
+ }
+ } else {
+ return webp_get_vlc(gb, r->vlc.table);
+ }
+}
+
+static int huff_reader_build_canonical(HuffReader *r, int *code_lengths,
+ int alphabet_size)
+{
+ int len, sym, code;
+ int max_code_length = 0;
+ uint16_t *codes;
+
+ for (sym = 0; sym < alphabet_size; sym++)
+ max_code_length = FFMAX(max_code_length, code_lengths[sym]);
+
+ if (max_code_length == 0 || max_code_length > MAX_HUFFMAN_CODE_LENGTH)
+ return AVERROR(EINVAL);
+
+ codes = av_malloc(alphabet_size * sizeof(*codes));
+ if (!codes)
+ return AVERROR(ENOMEM);
+
+ code = 0;
+ r->nb_symbols = 0;
+ for (len = 1; len <= max_code_length; len++) {
+ for (sym = 0; sym < alphabet_size; sym++) {
+ if (code_lengths[sym] != len)
+ continue;
+ codes[sym] = code++;
+ r->nb_symbols++;
+ }
+ code <<= 1;
+ }
+
+ init_vlc(&r->vlc, 8, alphabet_size,
+ code_lengths, sizeof(*code_lengths), sizeof(*code_lengths),
+ codes, sizeof(*codes), sizeof(*codes), 0);
+ r->simple = 0;
+
+ av_free(codes);
+ return 0;
+}
+
+static void read_huffman_code_simple(WebPContext *s, HuffReader *hc)
+{
+ hc->nb_symbols = get_bits1(&s->gb) + 1;
+
+ if (get_bits1(&s->gb))
+ hc->simple_symbols[0] = get_bits(&s->gb, 8);
+ else
+ hc->simple_symbols[0] = get_bits1(&s->gb);
+
+ if (hc->nb_symbols == 2) {
+ hc->simple_symbols[1] = get_bits(&s->gb, 8);
+ }
+ hc->simple = 1;
+}
+
+static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
+ int alphabet_size)
+{
+ HuffReader code_len_hc;
+ int *code_lengths = NULL;
+ int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
+ int i, symbol, max_symbol, prev_code_len, ret = 0;
+ int num_codes = 4 + get_bits(&s->gb, 4);
+
+ if (num_codes > NUM_CODE_LENGTH_CODES)
+ return AVERROR_INVALIDDATA;
+
+ for (i = 0; i < num_codes; i++)
+ code_length_code_lengths[code_length_code_order[i]] = get_bits(&s->gb,
3);
+
+ memset(&code_len_hc, 0, sizeof(code_len_hc));
+ ret = huff_reader_build_canonical(&code_len_hc, code_length_code_lengths,
+ NUM_CODE_LENGTH_CODES);
+ if (ret < 0)
+ goto finish;
+
+ code_lengths = av_mallocz_array(alphabet_size, sizeof(*code_lengths));
+ if (!code_lengths) {
+ ret = AVERROR(ENOMEM);
+ goto finish;
+ }
+
+ if (get_bits1(&s->gb)) {
+ int bits = 2 + 2 * get_bits(&s->gb, 3);
+ max_symbol = 2 + get_bits(&s->gb, bits);
+ if (max_symbol > alphabet_size) {
+ av_log(s->avctx, AV_LOG_ERROR, "max symbol %d > alphabet size
%d\n",
+ max_symbol, alphabet_size);
+ ret = AVERROR_INVALIDDATA;
+ goto finish;
+ }
+ } else {
+ max_symbol = alphabet_size;
+ }
+
+ prev_code_len = 8;
+ symbol = 0;
+ while (symbol < alphabet_size) {
+ int code_len;
+
+ if (!max_symbol--)
+ break;
+ code_len = huff_reader_get_symbol(&code_len_hc, &s->gb);
+ if (code_len < 16) {
+ /* Code length code [0..15] indicates literal code lengths. */
+ code_lengths[symbol++] = code_len;
+ if (code_len)
+ prev_code_len = code_len;
+ } else {
+ int repeat = 0, length = 0;
+ if (code_len == 16) {
+ /* Code 16 repeats the previous non-zero value [3..6] times,
+ i.e., 3 + ReadBits(2) times. If code 16 is used before a
+ non-zero value has been emitted, a value of 8 is repeated.
*/
+ repeat = 3 + get_bits(&s->gb, 2);
+ length = prev_code_len;
+ } else if (code_len == 17) {
+ /* Code 17 emits a streak of zeros [3..10], i.e.,
+ 3 + ReadBits(3) times. */
+ repeat = 3 + get_bits(&s->gb, 3);
+ } else if (code_len == 18) {
+ /* Code 18 emits a streak of zeros of length [11..138], i.e.,
+ 11 + ReadBits(7) times. */
+ repeat = 11 + get_bits(&s->gb, 7);
+ }
+ if (symbol + repeat > alphabet_size) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "invalid symbol %d + repeat %d > alphabet size %d\n",
+ symbol, repeat, alphabet_size);
+ ret = AVERROR_INVALIDDATA;
+ goto finish;
+ }
+ while (repeat-- > 0)
+ code_lengths[symbol++] = length;
+ }
+ }
+
+ ret = huff_reader_build_canonical(hc, code_lengths, alphabet_size);
+
+finish:
+ ff_free_vlc(&code_len_hc.vlc);
+ av_free(code_lengths);
+ return ret;
+}
+
+static int decode_entropy_coded_image(WebPContext *s, enum ImageRole role);
+
+static int decode_image(WebPContext *s, enum ImageRole role, int w, int h)
+{
+ ImageContext *img;
+ int ret;
+
+ img = &s->image[role];
+ img->role = role;
+
+ img->frame = av_frame_alloc();
+ if (!img->frame)
+ return AVERROR(ENOMEM);
+
+ img->frame->format = AV_PIX_FMT_ARGB;
+ img->frame->width = w;
+ img->frame->height = h;
+
+ ret = av_frame_get_buffer(img->frame, 1);
+ if (ret < 0) {
+ av_frame_free(&img->frame);
+ return ret;
+ }
+
+ ret = decode_entropy_coded_image(s, img->role);
+ if (ret < 0) {
+ av_frame_free(&img->frame);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define PARSE_BLOCK_SIZE(w, h) do { \
+ block_bits = get_bits(&s->gb, 3) + 2; \
+ block_size = 1 << block_bits; \
+ blocks_w = FFALIGN((w), block_size) / block_size; \
+ blocks_h = FFALIGN((h), block_size) / block_size; \
+} while (0)
+
+static int decode_entropy_image(WebPContext *s, ImageContext *rgba_img)
+{
+ int ret, block_bits, block_size, width, blocks_w, blocks_h;
+
+ width = s->width;
+ if (s->color_index_width_bits > 0)
+ width = s->reduced_width;
+
+ PARSE_BLOCK_SIZE(width, s->height);
+
+ ret = decode_image(s, IMAGE_ROLE_ENTROPY, blocks_w, blocks_h);
+ if (ret < 0)
+ return ret;
+
+ s->use_entropy_image = 1;
+ s->entropy_size = block_size;
+
+ /* the number of huffman groups is determined by the maximum group number
+ coded in the entropy image */
+ {
+ ImageContext *img;
+ int x, y, max;
+ uint8_t *p;
+
+ img = &s->image[IMAGE_ROLE_ENTROPY];
+ max = 0;
+ for (y = 0; y < img->frame->height; y++) {
+ for (x = 0; x < img->frame->width; x++) {
+ p = get_pixel(img->frame, x, y);
+ max = FFMAX(max, p[2]);
+ }
+ }
+ rgba_img->num_huffman_groups = max + 1;
+ }
+
+ return 0;
+}
+
+static int parse_transform_predictor(WebPContext *s)
+{
+ int block_bits, block_size, blocks_w, blocks_h, ret;
+
+ PARSE_BLOCK_SIZE(s->width, s->height);
+
+ ret = decode_image(s, IMAGE_ROLE_PREDICTOR, blocks_w, blocks_h);
+ if (ret < 0)
+ return ret;
+
+ s->predictor_block_size = block_size;
+
+ return 0;
+}
+
+static int parse_transform_color(WebPContext *s)
+{
+ int block_bits, block_size, blocks_w, blocks_h, ret;
+
+ PARSE_BLOCK_SIZE(s->width, s->height);
+
+ ret = decode_image(s, IMAGE_ROLE_COLOR_TRANSFORM, blocks_w, blocks_h);
+ if (ret < 0)
+ return ret;
+
+ s->color_block_size = block_size;
+
+ return 0;
+}
+
+static int parse_transform_color_indexing(WebPContext *s)
+{
+ int width_bits, index_size, ret;
+
+ index_size = get_bits(&s->gb, 8) + 1;
+
+ if (index_size <= 2)
+ width_bits = 3;
+ else if (index_size <= 4)
+ width_bits = 2;
+ else if (index_size <= 16)
+ width_bits = 1;
+ else
+ width_bits = 0;
+
+ ret = decode_image(s, IMAGE_ROLE_COLOR_INDEXING, index_size, 1);
+ if (ret < 0)
+ return ret;
+
+ s->color_index_width_bits = width_bits;
+ if (width_bits > 0)
+ s->reduced_width = (s->width + ((1 << width_bits) - 1)) >> width_bits;
+
+ /* color index values are delta-coded */
+ {
+ ImageContext *img;
+ int x;
+ uint8_t *ct, *ct0 = NULL;
+
+ img = &s->image[IMAGE_ROLE_COLOR_INDEXING];
+ ct = img->frame->data[0];
+ for (x = 0; x < img->frame->width; x++) {
+ if (ct0) {
+ ct[0] = add_comp(ct[0], ct0[0]);
+ ct[1] = add_comp(ct[1], ct0[1]);
+ ct[2] = add_comp(ct[2], ct0[2]);
+ ct[3] = add_comp(ct[3], ct0[3]);
+ }
+ ct0 = ct;
+ ct += 4;
+ }
+ }
+
+ return 0;
+}
+
+static HuffmanGroup *get_huffman_group(WebPContext *s, ImageContext *img,
+ int x, int y)
+{
+ ImageContext *gimg = &s->image[IMAGE_ROLE_ENTROPY];
+ int group = 0;
+ uint8_t *p;
+
+ if (s->use_entropy_image) {
+ int group_x, group_y;
+
+ group_x = x / s->entropy_size;
+ group_y = y / s->entropy_size;
+ p = get_pixel(gimg->frame, group_x, group_y);
+ group = p[2];
+ }
+
+ return &img->huffman_groups[group];
+}
+
+/* PRED_MODE_BLACK */
+static void inv_predict_0(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ static const uint8_t black[4] = { 0xFF, 0x00, 0x00, 0x00 };
+
+ AV_COPY32(p, black);
+}
+
+/* PRED_MODE_L */
+static void inv_predict_1(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ AV_COPY32(p, p_l);
+}
+
+/* PRED_MODE_T */
+static void inv_predict_2(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ AV_COPY32(p, p_t);
+}
+
+/* PRED_MODE_TR */
+static void inv_predict_3(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ AV_COPY32(p, p_tr);
+}
+
+/* PRED_MODE_TL */
+static void inv_predict_4(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ AV_COPY32(p, p_tl);
+}
+
+/* PRED_MODE_AVG_T_AVG_L_TR */
+static void inv_predict_5(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(p_t[0], avg2_comp(p_l[0], p_tr[0]));
+ p[1] = avg2_comp(p_t[1], avg2_comp(p_l[1], p_tr[1]));
+ p[2] = avg2_comp(p_t[2], avg2_comp(p_l[2], p_tr[2]));
+ p[3] = avg2_comp(p_t[3], avg2_comp(p_l[3], p_tr[3]));
+}
+
+/* PRED_MODE_AVG_L_TL */
+static void inv_predict_6(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(p_l[0], p_tl[0]);
+ p[1] = avg2_comp(p_l[1], p_tl[1]);
+ p[2] = avg2_comp(p_l[2], p_tl[2]);
+ p[3] = avg2_comp(p_l[3], p_tl[3]);
+}
+
+/* PRED_MODE_AVG_L_T */
+static void inv_predict_7(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(p_l[0], p_t[0]);
+ p[1] = avg2_comp(p_l[1], p_t[1]);
+ p[2] = avg2_comp(p_l[2], p_t[2]);
+ p[3] = avg2_comp(p_l[3], p_t[3]);
+}
+
+/* PRED_MODE_AVG_TL_T */
+static void inv_predict_8(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(p_tl[0], p_t[0]);
+ p[1] = avg2_comp(p_tl[1], p_t[1]);
+ p[2] = avg2_comp(p_tl[2], p_t[2]);
+ p[3] = avg2_comp(p_tl[3], p_t[3]);
+}
+
+/* PRED_MODE_AVG_T_TR */
+static void inv_predict_9(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(p_t[0], p_tr[0]);
+ p[1] = avg2_comp(p_t[1], p_tr[1]);
+ p[2] = avg2_comp(p_t[2], p_tr[2]);
+ p[3] = avg2_comp(p_t[3], p_tr[3]);
+}
+
+/* PRED_MODE_AVG_AVG_L_TL_AVG_T_TR */
+static void inv_predict_10(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = avg2_comp(avg2_comp(p_l[0], p_tl[0]), avg2_comp(p_t[0], p_tr[0]));
+ p[1] = avg2_comp(avg2_comp(p_l[1], p_tl[1]), avg2_comp(p_t[1], p_tr[1]));
+ p[2] = avg2_comp(avg2_comp(p_l[2], p_tl[2]), avg2_comp(p_t[2], p_tr[2]));
+ p[3] = avg2_comp(avg2_comp(p_l[3], p_tl[3]), avg2_comp(p_t[3], p_tr[3]));
+}
+
+/* PRED_MODE_SELECT */
+static void inv_predict_11(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ int diff = (abs(p_l[0] - p_tl[0]) - abs(p_t[0] - p_tl[0])) +
+ (abs(p_l[1] - p_tl[1]) - abs(p_t[1] - p_tl[1])) +
+ (abs(p_l[2] - p_tl[2]) - abs(p_t[2] - p_tl[2])) +
+ (abs(p_l[3] - p_tl[3]) - abs(p_t[3] - p_tl[3]));
+ if (diff <= 0)
+ AV_COPY32(p, p_t);
+ else
+ AV_COPY32(p, p_l);
+}
+
+/* PRED_MODE_ADD_SUBTRACT_FULL */
+static void inv_predict_12(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = clamp_add_subtract_full(p_l[0], p_t[0], p_tl[0]);
+ p[1] = clamp_add_subtract_full(p_l[1], p_t[1], p_tl[1]);
+ p[2] = clamp_add_subtract_full(p_l[2], p_t[2], p_tl[2]);
+ p[3] = clamp_add_subtract_full(p_l[3], p_t[3], p_tl[3]);
+}
+
+/* PRED_MODE_ADD_SUBTRACT_HALF */
+static void inv_predict_13(uint8_t *p, const uint8_t *p_l, const uint8_t *p_tl,
+ const uint8_t *p_t, const uint8_t *p_tr)
+{
+ p[0] = clamp_add_subtract_half(p_l[0], p_t[0], p_tl[0]);
+ p[1] = clamp_add_subtract_half(p_l[1], p_t[1], p_tl[1]);
+ p[2] = clamp_add_subtract_half(p_l[2], p_t[2], p_tl[2]);
+ p[3] = clamp_add_subtract_half(p_l[3], p_t[3], p_tl[3]);
+}
+
+typedef void (*inv_predict_func)(uint8_t *p, const uint8_t *p_l,
+ const uint8_t *p_tl, const uint8_t *p_t,
+ const uint8_t *p_tr);
+
+static const inv_predict_func inverse_predict[14] = {
+ inv_predict_0, inv_predict_1, inv_predict_2, inv_predict_3,
+ inv_predict_4, inv_predict_5, inv_predict_6, inv_predict_7,
+ inv_predict_8, inv_predict_9, inv_predict_10, inv_predict_11,
+ inv_predict_12, inv_predict_13,
+};
+
+static void inverse_prediction(AVFrame *frame, enum PredictionMode m, int x,
int y)
+{
+ uint8_t *dec, *p_l, *p_tl, *p_t, *p_tr;
+ uint8_t p[4];
+
+ dec = get_pixel(frame, x, y);
+ p_l = get_pixel(frame, x - 1, y);
+ p_tl = get_pixel(frame, x - 1, y - 1);
+ p_t = get_pixel(frame, x, y - 1);
+ p_tr = get_pixel(frame, x + 1, y - 1);
+
+ inverse_predict[m](p, p_l, p_tl, p_t, p_tr);
+
+ dec[0] = add_comp(dec[0], p[0]);
+ dec[1] = add_comp(dec[1], p[1]);
+ dec[2] = add_comp(dec[2], p[2]);
+ dec[3] = add_comp(dec[3], p[3]);
+}
+
+static int apply_predictor_transform(WebPContext *s)
+{
+ ImageContext *img = &s->image[IMAGE_ROLE_ARGB];
+ ImageContext *pimg = &s->image[IMAGE_ROLE_PREDICTOR];
+ int block_size = s->predictor_block_size;
+ int x, y;
+
+ for (y = 0; y < img->frame->height; y++) {
+ for (x = 0; x < img->frame->width; x++) {
+ int tx = x / block_size;
+ int ty = y / block_size;
+ uint8_t *p = get_pixel(pimg->frame, tx, ty);
+ enum PredictionMode m = p[2];
+
+ if (x == 0) {
+ if (y == 0)
+ m = PRED_MODE_BLACK;
+ else
+ m = PRED_MODE_T;
+ } else if (y == 0) {
+ m = PRED_MODE_L;
+ }
+
+ if (m > 13) {
+ av_log(s->avctx, AV_LOG_ERROR, "invalid predictor mode: %d\n",
m);
+ return AVERROR_INVALIDDATA;
+ }
+ inverse_prediction(img->frame, m, x, y);
+ }
+ }
+ return 0;
+}
+
+static av_always_inline int color_transform_delta(int color_pred, int color)
+{
+ return (color_pred * color) >> 5;
+}
+
+static int apply_color_transform(WebPContext *s)
+{
+ ImageContext *img, *cimg;
+ int x, y, cx, cy;
+ uint8_t *p, *cp;
+ int block_size = s->color_block_size;
+
+ img = &s->image[IMAGE_ROLE_ARGB];
+ cimg = &s->image[IMAGE_ROLE_COLOR_TRANSFORM];
+
+ for (y = 0; y < img->frame->height; y++) {
+ for (x = 0; x < img->frame->width; x++) {
+ alias8 green_to_red, green_to_blue, red_to_blue;
+ alias8 green, red;
+ int blue;
+
+ cx = x / block_size;
+ cy = y / block_size;
+ cp = get_pixel(cimg->frame, cx, cy);
+ red_to_blue.u8 = cp[1];
+ green_to_blue.u8 = cp[2];
+ green_to_red.u8 = cp[3];
+
+ p = get_pixel(img->frame, x, y);
+ red.u8 = p[1];
+ green.u8 = p[2];
+ blue = p[3];
+
+ red.u8 = add_comp(red.u8, color_transform_delta(green_to_red.s8,
green.s8));
+ blue += color_transform_delta(green_to_blue.s8,
green.s8);
+ blue = add_comp(blue, color_transform_delta(red_to_blue.s8,
red.s8));
+
+ p[1] = red.u8;
+ p[3] = blue;
+ }
+ }
+ return 0;
+}
+
+static int apply_subtract_green_transform(WebPContext *s)
+{
+ int x, y;
+ ImageContext *img = &s->image[IMAGE_ROLE_ARGB];
+
+ for (y = 0; y < img->frame->height; y++) {
+ for (x = 0; x < img->frame->width; x++) {
+ uint8_t *p = get_pixel(img->frame, x, y);
+ p[1] = add_comp(p[1], p[2]);
+ p[3] = add_comp(p[3], p[2]);
+ }
+ }
+ return 0;
+}
+
+static int apply_color_indexing_transform(WebPContext *s)
+{
+ ImageContext *img;
+ ImageContext *pal;
+ int i, x, y;
+ uint8_t *p, *pi;
+ int width_bits = s->color_index_width_bits;
+
+ img = &s->image[IMAGE_ROLE_ARGB];
+ pal = &s->image[IMAGE_ROLE_COLOR_INDEXING];
+
+ if (width_bits > 0) {
+ GetBitContext gb_g;
+ uint8_t *line;
+ int pixel_bits = 8 >> width_bits;
+
+ line = av_malloc(img->frame->linesize[0]);
+ if (!line)
+ return AVERROR(ENOMEM);
+
+ for (y = 0; y < img->frame->height; y++) {
+ p = get_pixel(img->frame, 0, y);
+ memcpy(line, p, img->frame->linesize[0]);
+ init_get_bits(&gb_g, line, img->frame->linesize[0] * 8);
+ skip_bits(&gb_g, 16);
+ i = 0;
+ for (x = 0; x < img->frame->width; x++) {
+ p = get_pixel(img->frame, x, y);
+ p[2] = get_bits(&gb_g, pixel_bits);
+ i++;
+ if (i == (1 << width_bits)) {
+ skip_bits(&gb_g, 24);
+ i = 0;
+ }
+ }
+ }
+ av_free(line);
+ }
+
+ for (y = 0; y < img->frame->height; y++) {
+ for (x = 0; x < img->frame->width; x++) {
+ p = get_pixel(img->frame, x, y);
+ i = p[2];
+ pi = get_pixel(pal->frame, i, 0);
+ if (!pi) {
+ av_log(s->avctx, AV_LOG_ERROR, "invalid palette index %d\n",
i);
+ return AVERROR_INVALIDDATA;
+ }
+ AV_COPY32(p, pi);
+ }
+ }
+
+ return 0;
+}
+
+static av_always_inline int color_cache_put(ImageContext *img, uint32_t c)
+{
+ int cache_idx = (0x1E35A7BD * c) >> (32 - img->color_cache_bits);
+ if (cache_idx >= (1 << img->color_cache_bits))
+ return AVERROR_BUG;
+ AV_WB32(&img->color_cache[4 * cache_idx], c);
+ return 0;
+}
+
+static int decode_entropy_coded_image(WebPContext *s, enum ImageRole role)
+{
+ ImageContext *img;
+ HuffmanGroup *hg;
+ int i, j, ret, x, y, width;
+
+ img = &s->image[role];
+
+ if (role == IMAGE_ROLE_ARGB) {
+ s->subtract_green = 0;
+ s->nb_transforms = 0;
+ ret = 0;
+ while (get_bits1(&s->gb)) {
+ enum TransformType transform = get_bits(&s->gb, 2);
+ s->transforms[s->nb_transforms++] = transform;
+ switch (transform) {
+ case PREDICTOR_TRANSFORM:
+ ret = parse_transform_predictor(s);
+ if (ret < 0)
+ return ret;
+ break;
+ case COLOR_TRANSFORM:
+ ret = parse_transform_color(s);
+ if (ret < 0)
+ return ret;
+ break;
+ case SUBTRACT_GREEN:
+ s->subtract_green = 1;
+ break;
+ case COLOR_INDEXING_TRANSFORM:
+ ret = parse_transform_color_indexing(s);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ }
+ }
+
+ if (get_bits1(&s->gb)) {
+ img->color_cache_bits = get_bits(&s->gb, 4);
+ if (img->color_cache_bits < 1 || img->color_cache_bits > 11) {
+ av_log(s->avctx, AV_LOG_ERROR, "invalid color cache bits: %d\n",
+ img->color_cache_bits);
+ return AVERROR_INVALIDDATA;
+ }
+ img->color_cache = av_mallocz_array(1 << img->color_cache_bits, 4);
+ if (!img->color_cache)
+ return AVERROR(ENOMEM);
+ } else {
+ img->color_cache_bits = 0;
+ }
+
+ img->num_huffman_groups = 1;
+ s->use_entropy_image = 0;
+ if (role == IMAGE_ROLE_ARGB && get_bits1(&s->gb)) {
+ ret = decode_entropy_image(s, img);
+ if (ret < 0)
+ return ret;
+ }
+ img->huffman_groups = av_mallocz_array(img->num_huffman_groups,
+ sizeof(*img->huffman_groups));
+ if (!img->huffman_groups)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < img->num_huffman_groups; i++) {
+ hg = &img->huffman_groups[i];
+ for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; j++) {
+ int alphabet_size = alphabet_sizes[j];
+ if (!j && img->color_cache_bits > 0)
+ alphabet_size += 1 << img->color_cache_bits;
+
+ if (get_bits1(&s->gb)) {
+ read_huffman_code_simple(s, &hg->huffman_code[j]);
+ } else {
+ ret = read_huffman_code_normal(s, &hg->huffman_code[j],
+ alphabet_size);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ width = img->frame->width;
+ if (role == IMAGE_ROLE_ARGB && s->color_index_width_bits > 0)
+ width = s->reduced_width;
+
+ x = 0; y = 0;
+ while (y < img->frame->height) {
+ int v;
+
+ hg = get_huffman_group(s, img, x, y);
+ v = huff_reader_get_symbol(&hg->huffman_code[HUFF_IDX_GREEN], &s->gb);
+ if (v < NUM_LITERAL_CODES) {
+ /* literal pixel values */
+ uint8_t *p = get_pixel(img->frame, x, y);
+ p[2] = v;
+ p[1] = huff_reader_get_symbol(&hg->huffman_code[HUFF_IDX_RED],
&s->gb);
+ p[3] = huff_reader_get_symbol(&hg->huffman_code[HUFF_IDX_BLUE],
&s->gb);
+ p[0] = huff_reader_get_symbol(&hg->huffman_code[HUFF_IDX_ALPHA],
&s->gb);
+ if (img->color_cache_bits) {
+ if ((ret = color_cache_put(img, AV_RB32(p))) < 0)
+ return ret;
+ }
+ x++;
+ if (x == width) {
+ x = 0;
+ y++;
+ }
+ } else if (v < NUM_LITERAL_CODES + NUM_LENGTH_CODES) {
+ /* LZ77 backwards mapping */
+ int prefix_code, length, distance, ref_x, ref_y;
+
+ /* parse length and distance */
+ prefix_code = v - NUM_LITERAL_CODES;
+ if (prefix_code < 4) {
+ length = prefix_code + 1;
+ } else {
+ int extra_bits = (prefix_code - 2) >> 1;
+ int offset = (2 + (prefix_code & 1)) << extra_bits;
+ length = offset + get_bits(&s->gb, extra_bits) + 1;
+ }
+ prefix_code =
huff_reader_get_symbol(&hg->huffman_code[HUFF_IDX_DIST], &s->gb);
+ if (prefix_code < 4) {
+ distance = prefix_code + 1;
+ } else {
+ int extra_bits = (prefix_code - 2) >> 1;
+ int offset = (2 + (prefix_code & 1)) << extra_bits;
+ distance = offset + get_bits(&s->gb, extra_bits) + 1;
+ }
+
+ /* find reference location */
+ if (distance <= NUM_SHORT_DISTANCES) {
+ int xi = lz77_distance_offsets[distance - 1][0];
+ int yi = lz77_distance_offsets[distance - 1][1];
+ distance = FFMAX(1, xi + yi * width);
+ } else {
+ distance -= NUM_SHORT_DISTANCES;
+ }
+ ref_x = x;
+ ref_y = y;
+ if (distance <= x) {
+ ref_x -= distance;
+ distance = 0;
+ } else {
+ ref_x = 0;
+ distance -= x;
+ }
+ while (distance >= width) {
+ ref_y--;
+ distance -= width;
+ }
+ if (distance > 0) {
+ ref_x = width - distance;
+ ref_y--;
+ }
+ ref_x = FFMAX(0, ref_x);
+ ref_y = FFMAX(0, ref_y);
+
+ /* copy pixels */
+ /* source and dest regions can overlap and wrap lines, so just
+ copy per-pixel */
+ for (i = 0; i < length; i++) {
+ uint8_t *p_ref = get_pixel(img->frame, ref_x, ref_y);
+ uint8_t *p = get_pixel(img->frame, x, y);
+
+ AV_COPY32(p, p_ref);
+ if (img->color_cache_bits) {
+ if ((ret = color_cache_put(img, AV_RB32(p))) < 0)
+ return ret;
+ }
+ x++;
+ ref_x++;
+ if (x == width) {
+ x = 0;
+ y++;
+ }
+ if (ref_x == width) {
+ ref_x = 0;
+ ref_y++;
+ }
+ if (y == img->frame->height || ref_y == img->frame->height)
+ break;
+ }
+ } else {
+ /* read from color cache */
+ uint8_t *p = get_pixel(img->frame, x, y);
+ int cache_idx = v - (NUM_LITERAL_CODES + NUM_LENGTH_CODES);
+
+ if (!img->color_cache_bits) {
+ av_log(s->avctx, AV_LOG_ERROR, "color cache not found\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (cache_idx >= (1 << img->color_cache_bits)) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "color cache index out-of-bounds\n");
+ return AVERROR_INVALIDDATA;
+ }
+ AV_COPY32(p, &img->color_cache[4 * cache_idx]);
+ x++;
+ if (x == width) {
+ x = 0;
+ y++;
+ }
+ }
+ }
+
+ if (role == IMAGE_ROLE_ARGB) {
+ for (i = s->nb_transforms - 1; i >= 0; i--) {
+ switch (s->transforms[i]) {
+ case PREDICTOR_TRANSFORM:
+ ret = apply_predictor_transform(s);
+ break;
+ case COLOR_TRANSFORM:
+ ret = apply_color_transform(s);
+ break;
+ case SUBTRACT_GREEN:
+ ret = apply_subtract_green_transform(s);
+ break;
+ case COLOR_INDEXING_TRANSFORM:
+ ret = apply_color_indexing_transform(s);
+ break;
+ }
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p,
int *got_frame, uint8_t *data_start,
unsigned int data_size)
{
WebPContext *s = avctx->priv_data;
- int ret;
+ ImageContext *img;
+ int w, h, ret, i;
+ s->lossless = 1;
avctx->pix_fmt = AV_PIX_FMT_ARGB;
- /* use dummy dimensions for now */
- if (s->width)
- avctx->width = s->width;
- else
- avctx->width = 800;
- if (s->height)
- avctx->height = s->height;
- else
- avctx->height = 600;
+ ret = init_get_bits(&s->gb, data_start, data_size * 8);
+ if (ret < 0)
+ return ret;
+
+ if (get_bits(&s->gb, 8) != 0x2F) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid WebP Lossless signature\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ w = get_bits(&s->gb, 14) + 1;
+ h = get_bits(&s->gb, 14) + 1;
+ if (s->width && s->width != w) {
+ av_log(avctx, AV_LOG_WARNING, "Width mismatch. %d != %d\n",
+ s->width, w);
+ }
+ s->width = w;
+ if (s->height && s->height != h) {
+ av_log(avctx, AV_LOG_WARNING, "Height mismatch. %d != %d\n",
+ s->width, w);
+ }
+ s->height = h;
+ avcodec_set_dimensions(avctx, s->width, s->height);
+
+ s->has_alpha = get_bits1(&s->gb);
+
+ if (get_bits(&s->gb, 3) != 0x0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid WebP Lossless version\n");
+ return AVERROR_INVALIDDATA;
+ }
if ((ret = ff_get_buffer(avctx, p, 0)) < 0) {
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
@@ -64,6 +1198,23 @@ static int vp8_lossless_decode_frame(AVCodecContext
*avctx, AVFrame *p,
}
memset(p->data[0], 0, p->linesize[0] * avctx->height);
+ img = &s->image[IMAGE_ROLE_ARGB];
+ img->role = IMAGE_ROLE_ARGB;
+ img->frame = p;
+
+ ret = decode_entropy_coded_image(s, img->role);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < IMAGE_ROLE_NB; i++)
+ image_ctx_free(&s->image[i]);
+ s->predictor_block_size = 0;
+ s->color_block_size = 0;
+ s->color_index_width_bits = 0;
+ s->reduced_width = 0;
+ s->use_entropy_image = 0;
+ s->entropy_size = 0;
+
*got_frame = 1;
p->pict_type = AV_PICTURE_TYPE_I;
p->key_frame = 1;
@@ -85,6 +1236,7 @@ static int vp8_lossy_decode_frame(AVCodecContext *avctx,
AVFrame *p,
if (s->has_alpha)
avctx->pix_fmt = AV_PIX_FMT_YUVA420P;
}
+ s->lossless = 0;
if (data_size > INT_MAX) {
av_log(avctx, AV_LOG_ERROR, "unsupported chunk size\n");
@@ -115,6 +1267,7 @@ static int webp_decode_frame(AVCodecContext *avctx, void
*data, int *got_frame,
unsigned int chunk_type, chunk_size;
uint8_t vp8x_flags = 0;
+ s->avctx = avctx;
s->width = 0;
s->height = 0;
*got_frame = 0;
--
1.8.1.2
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel