On Fri, Feb 5, 2021 at 12:57 AM Derek Buitenhuis <derek.buitenh...@gmail.com> wrote:
> GIF palette entries are not compressed, and writing 256 entries, > which can be up to every frame, uses a significant amount of > space, especially in extreme cases, where palettes can be very > small. > > Example, first six seconds of Tears of Steel, palette generated > with libimagequant, 320x240 resolution, and with transparency > optimization + per frame palette: > > * Before patch: 186765 bytes > * After patch: 77901 bytes > > Signed-off-by: Derek Buitenhuis <derek.buitenh...@gmail.com> > --- > libavcodec/gif.c | 81 +++++++++++++++++++++++++++++++++++++++++------- > 1 file changed, 69 insertions(+), 12 deletions(-) > > diff --git a/libavcodec/gif.c b/libavcodec/gif.c > index de41992851..c52db57edd 100644 > --- a/libavcodec/gif.c > +++ b/libavcodec/gif.c > @@ -52,6 +52,7 @@ typedef struct GIFContext { > int flags; > int image; > uint32_t palette[AVPALETTE_COUNT]; ///< local reference palette for > !pal8 > + size_t palette_count; > int palette_loaded; > int transparent_index; > uint8_t *tmpl; ///< temporary line buffer > @@ -62,6 +63,27 @@ enum { > GF_TRANSDIFF = 1<<1, > }; > > +static void shrink_palette(const uint32_t *src, uint32_t *dst, size_t > *palette_count) > +{ > + size_t colors_seen = 0; > + > + for (size_t i = 0; i < AVPALETTE_COUNT; i++) { > + int seen = 0; > + for (size_t c = 0; c < colors_seen; c++) { > + if (src[i] == dst[c]) { > + seen = 1; > + break; > + } > + } > + if (!seen) { > + dst[colors_seen] = src[i]; > + colors_seen++; > + } > + } > + > + *palette_count = colors_seen; > +} > + > static int is_image_translucent(AVCodecContext *avctx, > const uint8_t *buf, const int linesize) > { > @@ -83,7 +105,7 @@ static int is_image_translucent(AVCodecContext *avctx, > return 0; > } > > -static int get_palette_transparency_index(const uint32_t *palette) > +static int get_palette_transparency_index(const uint32_t *palette, int > palette_count) > { > int transparent_color_index = -1; > unsigned i, smallest_alpha = 0xff; > @@ -91,7 +113,7 @@ static int get_palette_transparency_index(const > uint32_t *palette) > if (!palette) > return -1; > > - for (i = 0; i < AVPALETTE_COUNT; i++) { > + for (i = 0; i < palette_count; i++) { > const uint32_t v = palette[i]; > if (v >> 24 < smallest_alpha) { > smallest_alpha = v >> 24; > @@ -266,6 +288,10 @@ static int gif_image_write_image(AVCodecContext > *avctx, > int x_start = 0, y_start = 0, trans = s->transparent_index; > int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && > s->last_frame && !palette; > const uint8_t *ptr; > + uint32_t shrunk_palette[AVPALETTE_COUNT] = { 0 }; > + size_t shrunk_palette_count; > + > + memset(shrunk_palette, 0xff, AVPALETTE_SIZE); This will memset each frame. Could we avoid it? Is memset memory between [global_palette_count, 1<<( av_log2(global_palette_count - 1) + 1)] enough? > if (!s->image && is_image_translucent(avctx, buf, linesize)) { > gif_crop_translucent(avctx, buf, linesize, &width, &height, > &x_start, &y_start); > @@ -277,10 +303,21 @@ static int gif_image_write_image(AVCodecContext > *avctx, > } > > if (s->image || !avctx->frame_number) { /* GIF header */ > - const uint32_t *global_palette = palette ? palette : s->palette; > + uint32_t *global_palette; > + uint32_t shrunk_global_palette[AVPALETTE_COUNT]; > + size_t global_palette_count; > + unsigned pow2_global_palette_count; > const AVRational sar = avctx->sample_aspect_ratio; > int64_t aspect = 0; > > + if (palette) { > + shrink_palette(palette, shrunk_global_palette, > &global_palette_count); > + global_palette = shrunk_global_palette; > + } else { > + global_palette = s->palette; > + global_palette_count = s->palette_count; > + } > + > if (sar.num > 0 && sar.den > 0) { > aspect = sar.num * 64LL / sar.den - 15; > if (aspect < 0 || aspect > 255) > @@ -291,17 +328,22 @@ static int gif_image_write_image(AVCodecContext > *avctx, > bytestream_put_le16(bytestream, avctx->width); > bytestream_put_le16(bytestream, avctx->height); > > - bcid = get_palette_transparency_index(global_palette); > + bcid = get_palette_transparency_index(global_palette, > global_palette_count); > > - bytestream_put_byte(bytestream, 0xf7); /* flags: global clut, 256 > entries */ > + pow2_global_palette_count = av_log2(global_palette_count - 1); > + > + bytestream_put_byte(bytestream, 0xf0 | > pow2_global_palette_count); /* flags: global clut, 256 entries */ > The comment may be misleading, it's not 256 anymore. > bytestream_put_byte(bytestream, bcid < 0 ? > DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */ > > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".