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 -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; }