static int decode_entropy_coded_image(WebPContext *s, enum ImageRole role,

                                      int w, int h)

{

    ImageContext *img;

    HuffReader *hg;

    int i, j, ret, x, y, width;



    img       = &s->image[role];

    img->role = role;



    if (!img->frame) {

        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;



    if (role == IMAGE_ROLE_ARGB && !img->is_alpha_primary) {

        ThreadFrame pt = { .f = img->frame };

        ret = ff_thread_get_buffer(s->avctx, &pt, 0);

    } else

        ret = av_frame_get_buffer(img->frame, 1);

    if (ret < 0)

        return ret;



    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,

                                            sizeof(*img->color_cache));

        if (!img->color_cache)

            return AVERROR(ENOMEM);

    } else {

        img->color_cache_bits = 0;

    }



    img->nb_huffman_groups = 1;

    if (role == IMAGE_ROLE_ARGB && get_bits1(&s->gb)) {

        ret = decode_entropy_image(s);

        if (ret < 0)

            return ret;

        img->nb_huffman_groups = s->nb_huffman_groups;

    }

    img->huffman_groups = av_mallocz_array(img->nb_huffman_groups *

                                           HUFFMAN_CODES_PER_META_CODE,

                                           sizeof(*img->huffman_groups));

    if (!img->huffman_groups)

        return AVERROR(ENOMEM);



    for (i = 0; i < img->nb_huffman_groups; i++) {

        hg = &img->huffman_groups[i * HUFFMAN_CODES_PER_META_CODE];

        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[j]);

            } else {

                ret = read_huffman_code_normal(s, &hg[j], alphabet_size);

                if (ret < 0)

                    return ret;

            }

        }

    }



    width = img->frame->width;

    if (role == IMAGE_ROLE_ARGB && s->reduced_width > 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[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[HUFF_IDX_RED],   &s->gb);

            p[3] = huff_reader_get_symbol(&hg[HUFF_IDX_BLUE],  &s->gb);

            p[0] = huff_reader_get_symbol(&hg[HUFF_IDX_ALPHA], &s->gb);

            if (img->color_cache_bits)

                color_cache_put(img, AV_RB32(p));

            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[HUFF_IDX_DIST], &s->gb);

            if (prefix_code > 39) {

                av_log(s->avctx, AV_LOG_ERROR,

                       "distance prefix code too large: %d\n", prefix_code);

                return AVERROR_INVALIDDATA;

            }

            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)

                    color_cache_put(img, AV_RB32(p));

                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_WB32(p, img->color_cache[cache_idx]);

            x++;

            if (x == width) {

                x = 0;

                y++;

            }

        }

    }



    return 0;

}
