libavcodec/gif.c
93481fe5
 /*
  * GIF encoder.
406792e7
  * Copyright (c) 2000 Fabrice Bellard
  * Copyright (c) 2002 Francois Revol
  * Copyright (c) 2006 Baptiste Coudurier
93481fe5
  *
  * This file is part of FFmpeg.
  *
  * 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.
  *
  * 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 FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 /*
  * First version by Francois Revol revol@free.fr
  *
  * Features and limitations:
  * - currently no compression is performed,
  *   in fact the size of the data is 9/8 the size of the image in 8bpp
  * - uses only a global standard palette
  * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
  *
  * Reference documents:
  * http://www.goice.co.jp/member/mo/formats/gif.html
  * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
  * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
  *
  * this url claims to have an LZW algorithm not covered by Unisys patent:
  * http://www.msg.net/utility/whirlgif/gifencod.html
  * could help reduce the size of the files _a lot_...
  * some sites mentions an RLE type compression also.
  */
 
 #include "avcodec.h"
 #include "bytestream.h"
f38e4507
 #include "lzw.h"
6d32ec6c
 
 /* The GIF format uses reversed order for bitstreams... */
 /* at least they don't use PDP_ENDIAN :) */
 #define BITSTREAM_WRITER_LE
 
b2755007
 #include "put_bits.h"
93481fe5
 
e4e8632a
 typedef struct {
     AVFrame picture;
f38e4507
     LZWState *lzw;
     uint8_t *buf;
e4e8632a
 } GIFContext;
 
93481fe5
 /* GIF header */
ab3faa8b
 static int gif_image_write_header(AVCodecContext *avctx,
                                   uint8_t **bytestream, uint32_t *palette)
93481fe5
 {
     int i;
     unsigned int v;
 
     bytestream_put_buffer(bytestream, "GIF", 3);
     bytestream_put_buffer(bytestream, "89a", 3);
ab3faa8b
     bytestream_put_le16(bytestream, avctx->width);
     bytestream_put_le16(bytestream, avctx->height);
93481fe5
 
     bytestream_put_byte(bytestream, 0xf7); /* flags: global clut, 256 entries */
     bytestream_put_byte(bytestream, 0x1f); /* background color index */
     bytestream_put_byte(bytestream, 0); /* aspect ratio */
 
     /* the global palette */
dacfaf23
     for(i=0;i<256;i++) {
         v = palette[i];
         bytestream_put_be24(bytestream, v);
93481fe5
     }
 
     return 0;
 }
 
f38e4507
 static int gif_image_write_image(AVCodecContext *avctx,
                                  uint8_t **bytestream, uint8_t *end,
af7436ae
                                  const uint8_t *buf, int linesize)
93481fe5
 {
f38e4507
     GIFContext *s = avctx->priv_data;
c56e7130
     int len = 0, height;
93481fe5
     const uint8_t *ptr;
     /* image block */
 
     bytestream_put_byte(bytestream, 0x2c);
cc26bf0d
     bytestream_put_le16(bytestream, 0);
     bytestream_put_le16(bytestream, 0);
ab3faa8b
     bytestream_put_le16(bytestream, avctx->width);
     bytestream_put_le16(bytestream, avctx->height);
93481fe5
     bytestream_put_byte(bytestream, 0x00); /* flags */
     /* no local clut */
 
     bytestream_put_byte(bytestream, 0x08);
 
f38e4507
     ff_lzw_encode_init(s->lzw, s->buf, avctx->width*avctx->height,
                        12, FF_LZW_GIF, put_bits);
93481fe5
 
     ptr = buf;
f38e4507
     for (height = avctx->height; height--;) {
         len += ff_lzw_encode(s->lzw, ptr, avctx->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;
93481fe5
     }
     bytestream_put_byte(bytestream, 0x00); /* end of image block */
     bytestream_put_byte(bytestream, 0x3b);
     return 0;
 }
 
98a6fff9
 static av_cold int gif_encode_init(AVCodecContext *avctx)
93481fe5
 {
     GIFContext *s = avctx->priv_data;
 
     avctx->coded_frame = &s->picture;
f38e4507
     s->lzw = av_mallocz(ff_lzw_encode_state_size);
     if (!s->lzw)
2874c81c
         return AVERROR(ENOMEM);
f38e4507
     s->buf = av_malloc(avctx->width*avctx->height*2);
     if (!s->buf)
2874c81c
          return AVERROR(ENOMEM);
93481fe5
     return 0;
 }
 
 /* better than nothing gif encoder */
 static int gif_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, int buf_size, void *data)
 {
     GIFContext *s = avctx->priv_data;
     AVFrame *pict = data;
     AVFrame *const p = (AVFrame *)&s->picture;
     uint8_t *outbuf_ptr = outbuf;
f38e4507
     uint8_t *end = outbuf + buf_size;
93481fe5
 
     *p = *pict;
ce5e49b0
     p->pict_type = AV_PICTURE_TYPE_I;
93481fe5
     p->key_frame = 1;
ab3faa8b
     gif_image_write_header(avctx, &outbuf_ptr, (uint32_t *)pict->data[1]);
f38e4507
     gif_image_write_image(avctx, &outbuf_ptr, end, pict->data[0], pict->linesize[0]);
93481fe5
     return outbuf_ptr - outbuf;
 }
 
f38e4507
 static int gif_encode_close(AVCodecContext *avctx)
 {
     GIFContext *s = avctx->priv_data;
 
     av_freep(&s->lzw);
     av_freep(&s->buf);
     return 0;
 }
 
e7e2df27
 AVCodec ff_gif_encoder = {
93481fe5
     "gif",
72415b2a
     AVMEDIA_TYPE_VIDEO,
93481fe5
     CODEC_ID_GIF,
     sizeof(GIFContext),
     gif_encode_init,
     gif_encode_frame,
f38e4507
     gif_encode_close,
2ba83017
     .pix_fmts= (const enum PixelFormat[]){PIX_FMT_RGB8, PIX_FMT_BGR8, PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE, PIX_FMT_GRAY8, PIX_FMT_PAL8, PIX_FMT_NONE},
fe4bf374
     .long_name= NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"),
93481fe5
 };