---
 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

Reply via email to