diff options
Diffstat (limited to 'libavcodec/webp.c')
-rw-r--r-- | libavcodec/webp.c | 97 |
1 files changed, 78 insertions, 19 deletions
diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 62f35f7..47e9e9e 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -3,20 +3,20 @@ * Copyright (c) 2013 Aneesh Dogra <aneesh@sugarlabs.org> * Copyright (c) 2013 Justin Ruggles <justin.ruggles@gmail.com> * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -31,16 +31,20 @@ * Lossless decoder * Compressed alpha for lossy * + * @author James Almer <jamrial@gmail.com> + * Exif metadata + * * Unimplemented: * - Animation * - ICC profile - * - Exif and XMP metadata + * - XMP metadata */ #define BITSTREAM_READER_LE #include "libavutil/imgutils.h" #include "avcodec.h" #include "bytestream.h" +#include "exif.h" #include "internal.h" #include "get_bits.h" #include "thread.h" @@ -191,6 +195,8 @@ typedef struct WebPContext { enum AlphaFilter alpha_filter; /* filtering method for alpha chunk */ uint8_t *alpha_data; /* alpha chunk data */ int alpha_data_size; /* alpha chunk data size */ + int has_exif; /* set after an EXIF chunk has been processed */ + AVDictionary *exif_metadata; /* EXIF chunk data */ int width; /* image width */ int height; /* image height */ int lossless; /* indicates lossless or lossy */ @@ -303,7 +309,7 @@ static int huff_reader_build_canonical(HuffReader *r, int *code_lengths, if (max_code_length == 0 || max_code_length > MAX_HUFFMAN_CODE_LENGTH) return AVERROR(EINVAL); - codes = av_malloc(alphabet_size * sizeof(*codes)); + codes = av_malloc_array(alphabet_size, sizeof(*codes)); if (!codes) return AVERROR(ENOMEM); @@ -1027,7 +1033,7 @@ static int apply_color_indexing_transform(WebPContext *s) ImageContext *img; ImageContext *pal; int i, x, y; - uint8_t *p, *pi; + uint8_t *p; img = &s->image[IMAGE_ROLE_ARGB]; pal = &s->image[IMAGE_ROLE_COLOR_INDEXING]; @@ -1060,16 +1066,33 @@ static int apply_color_indexing_transform(WebPContext *s) 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]; - if (i >= pal->frame->width) { - av_log(s->avctx, AV_LOG_ERROR, "invalid palette index %d\n", i); - return AVERROR_INVALIDDATA; + // switch to local palette if it's worth initializing it + if (img->frame->height * img->frame->width > 300) { + uint8_t palette[256 * 4]; + const int size = pal->frame->width * 4; + av_assert0(size <= 1024U); + memcpy(palette, GET_PIXEL(pal->frame, 0, 0), size); // copy palette + // set extra entries to transparent black + memset(palette + size, 0, 256 * 4 - size); + 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]; + AV_COPY32(p, &palette[i * 4]); + } + } + } else { + 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]; + if (i >= pal->frame->width) { + AV_WB32(p, 0x00000000); + } else { + const uint8_t *pi = GET_PIXEL(pal->frame, i, 0); + AV_COPY32(p, pi); + } } - pi = GET_PIXEL(pal->frame, i, 0); - AV_COPY32(p, pi); } } @@ -1088,7 +1111,7 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, avctx->pix_fmt = AV_PIX_FMT_ARGB; } - ret = init_get_bits(&s->gb, data_start, data_size * 8); + ret = init_get_bits8(&s->gb, data_start, data_size); if (ret < 0) return ret; @@ -1134,7 +1157,6 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, used = 0; while (get_bits1(&s->gb)) { enum TransformType transform = get_bits(&s->gb, 2); - s->transforms[s->nb_transforms++] = transform; if (used & (1 << transform)) { av_log(avctx, AV_LOG_ERROR, "Transform %d used more than once\n", transform); @@ -1142,6 +1164,7 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, goto free_and_return; } used |= (1 << transform); + s->transforms[s->nb_transforms++] = transform; switch (transform) { case PREDICTOR_TRANSFORM: ret = parse_transform_predictor(s); @@ -1343,6 +1366,7 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, s->height = 0; *got_frame = 0; s->has_alpha = 0; + s->has_exif = 0; bytestream2_init(&gb, avpkt->data, avpkt->size); if (bytestream2_get_bytes_left(&gb) < 12) @@ -1362,6 +1386,7 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; } + av_dict_free(&s->exif_metadata); while (bytestream2_get_bytes_left(&gb) > 0) { char chunk_str[5] = { 0 }; @@ -1435,10 +1460,44 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, break; } + case MKTAG('E', 'X', 'I', 'F'): { + int le, ifd_offset, exif_offset = bytestream2_tell(&gb); + GetByteContext exif_gb; + + if (s->has_exif) { + av_log(avctx, AV_LOG_VERBOSE, "Ignoring extra EXIF chunk\n"); + goto exif_end; + } + if (!(vp8x_flags & VP8X_FLAG_EXIF_METADATA)) + av_log(avctx, AV_LOG_WARNING, + "EXIF chunk present, but Exif bit not set in the " + "VP8X header\n"); + + s->has_exif = 1; + bytestream2_init(&exif_gb, avpkt->data + exif_offset, + avpkt->size - exif_offset); + if (ff_tdecode_header(&exif_gb, &le, &ifd_offset) < 0) { + av_log(avctx, AV_LOG_ERROR, "invalid TIFF header " + "in Exif data\n"); + goto exif_end; + } + + bytestream2_seek(&exif_gb, ifd_offset, SEEK_SET); + if (avpriv_exif_decode_ifd(avctx, &exif_gb, le, 0, &s->exif_metadata) < 0) { + av_log(avctx, AV_LOG_ERROR, "error decoding Exif data\n"); + goto exif_end; + } + + av_dict_copy(avpriv_frame_get_metadatap(data), s->exif_metadata, 0); + +exif_end: + av_dict_free(&s->exif_metadata); + bytestream2_skip(&gb, chunk_size); + break; + } case MKTAG('I', 'C', 'C', 'P'): case MKTAG('A', 'N', 'I', 'M'): case MKTAG('A', 'N', 'M', 'F'): - case MKTAG('E', 'X', 'I', 'F'): case MKTAG('X', 'M', 'P', ' '): AV_WL32(chunk_str, chunk_type); av_log(avctx, AV_LOG_VERBOSE, "skipping unsupported chunk: %s\n", |