typo fix
[mplayer/greg.git] / libmpdemux / demux_gif.c
blob99135db0c8a8525b0a4a1123a611ae2754b07444
1 /*
2 GIF file parser for MPlayer
3 by Joey Parrish
4 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
10 #include "config.h"
12 #include "mp_msg.h"
13 #include "help_mp.h"
15 #include "stream/stream.h"
16 #include "demuxer.h"
17 #include "stheader.h"
19 #include <gif_lib.h>
20 #include "libvo/fastmemcpy.h"
21 typedef struct {
22 int current_pts;
23 unsigned char *palette;
24 GifFileType *gif;
25 int w, h;
26 int useref;
27 uint8_t *refimg;
28 } gif_priv_t;
30 #define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F')
32 #ifndef HAVE_GIF_TVT_HACK
33 // not supported by certain versions of the library
34 int my_read_gif(GifFileType *gif, uint8_t *buf, int len) {
35 return stream_read(gif->UserData, buf, len);
37 #endif
39 static int gif_check_file(demuxer_t *demuxer)
41 if (stream_read_int24(demuxer->stream) == GIF_SIGNATURE)
42 return DEMUXER_TYPE_GIF;
43 return 0;
46 static void memcpy_transp_pic(uint8_t *dst, uint8_t *src, int w, int h,
47 int dstride, int sstride, int transp, uint8_t trans_col) {
48 if (transp) {
49 dstride -= w;
50 sstride -= w;
51 while (h-- > 0) {
52 int wleft = w;
53 while (wleft-- > 0) {
54 if (*src != trans_col)
55 *dst = *src;
56 dst++; src++;
58 dst += dstride;
59 src += sstride;
61 } else
62 memcpy_pic(dst, src, w, h, dstride, sstride);
65 static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
67 gif_priv_t *priv = demuxer->priv;
68 GifFileType *gif = priv->gif;
69 GifRecordType type = UNDEFINED_RECORD_TYPE;
70 int len = 0;
71 demux_packet_t *dp = NULL;
72 ColorMapObject *effective_map = NULL;
73 uint8_t *buf = NULL;
74 int refmode = 0;
75 int transparency = 0;
76 uint8_t transparent_col;
78 while (type != IMAGE_DESC_RECORD_TYPE) {
79 if (DGifGetRecordType(gif, &type) == GIF_ERROR) {
80 PrintGifError();
81 return 0; // oops
83 if (type == TERMINATE_RECORD_TYPE)
84 return 0; // eof
85 if (type == SCREEN_DESC_RECORD_TYPE) {
86 if (DGifGetScreenDesc(gif) == GIF_ERROR) {
87 PrintGifError();
88 return 0; // oops
91 if (type == EXTENSION_RECORD_TYPE) {
92 int code;
93 unsigned char *p = NULL;
94 if (DGifGetExtension(gif, &code, &p) == GIF_ERROR) {
95 PrintGifError();
96 return 0; // oops
98 if (code == 0xF9) {
99 int frametime = 0;
100 if (p[0] == 4) // is the length correct?
102 transparency = p[1] & 1;
103 refmode = (p[1] >> 2) & 3;
104 // HACK: specification says
105 // > 0 - No disposal specified. The decoder is not required to take any action.
106 // but browsers treat it the same way as
107 // > 1 - Do not dispose. The graphic is to be left in place.
108 // Some broken files rely on this, e.g.
109 // http://samples.mplayerhq.hu/GIF/broken-gif/CLAIRE.GIF
110 if (refmode == 0) refmode = 1;
111 frametime = (p[3] << 8) | p[2]; // set the time, centiseconds
112 transparent_col = p[4];
114 priv->current_pts += frametime;
115 } else if ((code == 0xFE) && (verbose)) { // comment extension
116 // print iff verbose
117 printf("GIF comment: ");
118 while (p != NULL) {
119 int length = p[0];
120 char *comments = p + 1;
121 comments[length] = 0;
122 printf("%s", comments);
123 if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
124 PrintGifError();
125 return 0; // oops
128 printf("\n");
130 while (p != NULL) {
131 if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
132 PrintGifError();
133 return 0; // oops
139 if (DGifGetImageDesc(gif) == GIF_ERROR) {
140 PrintGifError();
141 return 0; // oops
144 len = gif->Image.Width * gif->Image.Height;
145 dp = new_demux_packet(priv->w * priv->h);
146 buf = calloc(gif->Image.Width, gif->Image.Height);
147 if (priv->useref)
148 fast_memcpy(dp->buffer, priv->refimg, priv->w * priv->h);
149 else
150 memset(dp->buffer, gif->SBackGroundColor, priv->w * priv->h);
152 if (DGifGetLine(gif, buf, len) == GIF_ERROR) {
153 PrintGifError();
154 return 0; // oops
157 effective_map = gif->Image.ColorMap;
158 if (effective_map == NULL) effective_map = gif->SColorMap;
161 int y;
162 int cnt = FFMIN(effective_map->ColorCount, 256);
163 int l = av_clip(gif->Image.Left, 0, priv->w);
164 int t = av_clip(gif->Image.Top, 0, priv->h);
165 int w = av_clip(gif->Image.Width, 0, priv->w - l);
166 int h = av_clip(gif->Image.Height, 0, priv->h - t);
167 unsigned char *dest = dp->buffer + priv->w * t + l;
169 // copy the palette
170 for (y = 0; y < cnt; y++) {
171 priv->palette[(y * 4) + 0] = effective_map->Colors[y].Blue;
172 priv->palette[(y * 4) + 1] = effective_map->Colors[y].Green;
173 priv->palette[(y * 4) + 2] = effective_map->Colors[y].Red;
174 priv->palette[(y * 4) + 3] = 0;
177 if (gif->Image.Interlace) {
178 uint8_t *s = buf;
179 int ih = (h - 0 + 7) >> 3;
180 memcpy_transp_pic(dest, s, w, ih,
181 priv->w << 3, gif->Image.Width,
182 transparency, transparent_col);
183 s += ih * w;
184 ih = (h - 4 + 7) >> 3;
185 memcpy_transp_pic(dest + (priv->w << 2), s, w, ih,
186 priv->w << 3, gif->Image.Width,
187 transparency, transparent_col);
188 s += ih * w;
189 ih = (h - 2 + 3) >> 2;
190 memcpy_transp_pic(dest + (priv->w << 1), s, w, ih,
191 priv->w << 2, gif->Image.Width,
192 transparency, transparent_col);
193 s += ih * w;
194 ih = (h - 1 + 1) >> 1;
195 memcpy_transp_pic(dest + priv->w, s, w, ih,
196 priv->w << 1, gif->Image.Width,
197 transparency, transparent_col);
198 } else
199 memcpy_transp_pic(dest, buf, w, h, priv->w, gif->Image.Width,
200 transparency, transparent_col);
202 if (refmode == 1) fast_memcpy(priv->refimg, dp->buffer, priv->w * priv->h);
203 if (refmode == 2 && priv->useref) {
204 dest = priv->refimg + priv->w * t + l;
205 memset(buf, gif->SBackGroundColor, len);
206 memcpy_pic(dest, buf, w, h, priv->w, gif->Image.Width);
208 if (!(refmode & 2)) priv->useref = refmode & 1;
211 free(buf);
213 demuxer->video->dpos++;
214 dp->pts = ((float)priv->current_pts) / 100;
215 dp->pos = stream_tell(demuxer->stream);
216 ds_add_packet(demuxer->video, dp);
218 return 1;
221 static demuxer_t* demux_open_gif(demuxer_t* demuxer)
223 gif_priv_t *priv = calloc(1, sizeof(gif_priv_t));
224 sh_video_t *sh_video = NULL;
225 GifFileType *gif = NULL;
227 priv->current_pts = 0;
228 demuxer->seekable = 0; // FIXME
230 // go back to the beginning
231 stream_seek(demuxer->stream,demuxer->stream->start_pos);
233 #ifdef HAVE_GIF_TVT_HACK
234 // without the TVT functionality of libungif, a hard seek must be
235 // done to the beginning of the file. this is because libgif is
236 // unable to use mplayer's cache, and without this lseek libgif will
237 // not read from the beginning of the file and the command will fail.
238 // with this hack enabled, you will lose the ability to stream a GIF.
239 lseek(demuxer->stream->fd, 0, SEEK_SET);
240 gif = DGifOpenFileHandle(demuxer->stream->fd);
241 #else
242 gif = DGifOpen(demuxer->stream, my_read_gif);
243 #endif
244 if (!gif) {
245 PrintGifError();
246 return NULL;
249 // create a new video stream header
250 sh_video = new_sh_video(demuxer, 0);
252 // make sure the demuxer knows about the new video stream header
253 // (even though new_sh_video() ought to take care of it)
254 demuxer->video->sh = sh_video;
256 // make sure that the video demuxer stream header knows about its
257 // parent video demuxer stream (this is getting wacky), or else
258 // video_read_properties() will choke
259 sh_video->ds = demuxer->video;
261 sh_video->format = mmioFOURCC(8, 'R', 'G', 'B');
263 sh_video->fps = 5.0f;
264 sh_video->frametime = 1.0f / sh_video->fps;
266 sh_video->bih = malloc(sizeof(BITMAPINFOHEADER) + (256 * 4));
267 sh_video->bih->biCompression = sh_video->format;
268 sh_video->bih->biWidth = priv->w = (uint16_t)gif->SWidth;
269 sh_video->bih->biHeight = priv->h = (uint16_t)gif->SHeight;
270 sh_video->bih->biBitCount = 8;
271 sh_video->bih->biPlanes = 2;
272 priv->palette = (unsigned char *)(sh_video->bih + 1);
273 priv->refimg = malloc(priv->w * priv->h);
275 priv->gif = gif;
276 demuxer->priv = priv;
278 return demuxer;
281 static void demux_close_gif(demuxer_t* demuxer)
283 gif_priv_t *priv = demuxer->priv;
284 if (!priv) return;
285 if (priv->gif && DGifCloseFile(priv->gif) == GIF_ERROR)
286 PrintGifError();
287 free(priv->refimg);
288 free(priv);
292 const demuxer_desc_t demuxer_desc_gif = {
293 "GIF demuxer",
294 "gif",
295 "GIF",
296 "Joey Parrish",
298 DEMUXER_TYPE_GIF,
299 0, // unsafe autodetect
300 gif_check_file,
301 demux_gif_fill_buffer,
302 demux_open_gif,
303 demux_close_gif,
304 NULL,
305 NULL