Hi, Attached patch adds support for several animated gif features that either weren't supported by or outright crashed mplayer, including variable frame sizes, interlacing, transparency, and some alternative frame update methods. Also fixed a bug extracting timestamps, which was probably introduced by a change in giflib, since the code in question dates back to rev 1.1. Samples (courtesy of gifanimations.com) here: ftp://opensource:opensource@ftp.on2.com/gifanimations_com-samples.tar.bz2 Comments/testing welcome. Cheers, John Index: libmpdemux/demux_gif.c =================================================================== RCS file: /cvsroot/mplayer/main/libmpdemux/demux_gif.c,v retrieving revision 1.5 diff -u -r1.5 demux_gif.c --- libmpdemux/demux_gif.c 5 Aug 2005 19:57:46 -0000 1.5 +++ libmpdemux/demux_gif.c 3 Feb 2006 20:50:45 -0000 @@ -19,8 +19,21 @@ #include "stheader.h" #include <gif_lib.h> -static int current_pts = 0; -static unsigned char *pallete = NULL; + +struct gif_priv_s { + GifFileType* gif_hndl; + unsigned char* pallete; + unsigned char* canvas; + unsigned int canvas_sz; +}; +#define gif_hndl (((struct gif_priv_s*)(demuxer->priv))->gif_hndl) +#define pallete (((struct gif_priv_s*)(demuxer->priv))->pallete) +#define canvas (((struct gif_priv_s*)(demuxer->priv))->canvas) +#define canvas_sz (((struct gif_priv_s*)(demuxer->priv))->canvas_sz) + +static int current_pts = 0, + ilace_offset[] = { 0, 4, 2, 1 }, + ilace_jmp[] = { 8, 8, 4, 2 }; #define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F') @@ -40,13 +53,16 @@ static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds) { - GifFileType *gif = (GifFileType *)demuxer->priv; + GifFileType *gif = gif_hndl; sh_video_t *sh_video = (sh_video_t *)demuxer->video->sh; GifRecordType type = UNDEFINED_RECORD_TYPE; int len = 0; demux_packet_t *dp = NULL; ColorMapObject *effective_map = NULL; char *buf = NULL; + int trans = 0; + int trans_color = 0; + int reaction = 0; while (type != IMAGE_DESC_RECORD_TYPE) { if (DGifGetRecordType(gif, &type) == GIF_ERROR) { @@ -70,8 +86,13 @@ } if (code == 0xF9) { int frametime = 0; - if (p[0] == 4) // is the length correct? - frametime = (p[1] << 8) | p[2]; // set the time, centiseconds + if (p[0] == 4) { // is the length correct? + frametime = (p[3] << 8) | p[2]; // set the time, centiseconds + reaction = (p[1] >> 2) & 0x3; + // Get transparancy info + trans = p[1] & 0x1; + trans_color = p[4]; + } current_pts += frametime; } else if ((code == 0xFE) && (verbose)) { // comment extension // print iff verbose @@ -103,21 +124,35 @@ } len = gif->Image.Width * gif->Image.Height; - dp = new_demux_packet(len); + dp = new_demux_packet(canvas_sz); buf = malloc(len); memset(buf, 0, len); - memset(dp->buffer, 0, len); + memcpy(dp->buffer, canvas, canvas_sz); - if (DGifGetLine(gif, buf, len) == GIF_ERROR) { - PrintGifError(); - return 0; // oops + if(gif->Image.Interlace) { + int i,j; + for(i = 0; i < 4; i++) { + for(j = ilace_offset[i]; j < gif->Image.Height; j += ilace_jmp[i]) { + if (DGifGetLine(gif, buf + (j * gif->Image.Width), gif->Image.Width) + == GIF_ERROR) + { + PrintGifError(); + return 0; // oops + } + } + } + } else { + if (DGifGetLine(gif, buf, len) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } } effective_map = gif->Image.ColorMap; if (effective_map == NULL) effective_map = gif->SColorMap; { - int y; + int y,x,width,height; // copy the pallete for (y = 0; y < 256; y++) { @@ -127,15 +162,55 @@ pallete[(y * 4) + 3] = 0; } - for (y = 0; y < gif->Image.Height; y++) { + // Use the canvas w/h as a bounding box for the blit + height = gif->Image.Height; + width = gif->Image.Width; + if(gif->Image.Top + gif->Image.Height > gif->SHeight) + height = gif->SHeight - gif->Image.Top; + if(gif->Image.Left + gif->Image.Width > gif->SWidth) + width = gif->SWidth - gif->Image.Left; + + for (y = 0; y < height; y++) { unsigned char *drow = dp->buffer; unsigned char *gbuf = buf + (y * gif->Image.Width); - drow += gif->Image.Width * (y + gif->Image.Top); + drow += gif->SWidth * (y + gif->Image.Top); drow += gif->Image.Left; - memcpy(drow, gbuf, gif->Image.Width); + if(!trans) { + // No transparancy, can do a fast blit + memcpy(drow, gbuf, width); + } else { + // Do a byte by byte blit for all non transparant pixels. + for (x = 0; x < width; x++) { + if(gbuf[x] != trans_color) + drow[x] = gbuf[x]; + } + } } + + switch(reaction) { + case 1: + // Preserve the current frame contents for the next frame. + memcpy(canvas, dp->buffer, canvas_sz); + break; + case 2: + // Fill the image area with the background color + for (y = 0; y < height; y++) { + unsigned char *drow = canvas; + drow += gif->SWidth * (y + gif->Image.Top); + drow += gif->Image.Left; + memset(drow, gif->SBackGroundColor, width); + } + break; + case 3: + // Preserve the previous frame contents for the next frame. + // No action required. + break; + default: + // Wipe the whole canvas with the background color + memset(canvas, gif->SBackGroundColor, canvas_sz); + } } free(buf); @@ -199,16 +274,19 @@ sh_video->bih->biCompression = sh_video->format; sh_video->bih->biBitCount = 8; sh_video->bih->biPlanes = 2; - pallete = (unsigned char *)(sh_video->bih + 1); + demuxer->priv = malloc(sizeof(struct gif_priv_s)); + pallete = (unsigned char *)(sh_video->bih + 1); + canvas_sz = gif->SWidth * gif->SHeight; + gif_hndl = gif; + canvas = malloc(canvas_sz); + memset(canvas, gif->SBackGroundColor, canvas_sz); - demuxer->priv = gif; - return demuxer; } static void demux_close_gif(demuxer_t* demuxer) { - GifFileType *gif = (GifFileType *)demuxer->priv; + GifFileType *gif = gif_hndl; if(!gif) return; @@ -217,6 +295,8 @@ PrintGifError(); demuxer->stream->fd = 0; + if(demuxer->priv) + free(demuxer->priv); demuxer->priv = NULL; }