]> git.imager.perl.org - imager.git/blobdiff - gif.c
RT 58761: fix Imager->new(data => $data)
[imager.git] / gif.c
diff --git a/gif.c b/gif.c
index 84b51bf17bff752cf1cc83317faccc1a44d75233..ae365372661426a7e3ac980eae3c2944b3e9313c 100644 (file)
--- a/gif.c
+++ b/gif.c
@@ -1,6 +1,6 @@
 #include "imageri.h"
 #include <gif_lib.h>
-#ifdef _MSCVER
+#ifdef _MSC_VER
 #include <io.h>
 #else
 #include <unistd.h>
@@ -519,16 +519,21 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
   
   GifRowType GifRow;
   int got_gce = 0;
-  int trans_index; /* transparent index if we see a GCE */
-  int gif_delay; /* delay from a GCE */
-  int user_input; /* user input flag from a GCE */
-  int disposal; /* disposal method from a GCE */
+  int trans_index = 0; /* transparent index if we see a GCE */
+  int gif_delay = 0; /* delay from a GCE */
+  int user_input = 0; /* user input flag from a GCE */
+  int disposal = 0; /* disposal method from a GCE */
   int got_ns_loop = 0;
-  int ns_loop;
+  int ns_loop = 0;
   char *comment = NULL; /* a comment */
   i_img **results = NULL;
   int result_alloc = 0;
   int channels;
+  int image_colors = 0;
+  i_color black; /* used to expand the palette if needed */
+
+  for (i = 0; i < MAXCHANNELS; ++i)
+    black.channel[i] = 0;
   
   *count = 0;
 
@@ -549,6 +554,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
       free_images(results, *count);
       DGifCloseFile(GifFile);
       myfree(GifRow);
+      if (comment)
+       myfree(comment);
       return NULL;
     }
     
@@ -560,6 +567,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
         free_images(results, *count);
        DGifCloseFile(GifFile);
        myfree(GifRow);
+       if (comment)
+         myfree(comment);
        return NULL;
       }
 
@@ -577,6 +586,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          free_images(results, *count);
          DGifCloseFile(GifFile);
          myfree(GifRow);
+         if (comment)
+           myfree(comment);
          return NULL;
        }
        
@@ -588,12 +599,17 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          mm_log((1, "i_readgif: image size exceeds limits\n"));
          DGifCloseFile(GifFile);
          myfree(GifRow);
+         if (comment)
+           myfree(comment);
          return NULL;
        }
        img = i_img_pal_new(Width, Height, channels, 256);
        if (!img) {
          free_images(results, *count);
          DGifCloseFile(GifFile);
+         if (comment)
+           myfree(comment);
+         myfree(GifRow);
          return NULL;
        }
        /* populate the palette of the new image */
@@ -610,6 +626,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          
          i_addcolors(img, &col, 1);
        }
+       image_colors = ColorMapSize;
        ++*count;
        if (*count > result_alloc) {
          if (result_alloc == 0) {
@@ -630,6 +647,7 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
        i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
        i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
+       i_tags_addn(&img->tags, "gif_colormap_size", 0, ColorMapSize);
        if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
          i_tags_addn(&img->tags, "gif_background", 0, 
                      GifFile->SBackGroundColor);
@@ -666,6 +684,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          free_images(results, *count);        
          DGifCloseFile(GifFile);
          myfree(GifRow);
+         if (comment)
+           myfree(comment);
          return(0);
        }
        
@@ -680,9 +700,23 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
                free_images(results, *count);
                DGifCloseFile(GifFile);
                myfree(GifRow);
+               if (comment)
+                 myfree(comment);
                return NULL;
              }
-             
+
+             /* range check the scanline if needed */
+             if (image_colors != 256) {
+               int x;
+               for (x = 0; x < Width; ++x) {
+                 while (GifRow[x] >= image_colors) {
+                   /* expand the palette since a palette index is too big */
+                   i_addcolors(img, &black, 1);
+                   ++image_colors;
+                 }
+               }
+             }
+
              i_ppal(img, 0, Width, j, GifRow);
            }
          }
@@ -695,9 +729,23 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
              free_images(results, *count);
              DGifCloseFile(GifFile);
              myfree(GifRow);
+             if (comment)
+               myfree(comment);
              return NULL;
            }
            
+           /* range check the scanline if needed */
+           if (image_colors != 256) {
+             int x;
+             for (x = 0; x < Width; ++x) {
+               while (GifRow[x] >= image_colors) {
+                 /* expand the palette since a palette index is too big */
+                 i_addcolors(img, &black, 1);
+                 ++image_colors;
+               }
+             }
+           }
+
            i_ppal(img, 0, Width, i, GifRow);
          }
        }
@@ -706,6 +754,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        if (page != -1) {
          myfree(GifRow);
          DGifCloseFile(GifFile);
+         if (comment)
+           myfree(comment);
          return results;
        }
       }
@@ -720,6 +770,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
            free_images(results, *count);
            myfree(GifRow);
            DGifCloseFile(GifFile);
+           if (comment) 
+             myfree(comment);
            return NULL;
          }
        }
@@ -738,9 +790,15 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
        gif_push_error();
        i_push_error(0, "Reading extension record");
         free_images(results, *count);
+       myfree(GifRow);
        DGifCloseFile(GifFile);
+       if (comment)
+         myfree(comment);
        return NULL;
       }
+      /* possibly this should be an error, but "be liberal in what you accept" */
+      if (!Extension)
+       break;
       if (ExtCode == 0xF9) {
         got_gce = 1;
         if (Extension[1] & 1)
@@ -748,8 +806,8 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
         else
           trans_index = -1;
         gif_delay = Extension[2] + 256 * Extension[3];
-        user_input = (Extension[0] & 2) != 0;
-        disposal = (Extension[0] >> 2) & 3;
+        user_input = (Extension[1] & 2) != 0;
+        disposal = (Extension[1] >> 2) & 7;
       }
       if (ExtCode == 0xFF && *Extension == 11) {
         if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
@@ -757,7 +815,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
             gif_push_error();
             i_push_error(0, "reading loop extension");
             free_images(results, *count);
-            DGifCloseFile(GifFile);
+           myfree(GifRow);
+           DGifCloseFile(GifFile);
+           if (comment)
+             myfree(comment);
             return NULL;
           }
           if (Extension && *Extension == 3) {
@@ -784,7 +845,10 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
          gif_push_error();
          i_push_error(0, "reading next block of extension");
           free_images(results, *count);
+         myfree(GifRow);
          DGifCloseFile(GifFile);
+         if (comment)
+           myfree(comment);
          return NULL;
        }
       }
@@ -1369,9 +1433,11 @@ static int do_comments(GifFileType *gf, i_img *img) {
 
 Internal.  Add the Netscape2.0 loop extension block, if requested.
 
-The code for this function is currently "#if 0"ed out since the giflib
-extension writing code currently doesn't seem to support writing
-application extension blocks.
+Giflib/libungif prior to 4.1.1 didn't support writing application
+extension blocks, so we don't attempt to write them for older versions.
+
+Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
+writing extension blocks so that they could only be written to files.
 
 =cut
 */
@@ -1387,13 +1453,13 @@ static int do_ns_loop(GifFileType *gf, i_img *img)
      If giflib's callback interface wasn't broken by default, I'd 
      force file writes to use callbacks, but it is broken by default.
   */
-#if 0
   /* yes this was another attempt at supporting the loop extension */
+#if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
   int loop_count;
   if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
     unsigned char nsle[12] = "NETSCAPE2.0";
     unsigned char subblock[3];
-    if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
+    if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
       gif_push_error();
       i_push_error(0, "writing loop extension");
       return 0;
@@ -1401,18 +1467,14 @@ static int do_ns_loop(GifFileType *gf, i_img *img)
     subblock[0] = 1;
     subblock[1] = loop_count % 256;
     subblock[2] = loop_count / 256;
-    if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
-      gif_push_error();
-      i_push_error(0, "writing loop extention sub-block");
-      return 0;
-    }
-    if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
+    if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
       gif_push_error();
-      i_push_error(0, "writing loop extension terminator");
+      i_push_error(0, "writing loop extension sub-block");
       return 0;
     }
   }
 #endif
+
   return 1;
 }
 
@@ -1487,24 +1549,53 @@ 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.
+
 =cut
 */
 
 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
-  /* the following crashed giflib
-     the EGifSetGifVersion() is seriously borked in giflib
-     it's less borked in the ungiflib beta, but we don't have a mechanism
-     to distinguish them
-     Needs to be updated to support tags.
-     if (opts->delay_count
-     || opts->user_input_count
-     || opts->disposal_count
-     || opts->loop_count
-     || quant->transp != tr_none)
+#if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
+       && !defined(IM_NO_SET_GIF_VERSION)
+  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;
+      }
+    }
+  }
+  if (need_89a)
      EGifSetGifVersion("89a");
-     else
+  else
      EGifSetGifVersion("87a");
-  */
+#endif
 }
 
 static int 
@@ -1633,13 +1724,13 @@ Returns non-zero on success.
 
 static undef_int
 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
-  unsigned char *result;
+  unsigned char *result = NULL;
   int color_bits;
   ColorMapObject *map;
   int scrw = 0, scrh = 0;
   int imgn, orig_count, orig_size;
   int posx, posy;
-  int trans_index;
+  int trans_index = -1;
   i_mempool mp;
   int *localmaps;
   int anylocal;
@@ -1647,11 +1738,11 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
   int glob_img_count;
   i_color *orig_colors = quant->mc_colors;
   i_color *glob_colors = NULL;
-  int glob_color_count;
+  int glob_color_count = 0;
   int glob_want_trans;
-  int glob_paletted; /* the global map was made from the image palettes */
-  int colors_paletted;
-  int want_trans;
+  int glob_paletted = 0; /* the global map was made from the image palettes */
+  int colors_paletted = 0;
+  int want_trans = 0;
   int interlace;
   int gif_background;
 
@@ -1675,7 +1766,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
 
   if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
     scrw = 0;
-  if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
+  if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
     scrw = 0;
 
   anylocal = 0;