From: Bjorn Roche <bj...@xowave.com> --- libavcodec/avcodec.h | 6 ++ libavcodec/gif.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++-- libavformat/gif.c | 16 ++++- 3 files changed, 193 insertions(+), 6 deletions(-)
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 5c84974e03..3c64e8f7ee 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1599,6 +1599,12 @@ enum AVPacketSideDataType { */ AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + /** + * The disposal method that should be used with the frame. If missing, + * the frame will not be disposed. This contains exactly one byte. + */ + AV_PKT_DATA_GIF_FRAME_DISPOSAL, + /** * The number of side data elements (in fact a bit more than it). * This is not part of the public API/ABI in the sense that it may diff --git a/libavcodec/gif.c b/libavcodec/gif.c index d9c99d52cf..db2193a718 100644 --- a/libavcodec/gif.c +++ b/libavcodec/gif.c @@ -74,11 +74,36 @@ static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h) return -1; } -static int gif_image_write_image(AVCodecContext *avctx, - uint8_t **bytestream, uint8_t *end, - const uint32_t *palette, - const uint8_t *buf, const int linesize, - AVPacket *pkt) +// returns true if any of the pixels are transparent +static int is_image_translucent(AVCodecContext *avctx, + const uint32_t *palette, + const uint8_t *buf, const int linesize) +{ + GIFContext *s = avctx->priv_data; + int trans = s->transparent_index; + int p; + const int m = avctx->width * avctx->height ; + + if( trans < 0 ) { + return 0; + } + + // FIXME: this might be faster with strchr + for( p=0; p<m; ++p ) { + if( buf[p] == trans ) { + return 1; + } + } + return 0; +} + +// writes an opaque image. ie an image with no transparency. +// it also works, and should be used, for a first image. +static int gif_image_write_opaque(AVCodecContext *avctx, + uint8_t **bytestream, uint8_t *end, + const uint32_t *palette, + const uint8_t *buf, const int linesize, + AVPacket *pkt) { GIFContext *s = avctx->priv_data; int len = 0, height = avctx->height, width = avctx->width, x, y; @@ -86,6 +111,7 @@ static int gif_image_write_image(AVCodecContext *avctx, int honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette; const uint8_t *ptr; + /* Crop image */ if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) { const uint8_t *ref = s->last_frame->data[0]; @@ -137,6 +163,11 @@ static int gif_image_write_image(AVCodecContext *avctx, width, height, x_start, y_start, avctx->width, avctx->height); } + uint8_t *frame_disposal = av_packet_new_side_data(pkt, AV_PKT_DATA_GIF_FRAME_DISPOSAL, 1); + if (!frame_disposal) + return AVERROR(ENOMEM); + *frame_disposal = GCE_DISPOSAL_INPLACE; + /* image block */ bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR); bytestream_put_le16(bytestream, x_start); @@ -214,6 +245,142 @@ static int gif_image_write_image(AVCodecContext *avctx, return 0; } +// wrtites an image that may contain transparency +// this might work for opaque images as well, but will be less optimized. +static int gif_image_write_translucent(AVCodecContext *avctx, + uint8_t **bytestream, uint8_t *end, + const uint32_t *palette, + const uint8_t *buf, const int linesize, + AVPacket *pkt) +{ + GIFContext *s = avctx->priv_data; + int len = 0, height = avctx->height, width = avctx->width, x, y; + int x_start = 0, y_start = 0, trans = s->transparent_index; + int honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette; + const uint8_t *ptr; + + // /* Crop image */ + // if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) { + // const uint8_t *ref = s->last_frame->data[0]; + // const int ref_linesize = s->last_frame->linesize[0]; + // int x_end = avctx->width - 1, + // y_end = avctx->height - 1; + + // /* skip common lines */ + // while (y_start < y_end) { + // if (memcmp(ref + y_start*ref_linesize, buf + y_start*linesize, width)) + // break; + // y_start++; + // } + // while (y_end > y_start) { + // if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, width)) + // break; + // y_end--; + // } + // height = y_end + 1 - y_start; + + // /* skip common columns */ + // while (x_start < x_end) { + // int same_column = 1; + // for (y = y_start; y <= y_end; y++) { + // if (ref[y*ref_linesize + x_start] != buf[y*linesize + x_start]) { + // same_column = 0; + // break; + // } + // } + // if (!same_column) + // break; + // x_start++; + // } + // while (x_end > x_start) { + // int same_column = 1; + // for (y = y_start; y <= y_end; y++) { + // if (ref[y*ref_linesize + x_end] != buf[y*linesize + x_end]) { + // same_column = 0; + // break; + // } + // } + // if (!same_column) + // break; + // x_end--; + // } + // width = x_end + 1 - x_start; + + // av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n", + // width, height, x_start, y_start, avctx->width, avctx->height); + // } + + + uint8_t *frame_disposal = av_packet_new_side_data(pkt, AV_PKT_DATA_GIF_FRAME_DISPOSAL, 1); + if (!frame_disposal) + return AVERROR(ENOMEM); + *frame_disposal = GCE_DISPOSAL_BACKGROUND; + + /* image block */ + bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR); + bytestream_put_le16(bytestream, x_start); + bytestream_put_le16(bytestream, y_start); + bytestream_put_le16(bytestream, width); + bytestream_put_le16(bytestream, height); + + if (!palette) { + bytestream_put_byte(bytestream, 0x00); /* flags */ + } else { + unsigned i; + bytestream_put_byte(bytestream, 1<<7 | 0x7); /* flags */ + for (i = 0; i < AVPALETTE_COUNT; i++) { + const uint32_t v = palette[i]; + bytestream_put_be24(bytestream, v); + } + } + + bytestream_put_byte(bytestream, 0x08); + + ff_lzw_encode_init(s->lzw, s->buf, s->buf_size, + 12, FF_LZW_GIF, put_bits); + + ptr = buf + y_start*linesize + x_start; + + for (y = 0; y < height; y++) { + len += ff_lzw_encode(s->lzw, ptr, width); + ptr += linesize; + } + + len += ff_lzw_encode_flush(s->lzw, flush_put_bits); + + ptr = s->buf; + while (len > 0) { + int size = FFMIN(255, len); + bytestream_put_byte(bytestream, size); + if (end - *bytestream < size) + return -1; + bytestream_put_buffer(bytestream, ptr, size); + ptr += size; + len -= size; + } + bytestream_put_byte(bytestream, 0x00); /* end of image block */ + + return 0; +} + +static int gif_image_write_image(AVCodecContext *avctx, + uint8_t **bytestream, uint8_t *end, + const uint32_t *palette, + const uint8_t *buf, const int linesize, + AVPacket *pkt) +{ + GIFContext *s = avctx->priv_data; + if( !s->last_frame ) { + return gif_image_write_opaque(avctx, bytestream, end, palette, buf, linesize, pkt); + } + + if( is_image_translucent(avctx, palette, buf, linesize ) ) { + return gif_image_write_translucent(avctx, bytestream, end, palette, buf, linesize, pkt); + } else { + return gif_image_write_opaque(avctx, bytestream, end, palette, buf, linesize, pkt); + } +} + static av_cold int gif_encode_init(AVCodecContext *avctx) { GIFContext *s = avctx->priv_data; diff --git a/libavformat/gif.c b/libavformat/gif.c index 91cd40db5c..853e84430e 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -144,6 +144,8 @@ static int flush_packet(AVFormatContext *s, AVPacket *new) AVIOContext *pb = s->pb; const uint32_t *palette; AVPacket *pkt = gif->prev_pkt; + uint8_t *disposal; + uint8_t packed; if (!pkt) return 0; @@ -157,16 +159,28 @@ static int flush_packet(AVFormatContext *s, AVPacket *new) } bcid = get_palette_transparency_index(palette); + disposal = av_packet_get_side_data(pkt, AV_PKT_DATA_GIF_FRAME_DISPOSAL, &size); + if( disposal && size != 1 ) { + av_log(s, AV_LOG_ERROR, "Invalid gif frame disposal extradata\n"); + return AVERROR_INVALIDDATA; + } + if (new && new->pts != AV_NOPTS_VALUE) gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts); else if (!new && gif->last_delay >= 0) gif->duration = gif->last_delay; /* graphic control extension block */ + if( disposal ) { + packed = (0xff & (*disposal)<<2) | (bcid >= 0); + } else { + packed = 1<<2 | (bcid >= 0); + } + //FIXME: if disposal == 2, make sure backgrdoun color is specified appropriately. avio_w8(pb, 0x21); avio_w8(pb, 0xf9); avio_w8(pb, 0x04); /* block size */ - avio_w8(pb, 1<<2 | (bcid >= 0)); + avio_w8(pb, packed); avio_wl16(pb, gif->duration); avio_w8(pb, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); avio_w8(pb, 0x00); -- 2.14.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel