]> git.sesse.net Git - ffmpeg/blob - libavcodec/gif.c
0a84d2ab4cc14b17bcc6da664697980ef17d7def
[ffmpeg] / libavcodec / gif.c
1 /*
2  * GIF encoder.
3  * Copyright (c) 2000 Fabrice Bellard
4  * Copyright (c) 2002 Francois Revol
5  * Copyright (c) 2006 Baptiste Coudurier
6  *
7  * This file is part of FFmpeg.
8  *
9  * FFmpeg is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * FFmpeg is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with FFmpeg; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23
24 /*
25  * First version by Francois Revol revol@free.fr
26  *
27  * Features and limitations:
28  * - currently no compression is performed,
29  *   in fact the size of the data is 9/8 the size of the image in 8bpp
30  * - uses only a global standard palette
31  * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
32  *
33  * Reference documents:
34  * http://www.goice.co.jp/member/mo/formats/gif.html
35  * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
36  * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
37  *
38  * this url claims to have an LZW algorithm not covered by Unisys patent:
39  * http://www.msg.net/utility/whirlgif/gifencod.html
40  * could help reduce the size of the files _a lot_...
41  * some sites mentions an RLE type compression also.
42  */
43
44 #include "avcodec.h"
45 #include "bytestream.h"
46
47 /* The GIF format uses reversed order for bitstreams... */
48 /* at least they don't use PDP_ENDIAN :) */
49 #define BITSTREAM_WRITER_LE
50
51 #include "bitstream.h"
52
53 /* bitstream minipacket size */
54 #define GIF_CHUNKS 100
55
56 typedef struct {
57     unsigned char r;
58     unsigned char g;
59     unsigned char b;
60 } rgb_triplet;
61
62 /* we use the standard 216 color palette */
63
64 /* this script was used to create the palette:
65  * for r in 00 33 66 99 cc ff; do for g in 00 33 66 99 cc ff; do echo -n "    "; for b in 00 33 66 99 cc ff; do
66  *   echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done
67  */
68
69 static const rgb_triplet gif_clut[216] = {
70     { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
71     { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
72     { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
73     { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
74     { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
75     { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
76     { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
77     { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
78     { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
79     { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
80     { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
81     { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
82     { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
83     { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
84     { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
85     { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
86     { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
87     { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
88     { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
89     { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
90     { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
91     { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
92     { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
93     { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
94     { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
95     { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
96     { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
97     { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
98     { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
99     { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
100     { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
101     { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
102     { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
103     { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
104     { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
105     { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
106 };
107
108 /* GIF header */
109 static int gif_image_write_header(uint8_t **bytestream,
110                                   int width, int height,
111                                   uint32_t *palette)
112 {
113     int i;
114     unsigned int v;
115
116     bytestream_put_buffer(bytestream, "GIF", 3);
117     bytestream_put_buffer(bytestream, "89a", 3);
118     bytestream_put_le16(bytestream, width);
119     bytestream_put_le16(bytestream, height);
120
121     bytestream_put_byte(bytestream, 0xf7); /* flags: global clut, 256 entries */
122     bytestream_put_byte(bytestream, 0x1f); /* background color index */
123     bytestream_put_byte(bytestream, 0); /* aspect ratio */
124
125     /* the global palette */
126     if (!palette) {
127         bytestream_put_buffer(bytestream, (const unsigned char *)gif_clut, 216*3);
128         for(i=0;i<((256-216)*3);i++)
129             bytestream_put_byte(bytestream, 0);
130     } else {
131         for(i=0;i<256;i++) {
132             v = palette[i];
133             bytestream_put_be24(bytestream, v);
134         }
135     }
136
137     return 0;
138 }
139
140 /* this is maybe slow, but allows for extensions */
141 static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b)
142 {
143     return ((((r)/47)%6)*6*6+(((g)/47)%6)*6+(((b)/47)%6));
144 }
145
146
147 static int gif_image_write_image(uint8_t **bytestream,
148                                  int x1, int y1, int width, int height,
149                                  const uint8_t *buf, int linesize, int pix_fmt)
150 {
151     PutBitContext p;
152     uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */
153     int i, left, w, v;
154     const uint8_t *ptr;
155     /* image block */
156
157     bytestream_put_byte(bytestream, 0x2c);
158     bytestream_put_le16(bytestream, x1);
159     bytestream_put_le16(bytestream, y1);
160     bytestream_put_le16(bytestream, width);
161     bytestream_put_le16(bytestream, height);
162     bytestream_put_byte(bytestream, 0x00); /* flags */
163     /* no local clut */
164
165     bytestream_put_byte(bytestream, 0x08);
166
167     left= width * height;
168
169     init_put_bits(&p, buffer, 130);
170
171 /*
172  * the thing here is the bitstream is written as little packets, with a size byte before
173  * but it's still the same bitstream between packets (no flush !)
174  */
175     ptr = buf;
176     w = width;
177     while(left>0) {
178
179         put_bits(&p, 9, 0x0100); /* clear code */
180
181         for(i=(left<GIF_CHUNKS)?left:GIF_CHUNKS;i;i--) {
182             if (pix_fmt == PIX_FMT_RGB24) {
183                 v = gif_clut_index(ptr[0], ptr[1], ptr[2]);
184                 ptr+=3;
185             } else {
186                 v = *ptr++;
187             }
188             put_bits(&p, 9, v);
189             if (--w == 0) {
190                 w = width;
191                 buf += linesize;
192                 ptr = buf;
193             }
194         }
195
196         if(left<=GIF_CHUNKS) {
197             put_bits(&p, 9, 0x101); /* end of stream */
198             flush_put_bits(&p);
199         }
200         if(pbBufPtr(&p) - p.buf > 0) {
201             bytestream_put_byte(bytestream, pbBufPtr(&p) - p.buf); /* byte count of the packet */
202             bytestream_put_buffer(bytestream, p.buf, pbBufPtr(&p) - p.buf); /* the actual buffer */
203             p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
204         }
205         left-=GIF_CHUNKS;
206     }
207     bytestream_put_byte(bytestream, 0x00); /* end of image block */
208     bytestream_put_byte(bytestream, 0x3b);
209     return 0;
210 }
211
212 typedef struct {
213     int64_t time, file_time;
214     uint8_t buffer[100]; /* data chunks */
215     AVFrame picture;
216 } GIFContext;
217
218 static av_cold int gif_encode_init(AVCodecContext *avctx)
219 {
220     GIFContext *s = avctx->priv_data;
221
222     avctx->coded_frame = &s->picture;
223     return 0;
224 }
225
226 /* better than nothing gif encoder */
227 static int gif_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, int buf_size, void *data)
228 {
229     GIFContext *s = avctx->priv_data;
230     AVFrame *pict = data;
231     AVFrame *const p = (AVFrame *)&s->picture;
232     uint8_t *outbuf_ptr = outbuf;
233
234     *p = *pict;
235     p->pict_type = FF_I_TYPE;
236     p->key_frame = 1;
237     gif_image_write_header(&outbuf_ptr, avctx->width, avctx->height, (uint32_t *)pict->data[1]);
238     gif_image_write_image(&outbuf_ptr, 0, 0, avctx->width, avctx->height, pict->data[0], pict->linesize[0], PIX_FMT_PAL8);
239     return outbuf_ptr - outbuf;
240 }
241
242 AVCodec gif_encoder = {
243     "gif",
244     CODEC_TYPE_VIDEO,
245     CODEC_ID_GIF,
246     sizeof(GIFContext),
247     gif_encode_init,
248     gif_encode_frame,
249     NULL, //encode_end,
250     .pix_fmts= (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},
251     .long_name= NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"),
252 };