[MPlayer-dev-eng] [PATCH] Various updates to GIF demuxer

Diego Biurrun diego at biurrun.de
Sat Jan 13 10:01:49 CET 2007


On Sat, Jan 13, 2007 at 07:15:22AM +0100, Diego Biurrun wrote:
> On Sat, Jan 13, 2007 at 06:59:26AM +0100, Diego Biurrun wrote:
> > On Fri, Feb 03, 2006 at 04:55:34PM -0500, John Koleszar wrote:
> > > 
> > > 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.
> > > 
> > > --- libmpdemux/demux_gif.c	5 Aug 2005 19:57:46 -0000	1.5
> > > +++ libmpdemux/demux_gif.c	3 Feb 2006 20:50:45 -0000
> > > @@ -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
> > > +          frametime = (p[3] << 8) | p[2]; // set the time, centiseconds
> > 
> > This is the frametime fix, compare
> > 
> > http://www.onicos.com/staff/iz/formats/gif.html#gceb
> > 
> > to see that if p[0] is block size p[3] and p[2], not p[1] and p[2] must
> > be the delay time.
> > 
> > This hunk committed, it fixes several of the samples above.
> 
> Here is the rest of the patch, updated for Subversion HEAD, plus a few
> cosmetical fixes.

I think it's time that I found a club for scatterbrained patch
attachers.  Reimar will be invited in as exclusive member...

Diego
-------------- next part --------------
Index: libmpdemux/demux_gif.c
===================================================================
--- libmpdemux/demux_gif.c	(revision 21895)
+++ libmpdemux/demux_gif.c	(working copy)
@@ -17,9 +17,22 @@
 #include "stheader.h"
 
 #include <gif_lib.h>
-static int current_pts = 0;
-static unsigned char *palette = NULL;
 
+struct gif_priv_s {
+    GifFileType*    gif_hndl;
+    unsigned char*  palette;
+    unsigned char*  canvas;
+    unsigned int    canvas_sz;
+};
+#define gif_hndl  (((struct gif_priv_s*)(demuxer->priv))->gif_hndl)
+#define palette   (((struct gif_priv_s*)(demuxer->priv))->palette)
+#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')
 
 #ifndef HAVE_GIF_TVT_HACK
@@ -38,12 +51,15 @@
 
 static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
 {
-  GifFileType *gif = (GifFileType *)demuxer->priv;
+  GifFileType *gif = gif_hndl;
   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) {
@@ -67,8 +83,13 @@
       }
       if (code == 0xF9) {
         int frametime = 0;
-        if (p[0] == 4) // is the length correct?
+        if (p[0] == 4) { // Is the length correct?
           frametime = (p[3] << 8) | p[2]; // set the time, centiseconds
+          reaction = (p[1] >> 2) & 0x3;
+          // Get transparency info.
+          trans = p[1] & 0x1;
+          trans_color = p[4];
+        }
         current_pts += frametime;
       } else if ((code == 0xFE) && (verbose)) { // comment extension
 	// print iff verbose
@@ -100,23 +121,37 @@
   }
 
   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 palette
+    // Copy the palette.
     for (y = 0; y < 256; y++) {
 	palette[(y * 4) + 0] = effective_map->Colors[y].Blue;
 	palette[(y * 4) + 1] = effective_map->Colors[y].Green;
@@ -124,15 +159,55 @@
 	palette[(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 transparency, can do a fast blit.
+        memcpy(drow, gbuf, width);
+      } else {
+        // Do a byte by byte blit for all non-transparent 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);
@@ -196,16 +271,19 @@
   sh_video->bih->biCompression = sh_video->format;
   sh_video->bih->biBitCount = 8;
   sh_video->bih->biPlanes = 2;
+  demuxer->priv = malloc(sizeof(struct gif_priv_s));
   palette = (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 = (demuxer->priv)?gif_hndl:NULL;
 
   if(!gif)
     return;
@@ -214,6 +292,8 @@
     PrintGifError();
   
   demuxer->stream->fd = 0;
+  if(demuxer->priv)
+    free(demuxer->priv);
   demuxer->priv = NULL;
 }
 


More information about the MPlayer-dev-eng mailing list