]> git.imager.perl.org - imager.git/blobdiff - GIF/imgif.c
libt1 support is deprecated
[imager.git] / GIF / imgif.c
index 3302d4837b1ddfea4e0768683f3a049c8fe73895..7a7fdb2bbae1a4fce9d5f578da230498f548b2ea 100644 (file)
@@ -59,21 +59,96 @@ functionality with giflib3.
 =cut
 */
 
+#ifdef GIFLIB_MAJOR
+#define IMGIFLIB_API_VERSION (GIFLIB_MAJOR * 100 + GIFLIB_MINOR)
+#else
+/* only matters for pre-5.0 which we either reject, or which contains
+   no significant API changes */
+#define IMGIFLIB_API_VERSION 0
+#endif
+
+#if IMGIFLIB_API_VERSION >= 500
+#define POST_SET_VERSION
+#define myDGifOpen(userPtr, readFunc, Error) DGifOpen((userPtr), (readFunc), (Error))
+#define myEGifOpen(userPtr, readFunc, Error) EGifOpen((userPtr), (readFunc), (Error))
+#define myGifError(gif) ((gif)->Error)
+#define MakeMapObject GifMakeMapObject
+#define FreeMapObject GifFreeMapObject
+#define gif_mutex_lock(mutex)
+#define gif_mutex_unlock(mutex)
+#else
+#define PRE_SET_VERSION
+static GifFileType *
+myDGifOpen(void *userPtr, InputFunc readFunc, int *error) {
+  GifFileType *result = DGifOpen(userPtr, readFunc);
+  if (!result)
+    *error = GifLastError();
+
+  return result;
+}
+static GifFileType *
+myEGifOpen(void *userPtr, OutputFunc outputFunc, int *error) {
+  GifFileType *result = EGifOpen(userPtr, outputFunc);
+  if (!result)
+    *error = GifLastError();
+
+  return result;
+}
+#define myGifError(gif) GifLastError()
+#define gif_mutex_lock(mutex) i_mutex_lock(mutex)
+#define gif_mutex_unlock(mutex) i_mutex_unlock(mutex)
+
+#endif
+
+#if IMGIFLIB_API_VERSION >= 501
+#define myDGifCloseFile(gif, perror) (DGifCloseFile((gif), (perror)))
+#define myEGifCloseFile(gif, perror) (EGifCloseFile((gif), (perror)))
+#else
+static int
+myDGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
+  int result = DGifCloseFile(GifFile);
+  if (result == GIF_ERROR) {
+    if (ErrorCode)
+      *ErrorCode = myGifError(GifFile);
+    free(GifFile->Private);
+    free(GifFile);
+  }
+
+  return result;
+}
+
+static int
+myEGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
+  int result = EGifCloseFile(GifFile);
+  if (result == GIF_ERROR) {
+    if (ErrorCode)
+      *ErrorCode = myGifError(GifFile);
+    free(GifFile->Private);
+    free(GifFile);
+  }
+
+  return result;
+}
+#endif
+
 static char const *gif_error_msg(int code);
-static void gif_push_error(void);
+static void gif_push_error(int code);
 
 /* Make some variables global, so we could access them faster: */
 
-static int
+static const int
   InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
   InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
 
-
+#if IMGIFLIB_API_VERSION < 500
 static i_mutex_t mutex;
+#endif
 
 void
 i_init_gif(void) {
+#if IMGIFLIB_API_VERSION < 500
   mutex = i_mutex_new();
+#endif
 }
 
 static
@@ -137,15 +212,16 @@ i_img *
 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
   i_img *im;
   int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
-  int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
+  int cmapcnt = 0, ImageNum = 0;
   ColorMapObject *ColorMap;
  
   GifRecordType RecordType;
   GifByteType *Extension;
   
   GifRowType GifRow;
-  static GifColorType *ColorMapEntry;
+  GifColorType *ColorMapEntry;
   i_color col;
+  int error;
 
   mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
 
@@ -155,11 +231,9 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
   */
   if (colour_table) *colour_table = NULL;
 
-  BackGround = GifFile->SBackGroundColor;
   ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
 
   if (ColorMap) {
-    ColorMapSize = ColorMap->ColorCount;
     i_colortable_copy(colour_table, colours, ColorMap);
     cmapcnt++;
   }
@@ -169,7 +243,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
       myfree(*colour_table);
       *colour_table = NULL;
     }
-    DGifCloseFile(GifFile);
+    (void)myDGifCloseFile(GifFile, NULL);
     mm_log((1, "i_readgif: image size exceeds limits\n"));
     return NULL;
   }
@@ -180,7 +254,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
       myfree(*colour_table);
       *colour_table = NULL;
     }
-    DGifCloseFile(GifFile);
+    (void)myDGifCloseFile(GifFile, NULL);
     return NULL;
   }
 
@@ -193,7 +267,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
   /* Scan the content of the GIF file and load the image(s) in: */
   do {
     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
-      gif_push_error();
+      gif_push_error(myGifError(GifFile));
       i_push_error(0, "Unable to get record type");
       if (colour_table && *colour_table) {
        myfree(*colour_table);
@@ -201,14 +275,14 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
       }
       myfree(GifRow);
       i_img_destroy(im);
-      DGifCloseFile(GifFile);
+      (void)myDGifCloseFile(GifFile, NULL);
       return NULL;
     }
     
     switch (RecordType) {
     case IMAGE_DESC_RECORD_TYPE:
       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
-       gif_push_error();
+       gif_push_error(myGifError(GifFile));
        i_push_error(0, "Unable to get image descriptor");
        if (colour_table && *colour_table) {
          myfree(*colour_table);
@@ -216,13 +290,12 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
        }
        myfree(GifRow);
        i_img_destroy(im);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        return NULL;
       }
 
       if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
        mm_log((1, "Adding local colormap\n"));
-       ColorMapSize = ColorMap->ColorCount;
        if ( cmapcnt == 0) {
          i_colortable_copy(colour_table, colours, ColorMap);
          cmapcnt++;
@@ -234,7 +307,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
        /* we can't have allocated a colour table here */
        myfree(GifRow);
        i_img_destroy(im);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        return NULL;
       }
       
@@ -254,7 +327,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
        }
        myfree(GifRow);
        i_img_destroy(im);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        return NULL;
       }
       if (GifFile->Image.Interlace) {
@@ -262,7 +335,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
        for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
          Count++;
          if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
-           gif_push_error();
+           gif_push_error(myGifError(GifFile));
            i_push_error(0, "Reading GIF line");
            if (colour_table && *colour_table) {
              myfree(*colour_table);
@@ -270,7 +343,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
            }
            myfree(GifRow);
            i_img_destroy(im);
-           DGifCloseFile(GifFile);
+           (void)myDGifCloseFile(GifFile, NULL);
            return NULL;
          }
          
@@ -287,7 +360,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
       else {
        for (i = 0; i < Height; i++) {
          if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
-           gif_push_error();
+           gif_push_error(myGifError(GifFile));
            i_push_error(0, "Reading GIF line");
            if (colour_table && *colour_table) {
              myfree(*colour_table);
@@ -295,7 +368,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
            }
            myfree(GifRow);
            i_img_destroy(im);
-           DGifCloseFile(GifFile);
+           (void)myDGifCloseFile(GifFile, NULL);
            return NULL;
          }
 
@@ -313,7 +386,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
     case EXTENSION_RECORD_TYPE:
       /* Skip any extension blocks in file: */
       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
-       gif_push_error();
+       gif_push_error(myGifError(GifFile));
        i_push_error(0, "Reading extension record");
        if (colour_table && *colour_table) {
          myfree(*colour_table);
@@ -321,12 +394,12 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
        }
        myfree(GifRow);
        i_img_destroy(im);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        return NULL;
       }
       while (Extension != NULL) {
        if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
-         gif_push_error();
+         gif_push_error(myGifError(GifFile));
          i_push_error(0, "reading next block of extension");
          if (colour_table && *colour_table) {
            myfree(*colour_table);
@@ -334,7 +407,7 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
          }
          myfree(GifRow);
          i_img_destroy(im);
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          return NULL;
        }
       }
@@ -348,8 +421,8 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
   
   myfree(GifRow);
   
-  if (DGifCloseFile(GifFile) == GIF_ERROR) {
-    gif_push_error();
+  if (myDGifCloseFile(GifFile, &error) == GIF_ERROR) {
+    gif_push_error(error);
     i_push_error(0, "Closing GIF file object");
     if (colour_table && *colour_table) {
       myfree(*colour_table);
@@ -369,7 +442,8 @@ i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
 Internal function called by i_readgif_multi_low() in error handling
 
 */
-static void free_images(i_img **imgs, int count) {
+static void
+free_images(i_img **imgs, int count) {
   int i;
   
   if (count) {
@@ -463,10 +537,11 @@ standard.
 =cut
 */
 
-i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
+i_img **
+i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
   i_img *img;
   int i, j, Size, Width, Height, ExtCode, Count;
-  int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
+  int ImageNum = 0, ColorMapSize = 0;
   ColorMapObject *ColorMap;
  
   GifRecordType RecordType;
@@ -486,6 +561,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
   int channels;
   int image_colors = 0;
   i_color black; /* used to expand the palette if needed */
+  int error;
 
   for (i = 0; i < MAXCHANNELS; ++i)
     black.channel[i] = 0;
@@ -494,8 +570,6 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
 
   mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
 
-  BackGround = GifFile->SBackGroundColor;
-
   Size = GifFile->SWidth * sizeof(GifPixelType);
   
   GifRow = (GifRowType) mymalloc(Size);
@@ -503,10 +577,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
   /* Scan the content of the GIF file and load the image(s) in: */
   do {
     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
-      gif_push_error();
+      gif_push_error(myGifError(GifFile));
       i_push_error(0, "Unable to get record type");
       free_images(results, *count);
-      DGifCloseFile(GifFile);
+      (void)myDGifCloseFile(GifFile, NULL);
       myfree(GifRow);
       if (comment)
        myfree(comment);
@@ -516,10 +590,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
     switch (RecordType) {
     case IMAGE_DESC_RECORD_TYPE:
       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
-       gif_push_error();
+       gif_push_error(myGifError(GifFile));
        i_push_error(0, "Unable to get image descriptor");
         free_images(results, *count);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        myfree(GifRow);
        if (comment)
          myfree(comment);
@@ -538,7 +612,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          mm_log((1, "Going in with no colormap\n"));
          i_push_error(0, "Image does not have a local or a global color map");
          free_images(results, *count);
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          myfree(GifRow);
          if (comment)
            myfree(comment);
@@ -551,7 +625,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
          free_images(results, *count);
          mm_log((1, "i_readgif: image size exceeds limits\n"));
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          myfree(GifRow);
          if (comment)
            myfree(comment);
@@ -560,7 +634,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        img = i_img_pal_new(Width, Height, channels, 256);
        if (!img) {
          free_images(results, *count);
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          if (comment)
            myfree(comment);
          myfree(GifRow);
@@ -636,7 +710,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
            GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
          i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
          free_images(results, *count);        
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          myfree(GifRow);
          if (comment)
            myfree(comment);
@@ -649,10 +723,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
                 j += InterlacedJumps[i]) {
              Count++;
              if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
-               gif_push_error();
+               gif_push_error(myGifError(GifFile));
                i_push_error(0, "Reading GIF line");
                free_images(results, *count);
-               DGifCloseFile(GifFile);
+               (void)myDGifCloseFile(GifFile, NULL);
                myfree(GifRow);
                if (comment)
                  myfree(comment);
@@ -678,10 +752,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        else {
          for (i = 0; i < Height; i++) {
            if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
-             gif_push_error();
+             gif_push_error(myGifError(GifFile));
              i_push_error(0, "Reading GIF line");
              free_images(results, *count);
-             DGifCloseFile(GifFile);
+             (void)myDGifCloseFile(GifFile, NULL);
              myfree(GifRow);
              if (comment)
                myfree(comment);
@@ -707,7 +781,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        /* must be only one image wanted and that was it */
        if (page != -1) {
          myfree(GifRow);
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          if (comment)
            myfree(comment);
          return results;
@@ -719,11 +793,11 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        /* giflib does't have an interface to skip the image data */
        for (i = 0; i < Height; i++) {
          if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
-           gif_push_error();
+           gif_push_error(myGifError(GifFile));
            i_push_error(0, "Reading GIF line");
            free_images(results, *count);
            myfree(GifRow);
-           DGifCloseFile(GifFile);
+           (void)myDGifCloseFile(GifFile, NULL);
            if (comment) 
              myfree(comment);
            return NULL;
@@ -741,11 +815,11 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
     case EXTENSION_RECORD_TYPE:
       /* Skip any extension blocks in file: */
       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
-       gif_push_error();
+       gif_push_error(myGifError(GifFile));
        i_push_error(0, "Reading extension record");
         free_images(results, *count);
        myfree(GifRow);
-       DGifCloseFile(GifFile);
+       (void)myDGifCloseFile(GifFile, NULL);
        if (comment)
          myfree(comment);
        return NULL;
@@ -766,11 +840,11 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
       if (ExtCode == 0xFF && *Extension == 11) {
         if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
           if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
-            gif_push_error();
+            gif_push_error(myGifError(GifFile));
             i_push_error(0, "reading loop extension");
             free_images(results, *count);
            myfree(GifRow);
-           DGifCloseFile(GifFile);
+           (void)myDGifCloseFile(GifFile, NULL);
            if (comment)
              myfree(comment);
             return NULL;
@@ -796,11 +870,11 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
       }
       while (Extension != NULL) {
        if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
-         gif_push_error();
+         gif_push_error(myGifError(GifFile));
          i_push_error(0, "reading next block of extension");
           free_images(results, *count);
          myfree(GifRow);
-         DGifCloseFile(GifFile);
+         (void)myDGifCloseFile(GifFile, NULL);
          if (comment)
            myfree(comment);
          return NULL;
@@ -824,13 +898,19 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
   
   myfree(GifRow);
   
-  if (DGifCloseFile(GifFile) == GIF_ERROR) {
-    gif_push_error();
+  if (myDGifCloseFile(GifFile, &error) == GIF_ERROR) {
+    gif_push_error(error);
     i_push_error(0, "Closing GIF file object");
     free_images(results, *count);
     return NULL;
   }
 
+  if (!ImageNum) {
+    /* there were no images */
+    i_push_error(0, "no images found in file");
+    return NULL;
+  }
+
   if (ImageNum && page != -1) {
     /* there were images, but the page selected wasn't found */
     i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
@@ -852,23 +932,24 @@ static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
 i_img **
 i_readgif_multi_wiol(io_glue *ig, int *count) {
   GifFileType *GifFile;
+  int gif_error;
   i_img **result;
 
-  i_mutex_lock(mutex);
+  gif_mutex_lock(mutex);
 
   i_clear_error();
   
-  if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
-    gif_push_error();
+  if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
+    gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
-    i_mutex_unlock(mutex);
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
   result = i_readgif_multi_low(GifFile, count, -1);
 
-  i_mutex_unlock(mutex);
+  gif_mutex_unlock(mutex);
 
   return result;
 }
@@ -883,23 +964,24 @@ io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
 i_img *
 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
   GifFileType *GifFile;
+  int gif_error;
   i_img *result;
 
-  i_mutex_lock(mutex);
+  gif_mutex_lock(mutex);
 
   i_clear_error();
 
-  if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
-    gif_push_error();
+  if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
+    gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
-    i_mutex_unlock(mutex);
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
   result = i_readgif_low(GifFile, color_table, colors);
 
-  i_mutex_unlock(mutex);
+  gif_mutex_unlock(mutex);
 
   return result;
 }
@@ -946,6 +1028,7 @@ Returns NULL if the page isn't found.
 i_img *
 i_readgif_single_wiol(io_glue *ig, int page) {
   GifFileType *GifFile;
+  int gif_error;
   i_img *result;
 
   i_clear_error();
@@ -954,19 +1037,19 @@ i_readgif_single_wiol(io_glue *ig, int page) {
     return NULL;
   }
 
-  i_mutex_lock(mutex);
+  gif_mutex_lock(mutex);
 
-  if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
-    gif_push_error();
+  if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
+    gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
-    i_mutex_unlock(mutex);
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
   result = i_readgif_single_low(GifFile, page);
 
-  i_mutex_unlock(mutex);
+  gif_mutex_unlock(mutex);
 
   return result;
 }
@@ -988,10 +1071,9 @@ do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
     for (i = 0; i < 4; ++i) {
       for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
        if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
-         gif_push_error();
+         gif_push_error(myGifError(gf));
          i_push_error(0, "Could not save image data:");
          mm_log((1, "Error in EGifPutLine\n"));
-         EGifCloseFile(gf);
          return 0;
        }
       }
@@ -1001,10 +1083,9 @@ do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
     int y;
     for (y = 0; y < img->ysize; ++y) {
       if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
-       gif_push_error();
+       gif_push_error(myGifError(gf));
        i_push_error(0, "Could not save image data:");
        mm_log((1, "Error in EGifPutLine\n"));
-       EGifCloseFile(gf);
        return 0;
       }
       data += img->xsize;
@@ -1023,7 +1104,9 @@ Returns non-zero on success.
 
 =cut
 */
-static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
+
+static int
+do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
 {
   unsigned char gce[4] = {0};
   int want_gce = 0;
@@ -1052,7 +1135,7 @@ static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
   }
   if (want_gce) {
     if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
-      gif_push_error();
+      gif_push_error(myGifError(gf));
       i_push_error(0, "Could not save GCE");
     }
   }
@@ -1066,7 +1149,9 @@ Write any comments in the image.
 
 =cut
 */
-static int do_comments(GifFileType *gf, i_img *img) {
+
+static int
+do_comments(GifFileType *gf, i_img *img) {
   int pos = -1;
 
   while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
@@ -1104,7 +1189,9 @@ writing extension blocks so that they could only be written to files.
 
 =cut
 */
-static int do_ns_loop(GifFileType *gf, i_img *img)
+
+static int
+do_ns_loop(GifFileType *gf, i_img *img)
 {
   /* EGifPutExtension() doesn't appear to handle application 
      extension blocks in any way
@@ -1121,19 +1208,33 @@ static int do_ns_loop(GifFileType *gf, i_img *img)
   if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
     unsigned char nsle[12] = "NETSCAPE2.0";
     unsigned char subblock[3];
+
+    subblock[0] = 1;
+    subblock[1] = loop_count % 256;
+    subblock[2] = loop_count / 256;
+
+#if IMGIFLIB_API_VERSION >= 500
+    if (EGifPutExtensionLeader(gf, APPLICATION_EXT_FUNC_CODE) == GIF_ERROR
+       || EGifPutExtensionBlock(gf, 11, nsle) == GIF_ERROR
+       || EGifPutExtensionBlock(gf, 3, subblock) == GIF_ERROR
+       || EGifPutExtensionTrailer(gf) == GIF_ERROR) {
+      gif_push_error(myGifError(gf));
+      i_push_error(0, "writing loop extension");
+      return 0;
+    }
+       
+#else
     if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
-      gif_push_error();
+      gif_push_error(myGifError(gf));
       i_push_error(0, "writing loop extension");
       return 0;
     }
-    subblock[0] = 1;
-    subblock[1] = loop_count % 256;
-    subblock[2] = loop_count / 256;
     if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
-      gif_push_error();
+      gif_push_error(myGifError(gf));
       i_push_error(0, "writing loop extension sub-block");
       return 0;
     }
+#endif
   }
 
   return 1;
@@ -1147,8 +1248,8 @@ Create a giflib color map object from an Imager color map.
 =cut
 */
 
-static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img, 
-                                    int want_trans) {
+static ColorMapObject *
+make_gif_map(i_quantize *quant, i_img *img, int want_trans) {
   GifColorType colors[256];
   int i;
   int size = quant->mc_count;
@@ -1181,79 +1282,57 @@ static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
   }
   
   map = MakeMapObject(map_size, colors);
-  mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
   if (!map) {
-    gif_push_error();
     i_push_error(0, "Could not create color map object");
     return NULL;
   }
+  mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
+#if IMGIFLIB_API_VERSION >= 500
+  map->SortFlag = 0;
+#endif
   return map;
 }
 
 /*
-=item gif_set_version(i_quantize *quant, i_img *imgs, int count)
-
-We need to call EGifSetGifVersion() before opening the file - put that
-common code here.
-
-Unfortunately giflib 4.1.0 crashes when we use this.  Internally
-giflib 4.1.0 has code:
-
-  static char *GifVersionPrefix = GIF87_STAMP;
-
-and the code that sets the version internally does:
+=item need_version_89a(i_quantize *quant, i_img *imgs, int count)
 
-  strncpy(&GifVersionPrefix[3], Version, 3);
-
-which is very broken.
-
-Failing to set the correct GIF version doesn't seem to cause a problem
-with readers.
-
-Modern versions (4.1.4 anyway) of giflib/libungif handle
-EGifSetGifVersion correctly.
-
-If t/t105gif.t crashes here then run Makefile.PL with
---nogifsetversion, eg.:
-
-  perl Makefile.PL --nogifsetversion
-
-or install a less buggy giflib.
+Return true if the file we're creating on these images needs a GIF89a
+header.
 
 =cut
 */
 
-static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
+static int
+need_version_89a(i_quantize *quant, i_img **imgs, int count) {
   int need_89a = 0;
   int temp;
   int i;
 
-  if (quant->transp != tr_none)
-    need_89a = 1;
-  else {
-    for (i = 0; i < count; ++i) {
-      if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) { 
-        need_89a = 1; 
-        break;
-      }
-      if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
-        need_89a = 1; 
-        break;
-      }
-      if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
-        need_89a = 1;
-        break;
-      }
-      if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
-        need_89a = 1;
-        break;
-      }
+  for (i = 0; i < count; ++i) {
+    if (quant->transp != tr_none &&
+       (imgs[i]->channels == 2 || imgs[i]->channels == 4)) {
+      need_89a = 1;
+      break;
+    }
+    if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
+      need_89a = 1;
+      break;
+    }
+    if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
+      need_89a = 1; 
+      break;
+    }
+    if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
+      need_89a = 1;
+      break;
+    }
+    if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
+      need_89a = 1;
+      break;
     }
   }
-  if (need_89a)
-     EGifSetGifVersion("89a");
-  else
-     EGifSetGifVersion("87a");
+
+  return need_89a;
 }
 
 static int 
@@ -1388,9 +1467,9 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   int imgn, orig_count, orig_size;
   int posx, posy;
   int trans_index = -1;
-  int *localmaps;
+  int *localmaps = NULL;
   int anylocal;
-  i_img **glob_imgs; /* images that will use the global color map */
+  i_img **glob_imgs = NULL; /* images that will use the global color map */
   int glob_img_count;
   i_color *orig_colors = quant->mc_colors;
   i_color *glob_colors = NULL;
@@ -1401,6 +1480,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   int want_trans = 0;
   int interlace;
   int gif_background;
+  int error;
 
   mm_log((1, "i_writegif_low(quant %p, gf  %p, imgs %p, count %d)\n", 
          quant, gf, imgs, count));
@@ -1409,7 +1489,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   
   if (count <= 0) {
     i_push_error(0, "No images provided to write");
-    return 0; /* what are you smoking? */
+    goto fail_cleanup;
   }
 
   /* sanity is nice */
@@ -1432,7 +1512,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
     i_img *im = imgs[imgn];
     if (im->xsize > 0xFFFF || im->ysize > 0xFFFF) {
       i_push_error(0, "image too large for GIF");
-      return 0;
+      goto fail_cleanup;
     }
     
     posx = posy = 0;
@@ -1459,7 +1539,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
 
   if (scrw > 0xFFFF || scrh > 0xFFFF) {
     i_push_error(0, "screen size too large for GIF");
-    return 0;
+    goto fail_cleanup;
   }
 
   orig_count = quant->mc_count;
@@ -1507,13 +1587,8 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   }
 
   if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    EGifCloseFile(gf);
     mm_log((1, "Error in MakeMapObject"));
-    return 0;
+    goto fail_cleanup;
   }
   color_bits = 1;
   if (anylocal) {
@@ -1532,17 +1607,11 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   
   if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 
                         gif_background, map) == GIF_ERROR) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    gif_push_error();
+    gif_push_error(myGifError(gf));
     i_push_error(0, "Could not save screen descriptor");
     FreeMapObject(map);
-    myfree(result);
-    EGifCloseFile(gf);
     mm_log((1, "Error in EGifPutScreenDesc."));
-    return 0;
+    goto fail_cleanup;
   }
   FreeMapObject(map);
 
@@ -1575,13 +1644,8 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
       i_quant_makemap(quant, imgs, 1);
       colors_paletted = has_common_palette(imgs, 1, quant);
       if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
-       myfree(glob_colors);
-       myfree(localmaps);
-       myfree(glob_imgs);
-        EGifCloseFile(gf);
-        quant->mc_colors = orig_colors;
         mm_log((1, "Error in MakeMapObject"));
-        return 0;
+        goto fail_cleanup;
       }
     }
     else {
@@ -1596,12 +1660,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   else
     result = i_quant_translate(quant, imgs[0]);
   if (!result) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    EGifCloseFile(gf);
-    return 0;
+    goto fail_cleanup;
   }
   if (want_trans) {
     i_quant_transparent(quant, result, imgs[0], quant->mc_count);
@@ -1609,60 +1668,36 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   }
 
   if (!do_ns_loop(gf, imgs[0])) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    return 0;
+    goto fail_cleanup;
   }
 
   if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    myfree(result);
-    EGifCloseFile(gf);
-    return 0;
+    goto fail_cleanup;
   }
 
   if (!do_comments(gf, imgs[0])) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    myfree(result);
-    EGifCloseFile(gf);
-    return 0;
+    goto fail_cleanup;
   }
 
   if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
     interlace = 0;
   if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize, 
                        interlace, map) == GIF_ERROR) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    gif_push_error();
+    if (map)
+      FreeMapObject(map);
+    gif_push_error(myGifError(gf));
     i_push_error(0, "Could not save image descriptor");
-    EGifCloseFile(gf);
     mm_log((1, "Error in EGifPutImageDesc."));
-    return 0;
+    goto fail_cleanup;
   }
   if (map)
     FreeMapObject(map);
 
   if (!do_write(gf, interlace, imgs[0], result)) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    quant->mc_colors = orig_colors;
-    EGifCloseFile(gf);
-    myfree(result);
-    return 0;
+    goto fail_cleanup;
   }
   myfree(result);
+  result = NULL;
 
   /* that first awful image is out of the way, do the rest */
   for (imgn = 1; imgn < count; ++imgn) {
@@ -1689,13 +1724,8 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
         result = i_quant_translate(quant, imgs[imgn]);
       }
       if (!result) {
-       myfree(glob_colors);
-       myfree(localmaps);
-       myfree(glob_imgs);
-        quant->mc_colors = orig_colors;
-        EGifCloseFile(gf);
         mm_log((1, "error in i_quant_translate()"));
-        return 0;
+        goto fail_cleanup;
       }
       if (want_trans) {
         i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
@@ -1703,14 +1733,8 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
       }
 
       if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
-       myfree(glob_colors);
-       myfree(localmaps);
-       myfree(glob_imgs);
-        quant->mc_colors = orig_colors;
-        myfree(result);
-        EGifCloseFile(gf);
         mm_log((1, "Error in MakeMapObject."));
-        return 0;
+        goto fail_cleanup;
       }
     }
     else {
@@ -1729,23 +1753,11 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
     }
 
     if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
-      myfree(glob_colors);
-      myfree(localmaps);
-      myfree(glob_imgs);
-      quant->mc_colors = orig_colors;
-      myfree(result);
-      EGifCloseFile(gf);
-      return 0;
+      goto fail_cleanup;
     }
 
     if (!do_comments(gf, imgs[imgn])) {
-      myfree(glob_colors);
-      myfree(localmaps);
-      myfree(glob_imgs);
-      quant->mc_colors = orig_colors;
-      myfree(result);
-      EGifCloseFile(gf);
-      return 0;
+      goto fail_cleanup;
     }
 
     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
@@ -1757,42 +1769,27 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
       interlace = 0;
     if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize, 
                          imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
-      myfree(glob_colors);
-      myfree(localmaps);
-      myfree(glob_imgs);
-      quant->mc_colors = orig_colors;
-      gif_push_error();
+      gif_push_error(myGifError(gf));
       i_push_error(0, "Could not save image descriptor");
-      myfree(result);
       if (map)
         FreeMapObject(map);
-      EGifCloseFile(gf);
       mm_log((1, "Error in EGifPutImageDesc."));
-      return 0;
+      goto fail_cleanup;
     }
     if (map)
       FreeMapObject(map);
     
     if (!do_write(gf, interlace, imgs[imgn], result)) {
-      myfree(glob_colors);
-      myfree(localmaps);
-      myfree(glob_imgs);
-      quant->mc_colors = orig_colors;
-      EGifCloseFile(gf);
-      myfree(result);
-      return 0;
+      goto fail_cleanup;
     }
     myfree(result);
+    result = NULL;
   }
 
-  if (EGifCloseFile(gf) == GIF_ERROR) {
-    myfree(glob_colors);
-    myfree(localmaps);
-    myfree(glob_imgs);
-    gif_push_error();
+  if (myEGifCloseFile(gf, &error) == GIF_ERROR) {
+    gif_push_error(error);
     i_push_error(0, "Could not close GIF file");
-    mm_log((1, "Error in EGifCloseFile\n"));
-    return 0;
+    goto fail_cleanup;
   }
   if (glob_colors) {
     int i;
@@ -1806,6 +1803,15 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   quant->mc_colors = orig_colors;
 
   return 1;
+
+ fail_cleanup:
+  quant->mc_colors = orig_colors;
+  myfree(result);
+  myfree(glob_colors);
+  myfree(localmaps);
+  myfree(glob_imgs);
+  (void)myEGifCloseFile(gf, &error);
+  return 0;
 }
 
 static int
@@ -1825,25 +1831,32 @@ undef_int
 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
                 int count) {
   GifFileType *GifFile;
+  int gif_error;
   int result;
 
-  i_mutex_lock(mutex);
+  gif_mutex_lock(mutex);
 
   i_clear_error();
 
-  gif_set_version(quant, imgs, count);
+#ifdef PRE_SET_VERSION
+  EGifSetGifVersion(need_version_89a(quant, imgs, count) ? "89a" : "87a");
+#endif
   
-  if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
-    gif_push_error();
+  if ((GifFile = myEGifOpen((void *)ig, io_glue_write_cb, &gif_error )) == NULL) {
+    gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
-    i_mutex_unlock(mutex);
+    gif_mutex_unlock(mutex);
     return 0;
   }
   
+#ifdef POST_SET_VERSION
+  EGifSetGifVersion(GifFile, need_version_89a(quant, imgs, count));
+#endif
+
   result = i_writegif_low(quant, GifFile, imgs, count);
   
-  i_mutex_unlock(mutex);
+  gif_mutex_unlock(mutex);
 
   if (i_io_close(ig))
     return 0;
@@ -1857,15 +1870,16 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
 Grabs the most recent giflib error code from GifLastError() and 
 returns a string that describes that error.
 
-The returned pointer points to a static buffer, either from a literal
-C string or a static buffer.
+Returns NULL for unknown error codes.
 
 =cut
 */
 
-static char const *gif_error_msg(int code) {
-  static char msg[80];
-
+static char const *
+gif_error_msg(int code) {
+#if IMGIFLIB_API_VERSION >= 500
+  return GifErrorString(code);
+#else
   switch (code) {
   case E_GIF_ERR_OPEN_FAILED: /* should not see this */
     return "Failed to open given file";
@@ -1937,17 +1951,13 @@ static char const *gif_error_msg(int code) {
     return "Unexpected EOF - invalid file";
 
   default:
-#ifdef IMAGER_SNPRINTF
-    snprintf(msg, sizeof(msg), "Unknown giflib error code %d", code);
-#else
-    sprintf(msg, "Unknown giflib error code %d", code);
-#endif
-    return msg;
+    return NULL;
   }
+#endif
 }
 
 /*
-=item gif_push_error()
+=item gif_push_error(code)
 
 Utility function that takes the current GIF error code, converts it to
 an error message and pushes it on the error stack.
@@ -1955,27 +1965,16 @@ an error message and pushes it on the error stack.
 =cut
 */
 
-static void gif_push_error(void) {
-  int code = GifLastError(); /* clears saved error */
-
-  i_push_error(code, gif_error_msg(code));
+static void
+gif_push_error(int code) {
+  const char *msg = gif_error_msg(code);
+  if (msg)
+    i_push_error(code, msg);
+  else
+    i_push_errorf(code, "Unknown GIF error %d", code);
 }
 
 /*
-=head1 BUGS
-
-The Netscape loop extension isn't implemented.  Giflib's extension
-writing code doesn't seem to support writing named extensions in this 
-form.
-
-A bug in giflib is tickled by the i_writegif_callback().  This isn't a
-problem on ungiflib, but causes a SEGV on giflib.  A patch is provided
-in t/t10formats.t
-
-The GIF file tag (GIF87a vs GIF89a) currently isn't set.  Using the
-supplied interface in giflib 4.1.0 causes a SEGV in
-EGifSetGifVersion().  See L<gif_set_version> for an explanation.
-
 =head1 AUTHOR
 
 Arnar M. Hrafnkelsson, addi@umich.edu