long delayed renaming of m_fatal() to i_fatal() to match Imager's
[imager.git] / gif.c
1 #include "imageri.h"
2 #include <gif_lib.h>
3 #ifdef _MSCVER
4 #include <io.h>
5 #else
6 #include <unistd.h>
7 #endif
8 #include <errno.h>
9 /* XXX: Reading still needs to support reading all those gif properties */
10
11 /*
12 =head1 NAME
13
14 gif.c - read and write gif files for Imager
15
16 =head1 SYNOPSIS
17
18   i_img *img;
19   i_img *imgs[count];
20   int fd;
21   int *colour_table,
22   int colours;
23   int max_colours; // number of bits per colour
24   int pixdev;  // how much noise to add 
25   i_color fixed[N]; // fixed palette entries 
26   int fixedlen; // number of fixed colours 
27   int success; // non-zero on success
28   char *data; // a GIF file in memory
29   int length; // how big data is 
30   int reader(char *, char *, int, int);
31   int writer(char *, char *, int);
32   char *userdata; // user's data, whatever it is
33   i_quantize quant;
34   i_gif_opts opts;
35
36   img = i_readgif(fd, &colour_table, &colours);
37   success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
38   success = i_writegifmc(img, fd, max_colours);
39   img = i_readgif_scalar(data, length, &colour_table, &colours);
40   img = i_readgif_callback(cb, userdata, &colour_table, &colours);
41   success = i_writegif_gen(&quant, fd, imgs, count, &opts);
42   success = i_writegif_callback(&quant, writer, userdata, maxlength, 
43                                 imgs, count, &opts);
44
45 =head1 DESCRIPTION
46
47 This source file provides the C level interface to reading and writing
48 GIF files for Imager.
49
50 This has been tested with giflib 3 and 4, though you lose the callback
51 functionality with giflib3.
52
53 =head1 REFERENCE
54
55 =over
56
57 =cut
58 */
59
60 static char const *gif_error_msg(int code);
61 static void gif_push_error(void);
62
63 #if IM_GIFMAJOR >= 4
64
65 static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
66
67 /*
68 =item gif_scalar_info
69
70 Internal.  A structure passed to the reader function used for reading
71 GIFs from scalars.
72
73 Used with giflib 4 and later.
74
75 =cut
76 */
77
78 struct gif_scalar_info {
79   char *data;
80   int length;
81   int cpos;
82 };
83
84 /*
85 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
86
87 Internal.  The reader callback passed to giflib.
88
89 Used with giflib 4 and later.
90
91 =cut
92 */
93
94 int
95 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
96   struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
97   /*   fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
98
99   if (gsi->cpos == gsi->length) return 0;
100   if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
101   memcpy(buf,gsi->data+gsi->cpos,length);
102   gsi->cpos+=length;
103   return length;
104 }
105
106 #endif
107
108
109
110 /* Make some variables global, so we could access them faster: */
111
112 static int
113   InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
114   InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
115
116
117
118 static
119 void
120 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
121   GifColorType *mapentry;
122   int q;
123   int colourmapsize = colourmap->ColorCount;
124
125   if(colours) *colours = colourmapsize;
126   if(!colour_table) return;
127   
128   *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
129   memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
130
131   for(q=0; q<colourmapsize; q++) {
132     mapentry = &colourmap->Colors[q];
133     (*colour_table)[q*3 + 0] = mapentry->Red;
134     (*colour_table)[q*3 + 1] = mapentry->Green;
135     (*colour_table)[q*3 + 2] = mapentry->Blue;
136   }
137 }
138
139
140 /*
141 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
142
143 Internal.  Low-level function for reading a GIF file.  The caller must
144 create the appropriate GifFileType object and pass it in.
145
146 =cut
147 */
148
149 i_img *
150 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
151   i_img *im;
152   int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
153   int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
154   ColorMapObject *ColorMap;
155  
156   GifRecordType RecordType;
157   GifByteType *Extension;
158   
159   GifRowType GifRow;
160   static GifColorType *ColorMapEntry;
161   i_color col;
162
163   mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
164
165   /* it's possible that the caller has called us with *colour_table being
166      non-NULL, but we check that to see if we need to free an allocated
167      colour table on error.
168   */
169   if (colour_table) *colour_table = NULL;
170
171   BackGround = GifFile->SBackGroundColor;
172   ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
173
174   if (ColorMap) {
175     ColorMapSize = ColorMap->ColorCount;
176     i_colortable_copy(colour_table, colours, ColorMap);
177     cmapcnt++;
178   }
179   
180   if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
181     if (colour_table && *colour_table) {
182       myfree(*colour_table);
183       *colour_table = NULL;
184     }
185     DGifCloseFile(GifFile);
186     mm_log((1, "i_readgif: image size exceeds limits\n"));
187     return NULL;
188   }
189
190   im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
191   if (!im) {
192     if (colour_table && *colour_table) {
193       myfree(*colour_table);
194       *colour_table = NULL;
195     }
196     DGifCloseFile(GifFile);
197     return NULL;
198   }
199
200   Size = GifFile->SWidth * sizeof(GifPixelType); 
201   
202   GifRow = mymalloc(Size);
203
204   for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
205   
206   /* Scan the content of the GIF file and load the image(s) in: */
207   do {
208     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
209       gif_push_error();
210       i_push_error(0, "Unable to get record type");
211       if (colour_table && *colour_table) {
212         myfree(*colour_table);
213         *colour_table = NULL;
214       }
215       myfree(GifRow);
216       i_img_destroy(im);
217       DGifCloseFile(GifFile);
218       return NULL;
219     }
220     
221     switch (RecordType) {
222     case IMAGE_DESC_RECORD_TYPE:
223       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
224         gif_push_error();
225         i_push_error(0, "Unable to get image descriptor");
226         if (colour_table && *colour_table) {
227           myfree(*colour_table);
228           *colour_table = NULL;
229         }
230         myfree(GifRow);
231         i_img_destroy(im);
232         DGifCloseFile(GifFile);
233         return NULL;
234       }
235
236       if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
237         mm_log((1, "Adding local colormap\n"));
238         ColorMapSize = ColorMap->ColorCount;
239         if ( cmapcnt == 0) {
240           i_colortable_copy(colour_table, colours, ColorMap);
241           cmapcnt++;
242         }
243       } else {
244         /* No colormap and we are about to read in the image - abandon for now */
245         mm_log((1, "Going in with no colormap\n"));
246         i_push_error(0, "Image does not have a local or a global color map");
247         /* we can't have allocated a colour table here */
248         myfree(GifRow);
249         i_img_destroy(im);
250         DGifCloseFile(GifFile);
251         return NULL;
252       }
253       
254       Row = GifFile->Image.Top; /* Image Position relative to Screen. */
255       Col = GifFile->Image.Left;
256       Width = GifFile->Image.Width;
257       Height = GifFile->Image.Height;
258       ImageNum++;
259       mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
260
261       if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
262           GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
263         i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
264         if (colour_table && *colour_table) {
265           myfree(*colour_table);
266           *colour_table = NULL;
267         }
268         myfree(GifRow);
269         i_img_destroy(im);
270         DGifCloseFile(GifFile);
271         return NULL;
272       }
273       if (GifFile->Image.Interlace) {
274
275         for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
276           Count++;
277           if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
278             gif_push_error();
279             i_push_error(0, "Reading GIF line");
280             if (colour_table && *colour_table) {
281               myfree(*colour_table);
282               *colour_table = NULL;
283             }
284             myfree(GifRow);
285             i_img_destroy(im);
286             DGifCloseFile(GifFile);
287             return NULL;
288           }
289           
290           for (x = 0; x < Width; x++) {
291             ColorMapEntry = &ColorMap->Colors[GifRow[x]];
292             col.rgb.r = ColorMapEntry->Red;
293             col.rgb.g = ColorMapEntry->Green;
294             col.rgb.b = ColorMapEntry->Blue;
295             i_ppix(im,Col+x,j,&col);
296           }
297           
298         }
299       }
300       else {
301         for (i = 0; i < Height; i++) {
302           if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
303             gif_push_error();
304             i_push_error(0, "Reading GIF line");
305             if (colour_table && *colour_table) {
306               myfree(*colour_table);
307               *colour_table = NULL;
308             }
309             myfree(GifRow);
310             i_img_destroy(im);
311             DGifCloseFile(GifFile);
312             return NULL;
313           }
314
315           for (x = 0; x < Width; x++) {
316             ColorMapEntry = &ColorMap->Colors[GifRow[x]];
317             col.rgb.r = ColorMapEntry->Red;
318             col.rgb.g = ColorMapEntry->Green;
319             col.rgb.b = ColorMapEntry->Blue;
320             i_ppix(im, Col+x, Row, &col);
321           }
322           Row++;
323         }
324       }
325       break;
326     case EXTENSION_RECORD_TYPE:
327       /* Skip any extension blocks in file: */
328       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
329         gif_push_error();
330         i_push_error(0, "Reading extension record");
331         if (colour_table && *colour_table) {
332           myfree(*colour_table);
333           *colour_table = NULL;
334         }
335         myfree(GifRow);
336         i_img_destroy(im);
337         DGifCloseFile(GifFile);
338         return NULL;
339       }
340       while (Extension != NULL) {
341         if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
342           gif_push_error();
343           i_push_error(0, "reading next block of extension");
344           if (colour_table && *colour_table) {
345             myfree(*colour_table);
346             *colour_table = NULL;
347           }
348           myfree(GifRow);
349           i_img_destroy(im);
350           DGifCloseFile(GifFile);
351           return NULL;
352         }
353       }
354       break;
355     case TERMINATE_RECORD_TYPE:
356       break;
357     default:                /* Should be traps by DGifGetRecordType. */
358       break;
359     }
360   } while (RecordType != TERMINATE_RECORD_TYPE);
361   
362   myfree(GifRow);
363   
364   if (DGifCloseFile(GifFile) == GIF_ERROR) {
365     gif_push_error();
366     i_push_error(0, "Closing GIF file object");
367     if (colour_table && *colour_table) {
368       myfree(*colour_table);
369       *colour_table = NULL;
370     }
371     i_img_destroy(im);
372     return NULL;
373   }
374
375   i_tags_add(&im->tags, "i_format", 0, "gif", -1, 0);
376
377   return im;
378 }
379
380 /*
381 =item i_readgif(int fd, int **colour_table, int *colours)
382
383 Reads in a GIF file from a file handle and converts it to an Imager
384 RGB image object.
385
386 Returns the palette for the object in colour_table for colours
387 colours.
388
389 Returns NULL on failure.
390
391 =cut
392 */
393
394 i_img *
395 i_readgif(int fd, int **colour_table, int *colours) {
396   GifFileType *GifFile;
397
398   i_clear_error();
399   
400   mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
401
402   if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
403     gif_push_error();
404     i_push_error(0, "Cannot create giflib file object");
405     mm_log((1,"i_readgif: Unable to open file\n"));
406     return NULL;
407   }
408
409   return i_readgif_low(GifFile, colour_table, colours);
410 }
411
412 /*
413
414 Internal function called by i_readgif_multi_low() in error handling
415
416 */
417 static void free_images(i_img **imgs, int count) {
418   int i;
419   
420   if (count) {
421     for (i = 0; i < count; ++i)
422       i_img_destroy(imgs[i]);
423     myfree(imgs);
424   }
425 }
426
427 /*
428 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
429
430 Reads one of more gif images from the given GIF file.
431
432 Returns a pointer to an array of i_img *, and puts the count into 
433 *count.
434
435 If page is not -1 then the given image _only_ is returned from the
436 file, where the first image is 0, the second 1 and so on.
437
438 Unlike the normal i_readgif*() functions the images are paletted
439 images rather than a combined RGB image.
440
441 This functions sets tags on the images returned:
442
443 =over
444
445 =item gif_left
446
447 the offset of the image from the left of the "screen" ("Image Left
448 Position")
449
450 =item gif_top
451
452 the offset of the image from the top of the "screen" ("Image Top Position")
453
454 =item gif_interlace
455
456 non-zero if the image was interlaced ("Interlace Flag")
457
458 =item gif_screen_width
459
460 =item gif_screen_height
461
462 the size of the logical screen ("Logical Screen Width", 
463 "Logical Screen Height")
464
465 =item gif_local_map
466
467 Non-zero if this image had a local color map.
468
469 =item gif_background
470
471 The index in the global colormap of the logical screen's background
472 color.  This is only set if the current image uses the global
473 colormap.
474
475 =item gif_trans_index
476
477 The index of the color in the colormap used for transparency.  If the
478 image has a transparency then it is returned as a 4 channel image with
479 the alpha set to zero in this palette entry. ("Transparent Color Index")
480
481 =item gif_delay
482
483 The delay until the next frame is displayed, in 1/100 of a second. 
484 ("Delay Time").
485
486 =item gif_user_input
487
488 whether or not a user input is expected before continuing (view dependent) 
489 ("User Input Flag").
490
491 =item gif_disposal
492
493 how the next frame is displayed ("Disposal Method")
494
495 =item gif_loop
496
497 the number of loops from the Netscape Loop extension.  This may be zero.
498
499 =item gif_comment
500
501 the first block of the first gif comment before each image.
502
503 =back
504
505 Where applicable, the ("name") is the name of that field from the GIF89 
506 standard.
507
508 =cut
509 */
510
511 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
512   i_img *img;
513   int i, j, Size, Width, Height, ExtCode, Count;
514   int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
515   ColorMapObject *ColorMap;
516  
517   GifRecordType RecordType;
518   GifByteType *Extension;
519   
520   GifRowType GifRow;
521   int got_gce = 0;
522   int trans_index; /* transparent index if we see a GCE */
523   int gif_delay; /* delay from a GCE */
524   int user_input; /* user input flag from a GCE */
525   int disposal; /* disposal method from a GCE */
526   int got_ns_loop = 0;
527   int ns_loop;
528   char *comment = NULL; /* a comment */
529   i_img **results = NULL;
530   int result_alloc = 0;
531   int channels;
532   
533   *count = 0;
534
535   mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
536
537   BackGround = GifFile->SBackGroundColor;
538
539   Size = GifFile->SWidth * sizeof(GifPixelType);
540   
541   if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
542     i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
543
544   /* Scan the content of the GIF file and load the image(s) in: */
545   do {
546     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
547       gif_push_error();
548       i_push_error(0, "Unable to get record type");
549       free_images(results, *count);
550       DGifCloseFile(GifFile);
551       myfree(GifRow);
552       return NULL;
553     }
554     
555     switch (RecordType) {
556     case IMAGE_DESC_RECORD_TYPE:
557       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
558         gif_push_error();
559         i_push_error(0, "Unable to get image descriptor");
560         free_images(results, *count);
561         DGifCloseFile(GifFile);
562         myfree(GifRow);
563         return NULL;
564       }
565
566       Width = GifFile->Image.Width;
567       Height = GifFile->Image.Height;
568       if (page == -1 || page == ImageNum) {
569         if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
570           mm_log((1, "Adding local colormap\n"));
571           ColorMapSize = ColorMap->ColorCount;
572         } else {
573           /* No colormap and we are about to read in the image - 
574              abandon for now */
575           mm_log((1, "Going in with no colormap\n"));
576           i_push_error(0, "Image does not have a local or a global color map");
577           free_images(results, *count);
578           DGifCloseFile(GifFile);
579           myfree(GifRow);
580           return NULL;
581         }
582         
583         channels = 3;
584         if (got_gce && trans_index >= 0)
585           channels = 4;
586         if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
587           free_images(results, *count);
588           mm_log((1, "i_readgif: image size exceeds limits\n"));
589           DGifCloseFile(GifFile);
590           myfree(GifRow);
591           return NULL;
592         }
593         img = i_img_pal_new(Width, Height, channels, 256);
594         if (!img) {
595           free_images(results, *count);
596           DGifCloseFile(GifFile);
597           return NULL;
598         }
599         /* populate the palette of the new image */
600         mm_log((1, "ColorMapSize %d\n", ColorMapSize));
601         for (i = 0; i < ColorMapSize; ++i) {
602           i_color col;
603           col.rgba.r = ColorMap->Colors[i].Red;
604           col.rgba.g = ColorMap->Colors[i].Green;
605           col.rgba.b = ColorMap->Colors[i].Blue;
606           if (channels == 4 && trans_index == i)
607             col.rgba.a = 0;
608           else
609             col.rgba.a = 255;
610           
611           i_addcolors(img, &col, 1);
612         }
613         ++*count;
614         if (*count > result_alloc) {
615           if (result_alloc == 0) {
616             result_alloc = 5;
617             results = mymalloc(result_alloc * sizeof(i_img *));
618           }
619           else {
620             /* myrealloc never fails (it just dies if it can't allocate) */
621             result_alloc *= 2;
622             results = myrealloc(results, result_alloc * sizeof(i_img *));
623           }
624         }
625         results[*count-1] = img;
626         i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
627         i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
628         /**(char *)0 = 1;*/
629         i_tags_addn(&img->tags, "gif_top",  0, GifFile->Image.Top);
630         i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
631         i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
632         i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
633         if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
634           i_tags_addn(&img->tags, "gif_background", 0, 
635                       GifFile->SBackGroundColor);
636         }
637         if (GifFile->Image.ColorMap) {
638           i_tags_addn(&img->tags, "gif_localmap", 0, 1);
639         }
640         if (got_gce) {
641           if (trans_index >= 0) {
642             i_color trans;
643             i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
644             i_getcolors(img, trans_index, &trans, 1);
645             i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
646           }
647           i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
648           i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
649           i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
650         }
651         got_gce = 0;
652         if (got_ns_loop)
653           i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
654         if (comment) {
655           i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
656           myfree(comment);
657           comment = NULL;
658         }
659         
660         mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
661                 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
662         
663         if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
664             GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
665           i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
666           free_images(results, *count);        
667           DGifCloseFile(GifFile);
668           myfree(GifRow);
669           return(0);
670         }
671         
672         if (GifFile->Image.Interlace) {
673           for (Count = i = 0; i < 4; i++) {
674             for (j = InterlacedOffset[i]; j < Height; 
675                  j += InterlacedJumps[i]) {
676               Count++;
677               if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
678                 gif_push_error();
679                 i_push_error(0, "Reading GIF line");
680                 free_images(results, *count);
681                 DGifCloseFile(GifFile);
682                 myfree(GifRow);
683                 return NULL;
684               }
685               
686               i_ppal(img, 0, Width, j, GifRow);
687             }
688           }
689         }
690         else {
691           for (i = 0; i < Height; i++) {
692             if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
693               gif_push_error();
694               i_push_error(0, "Reading GIF line");
695               free_images(results, *count);
696               DGifCloseFile(GifFile);
697               myfree(GifRow);
698               return NULL;
699             }
700             
701             i_ppal(img, 0, Width, i, GifRow);
702           }
703         }
704
705         /* must be only one image wanted and that was it */
706         if (page != -1) {
707           myfree(GifRow);
708           DGifCloseFile(GifFile);
709           return results;
710         }
711       }
712       else {
713         /* skip the image */
714         /* whether interlaced or not, it has the same number of lines */
715         /* giflib does't have an interface to skip the image data */
716         for (i = 0; i < Height; i++) {
717           if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
718             gif_push_error();
719             i_push_error(0, "Reading GIF line");
720             free_images(results, *count);
721             myfree(GifRow);
722             DGifCloseFile(GifFile);
723             return NULL;
724           }
725         }
726
727         /* kill the comment so we get the right comment for the page */
728         if (comment) {
729           myfree(comment);
730           comment = NULL;
731         }
732       }
733       ImageNum++;
734       break;
735     case EXTENSION_RECORD_TYPE:
736       /* Skip any extension blocks in file: */
737       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
738         gif_push_error();
739         i_push_error(0, "Reading extension record");
740         free_images(results, *count);
741         DGifCloseFile(GifFile);
742         return NULL;
743       }
744       if (ExtCode == 0xF9) {
745         got_gce = 1;
746         if (Extension[1] & 1)
747           trans_index = Extension[4];
748         else
749           trans_index = -1;
750         gif_delay = Extension[2] + 256 * Extension[3];
751         user_input = (Extension[0] & 2) != 0;
752         disposal = (Extension[0] >> 2) & 3;
753       }
754       if (ExtCode == 0xFF && *Extension == 11) {
755         if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
756           if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
757             gif_push_error();
758             i_push_error(0, "reading loop extension");
759             free_images(results, *count);
760             DGifCloseFile(GifFile);
761             return NULL;
762           }
763           if (Extension && *Extension == 3) {
764             got_ns_loop = 1;
765             ns_loop = Extension[2] + 256 * Extension[3];
766           }
767         }
768       }
769       else if (ExtCode == 0xFE) {
770         /* while it's possible for a GIF file to contain more than one
771            comment, I'm only implementing a single comment per image, 
772            with the comment saved into the following image.
773            If someone wants more than that they can implement it.
774            I also don't handle comments that take more than one block.
775         */
776         if (!comment) {
777           comment = mymalloc(*Extension+1);
778           memcpy(comment, Extension+1, *Extension);
779           comment[*Extension] = '\0';
780         }
781       }
782       while (Extension != NULL) {
783         if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
784           gif_push_error();
785           i_push_error(0, "reading next block of extension");
786           free_images(results, *count);
787           DGifCloseFile(GifFile);
788           return NULL;
789         }
790       }
791       break;
792     case TERMINATE_RECORD_TYPE:
793       break;
794     default:                /* Should be trapped by DGifGetRecordType. */
795       break;
796     }
797   } while (RecordType != TERMINATE_RECORD_TYPE);
798
799   if (comment) {
800     if (*count) {
801       i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment, 
802                  strlen(comment), 0);
803     }
804     myfree(comment);
805   }
806   
807   myfree(GifRow);
808   
809   if (DGifCloseFile(GifFile) == GIF_ERROR) {
810     gif_push_error();
811     i_push_error(0, "Closing GIF file object");
812     free_images(results, *count);
813     return NULL;
814   }
815
816   if (ImageNum && page != -1) {
817     /* there were images, but the page selected wasn't found */
818     i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
819     free_images(results, *count);
820     return NULL;
821   }
822
823   return results;
824 }
825
826 #if IM_GIFMAJOR >= 4
827 /* giflib declares this incorrectly as EgifOpen */
828 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
829
830 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
831 #endif
832
833 /*
834 =item i_readgif_multi_wiol(ig, int *count)
835
836 =cut
837 */
838
839 i_img **
840 i_readgif_multi_wiol(io_glue *ig, int *count) {
841   io_glue_commit_types(ig);
842
843   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
844     return i_readgif_multi(ig->source.fdseek.fd, count);
845   }
846   else {
847 #if IM_GIFMAJOR >= 4
848     GifFileType *GifFile;
849
850     i_clear_error();
851
852     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
853       gif_push_error();
854       i_push_error(0, "Cannot create giflib callback object");
855       mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
856       return NULL;
857     }
858     
859     return i_readgif_multi_low(GifFile, count, -1);
860 #else
861     i_clear_error();
862     i_push_error(0, "callbacks not supported with giflib3");
863     
864     return NULL;
865 #endif
866   }
867 }
868
869 /*
870 =item i_readgif_multi(int fd, int *count)
871
872 =cut
873 */
874 i_img **
875 i_readgif_multi(int fd, int *count) {
876   GifFileType *GifFile;
877
878   i_clear_error();
879   
880   mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
881
882   if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
883     gif_push_error();
884     i_push_error(0, "Cannot create giflib file object");
885     mm_log((1,"i_readgif: Unable to open file\n"));
886     return NULL;
887   }
888
889   return i_readgif_multi_low(GifFile, count, -1);
890 }
891
892 /*
893 =item i_readgif_multi_scalar(char *data, int length, int *count)
894
895 =cut
896 */
897 i_img **
898 i_readgif_multi_scalar(char *data, int length, int *count) {
899 #if IM_GIFMAJOR >= 4
900   GifFileType *GifFile;
901   struct gif_scalar_info gsi;
902
903   i_clear_error();
904   
905   gsi.cpos=0;
906   gsi.length=length;
907   gsi.data=data;
908
909   mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n", 
910           data, length, count));
911
912   if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
913     gif_push_error();
914     i_push_error(0, "Cannot create giflib callback object");
915     mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
916     return NULL;
917   }
918
919   return i_readgif_multi_low(GifFile, count, -1);
920 #else
921   return NULL;
922 #endif
923 }
924
925 /*
926 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
927
928 Read a GIF file into an Imager RGB file, the data of the GIF file is
929 retreived by callin the user supplied callback function.
930
931 This function is only used with giflib 4 and higher.
932
933 =cut
934 */
935
936 i_img**
937 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
938 #if IM_GIFMAJOR >= 4
939   GifFileType *GifFile;
940   i_img **result;
941
942   i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
943
944   i_clear_error();
945   
946   mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
947   if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
948     gif_push_error();
949     i_push_error(0, "Cannot create giflib callback object");
950     mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
951     myfree(gci);
952     return NULL;
953   }
954
955   result = i_readgif_multi_low(GifFile, count, -1);
956   i_free_gen_read_data(gci);
957
958   return result;
959 #else
960   return NULL;
961 #endif
962 }
963
964 /*
965 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
966
967 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
968 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
969 colours taken from I<fixed>.
970
971 Returns non-zero on success.
972
973 =cut
974 */
975
976 undef_int
977 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
978   i_color colors[256];
979   i_quantize quant;
980   
981   memset(&quant, 0, sizeof(quant));
982   quant.make_colors = mc_addi;
983   quant.mc_colors = colors;
984   quant.mc_size = 1<<max_colors;
985   quant.mc_count = fixedlen;
986   memcpy(colors, fixed, fixedlen * sizeof(i_color));
987   quant.translate = pt_perturb;
988   quant.perturb = pixdev;
989   return i_writegif_gen(&quant, fd, &im, 1);
990 }
991
992 /*
993 =item i_writegifmc(i_img *im, int fd, int max_colors)
994
995 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
996 maximum of 1<<I<max_colours> colours.
997
998 Returns non-zero on success.
999
1000 =cut
1001 */
1002
1003 undef_int
1004 i_writegifmc(i_img *im, int fd, int max_colors) {
1005   i_color colors[256];
1006   i_quantize quant;
1007
1008 /*    *(char *)0 = 1; */
1009   
1010   memset(&quant, 0, sizeof(quant));
1011   quant.make_colors = mc_none; /* ignored for pt_giflib */
1012   quant.mc_colors = colors;
1013   quant.mc_size = 1 << max_colors;
1014   quant.mc_count = 0;
1015   quant.translate = pt_giflib;
1016   return i_writegif_gen(&quant, fd, &im, 1);
1017 }
1018
1019
1020 /*
1021 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1022
1023 Reads a GIF file from an in memory copy of the file.  This can be used
1024 if you get the 'file' from some source other than an actual file (or
1025 some other file handle).
1026
1027 This function is only available with giflib 4 and higher.
1028
1029 =cut
1030 */
1031 i_img*
1032 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1033 #if IM_GIFMAJOR >= 4
1034   GifFileType *GifFile;
1035   struct gif_scalar_info gsi;
1036
1037   i_clear_error();
1038
1039   gsi.cpos=0;
1040   gsi.length=length;
1041   gsi.data=data;
1042
1043   mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1044   if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1045     gif_push_error();
1046     i_push_error(0, "Cannot create giflib callback object");
1047     mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1048     return NULL;
1049   }
1050
1051   return i_readgif_low(GifFile, colour_table, colours);
1052 #else
1053   return NULL;
1054 #endif
1055 }
1056
1057 #if IM_GIFMAJOR >= 4
1058
1059 /*
1060 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1061
1062 Internal.  The reader callback wrapper passed to giflib.
1063
1064 This function is only used with giflib 4 and higher.
1065
1066 =cut
1067 */
1068
1069 static int
1070 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1071   return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1072 }
1073
1074 #endif
1075
1076
1077 /*
1078 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1079
1080 Read a GIF file into an Imager RGB file, the data of the GIF file is
1081 retreived by callin the user supplied callback function.
1082
1083 This function is only used with giflib 4 and higher.
1084
1085 =cut
1086 */
1087
1088 i_img*
1089 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1090 #if IM_GIFMAJOR >= 4
1091   GifFileType *GifFile;
1092   i_img *result;
1093
1094   i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1095
1096   i_clear_error();
1097   
1098   mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1099   if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1100     gif_push_error();
1101     i_push_error(0, "Cannot create giflib callback object");
1102     mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1103     myfree(gci);
1104     return NULL;
1105   }
1106
1107   result = i_readgif_low(GifFile, colour_table, colours);
1108   i_free_gen_read_data(gci);
1109
1110   return result;
1111 #else
1112   i_clear_error();
1113   i_push_error(0, "callbacks not supported with giflib3");
1114
1115   return NULL;
1116 #endif
1117 }
1118
1119 #if IM_GIFMAJOR >= 4
1120
1121 static int
1122 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1123   io_glue *ig = (io_glue *)gft->UserData;
1124
1125   return ig->readcb(ig, buf, length);
1126 }
1127
1128 #endif
1129
1130 i_img *
1131 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1132   io_glue_commit_types(ig);
1133
1134   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1135     int fd = dup(ig->source.fdseek.fd);
1136     if (fd < 0) {
1137       i_push_error(errno, "dup() failed");
1138       return 0;
1139     }
1140     return i_readgif(fd, color_table, colors);
1141   }
1142   else {
1143 #if IM_GIFMAJOR >= 4
1144     GifFileType *GifFile;
1145
1146     i_clear_error();
1147
1148     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1149       gif_push_error();
1150       i_push_error(0, "Cannot create giflib callback object");
1151       mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1152       return NULL;
1153     }
1154     
1155     return i_readgif_low(GifFile, color_table, colors);
1156   
1157 #else
1158   i_clear_error();
1159   i_push_error(0, "callbacks not supported with giflib3");
1160
1161   return NULL;
1162 #endif
1163   }
1164 }
1165
1166 /*
1167 =item i_readgif_single_low(GifFile, page)
1168
1169 Lower level function to read a single image from a GIF.
1170
1171 page must be non-negative.
1172
1173 =cut
1174 */
1175 static i_img *
1176 i_readgif_single_low(GifFileType *GifFile, int page) {
1177   int count = 0;
1178   i_img **imgs;
1179
1180   imgs = i_readgif_multi_low(GifFile, &count, page);
1181
1182   if (imgs && count) {
1183     i_img *result = imgs[0];
1184
1185     myfree(imgs);
1186     return result;
1187   }
1188   else {
1189     /* i_readgif_multi_low() handles the errors appropriately */
1190     return NULL;
1191   }
1192 }
1193
1194 /*
1195 =item i_readgif_single_wiol(ig, page)
1196
1197 Read a single page from a GIF image file, where the page is indexed
1198 from 0.
1199
1200 Returns NULL if the page isn't found.
1201
1202 =cut
1203 */
1204
1205 i_img *
1206 i_readgif_single_wiol(io_glue *ig, int page) {
1207   io_glue_commit_types(ig);
1208
1209   i_clear_error();
1210
1211   if (page < 0) {
1212     i_push_error(0, "page must be non-negative");
1213     return NULL;
1214   }
1215
1216   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1217     GifFileType *GifFile;
1218     int fd = dup(ig->source.fdseek.fd);
1219     if (fd < 0) {
1220       i_push_error(errno, "dup() failed");
1221       return NULL;
1222     }
1223     if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1224       gif_push_error();
1225       i_push_error(0, "Cannot create giflib file object");
1226       mm_log((1,"i_readgif: Unable to open file\n"));
1227       return NULL;
1228     }
1229     return i_readgif_single_low(GifFile, page);
1230   }
1231   else {
1232 #if IM_GIFMAJOR >= 4
1233     GifFileType *GifFile;
1234
1235     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1236       gif_push_error();
1237       i_push_error(0, "Cannot create giflib callback object");
1238       mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1239       return NULL;
1240     }
1241     
1242     return i_readgif_single_low(GifFile, page);
1243 #else
1244     i_push_error(0, "callbacks not supported with giflib3");
1245
1246     return NULL;
1247 #endif
1248   }
1249 }
1250
1251 /*
1252 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1253
1254 Internal.  Low level image write function.  Writes in interlace if
1255 that was requested in the GIF options.
1256
1257 Returns non-zero on success.
1258
1259 =cut
1260 */
1261 static undef_int 
1262 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1263   if (interlace) {
1264     int i, j;
1265     for (i = 0; i < 4; ++i) {
1266       for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1267         if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1268           gif_push_error();
1269           i_push_error(0, "Could not save image data:");
1270           mm_log((1, "Error in EGifPutLine\n"));
1271           EGifCloseFile(gf);
1272           return 0;
1273         }
1274       }
1275     }
1276   }
1277   else {
1278     int y;
1279     for (y = 0; y < img->ysize; ++y) {
1280       if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1281         gif_push_error();
1282         i_push_error(0, "Could not save image data:");
1283         mm_log((1, "Error in EGifPutLine\n"));
1284         EGifCloseFile(gf);
1285         return 0;
1286       }
1287       data += img->xsize;
1288     }
1289   }
1290
1291   return 1;
1292 }
1293
1294 /*
1295 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1296
1297 Internal. Writes the GIF graphics control extension, if necessary.
1298
1299 Returns non-zero on success.
1300
1301 =cut
1302 */
1303 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1304 {
1305   unsigned char gce[4] = {0};
1306   int want_gce = 0;
1307   int delay;
1308   int user_input;
1309   int disposal_method;
1310
1311   if (want_trans) {
1312     gce[0] |= 1;
1313     gce[3] = trans_index;
1314     ++want_gce;
1315   }
1316   if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1317     gce[1] = delay % 256;
1318     gce[2] = delay / 256;
1319     ++want_gce;
1320   }
1321   if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input) 
1322       && user_input) {
1323     gce[0] |= 2;
1324     ++want_gce;
1325   }
1326   if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1327     gce[0] |= (disposal_method & 3) << 2;
1328     ++want_gce;
1329   }
1330   if (want_gce) {
1331     if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1332       gif_push_error();
1333       i_push_error(0, "Could not save GCE");
1334     }
1335   }
1336   return 1;
1337 }
1338
1339 /*
1340 =item do_comments(gf, img)
1341
1342 Write any comments in the image.
1343
1344 =cut
1345 */
1346 static int do_comments(GifFileType *gf, i_img *img) {
1347   int pos = -1;
1348
1349   while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1350     if (img->tags.tags[pos].data) {
1351       if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1352         return 0;
1353       }
1354     }
1355     else {
1356       char buf[50];
1357       sprintf(buf, "%d", img->tags.tags[pos].idata);
1358       if (EGifPutComment(gf, buf) == GIF_ERROR) {
1359         return 0;
1360       }
1361     }
1362   }
1363
1364   return 1;
1365 }
1366
1367 /*
1368 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1369
1370 Internal.  Add the Netscape2.0 loop extension block, if requested.
1371
1372 The code for this function is currently "#if 0"ed out since the giflib
1373 extension writing code currently doesn't seem to support writing
1374 application extension blocks.
1375
1376 =cut
1377 */
1378 static int do_ns_loop(GifFileType *gf, i_img *img)
1379 {
1380   /* EGifPutExtension() doesn't appear to handle application 
1381      extension blocks in any way
1382      Since giflib wraps the fd with a FILE * (and puts that in its
1383      private data), we can't do an end-run and write the data 
1384      directly to the fd.
1385      There's no open interface that takes a FILE * either, so we 
1386      can't workaround it that way either.
1387      If giflib's callback interface wasn't broken by default, I'd 
1388      force file writes to use callbacks, but it is broken by default.
1389   */
1390 #if 0
1391   /* yes this was another attempt at supporting the loop extension */
1392   int loop_count;
1393   if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1394     unsigned char nsle[12] = "NETSCAPE2.0";
1395     unsigned char subblock[3];
1396     if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1397       gif_push_error();
1398       i_push_error(0, "writing loop extension");
1399       return 0;
1400     }
1401     subblock[0] = 1;
1402     subblock[1] = loop_count % 256;
1403     subblock[2] = loop_count / 256;
1404     if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1405       gif_push_error();
1406       i_push_error(0, "writing loop extention sub-block");
1407       return 0;
1408     }
1409     if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1410       gif_push_error();
1411       i_push_error(0, "writing loop extension terminator");
1412       return 0;
1413     }
1414   }
1415 #endif
1416   return 1;
1417 }
1418
1419 /*
1420 =item make_gif_map(i_quantize *quant, int want_trans)
1421
1422 Create a giflib color map object from an Imager color map.
1423
1424 =cut
1425 */
1426
1427 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img, 
1428                                     int want_trans) {
1429   GifColorType colors[256];
1430   int i;
1431   int size = quant->mc_count;
1432   int map_size;
1433   ColorMapObject *map;
1434   i_color trans;
1435
1436   for (i = 0; i < quant->mc_count; ++i) {
1437     colors[i].Red = quant->mc_colors[i].rgb.r;
1438     colors[i].Green = quant->mc_colors[i].rgb.g;
1439     colors[i].Blue = quant->mc_colors[i].rgb.b;
1440   }
1441   if (want_trans) {
1442     if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1443       trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1444     colors[size].Red = trans.rgb.r;
1445     colors[size].Green = trans.rgb.g;
1446     colors[size].Blue = trans.rgb.b;
1447     ++size;
1448   }
1449   map_size = 1;
1450   while (map_size < size)
1451     map_size <<= 1;
1452   /* giflib spews for 1 colour maps, reasonable, I suppose */
1453   if (map_size == 1)
1454     map_size = 2;
1455   while (i < map_size) {
1456     colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1457     ++i;
1458   }
1459   
1460   map = MakeMapObject(map_size, colors);
1461   mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1462   if (!map) {
1463     gif_push_error();
1464     i_push_error(0, "Could not create color map object");
1465     return NULL;
1466   }
1467   return map;
1468 }
1469
1470 /*
1471 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1472
1473 We need to call EGifSetGifVersion() before opening the file - put that
1474 common code here.
1475
1476 Unfortunately giflib 4.1.0 crashes when we use this.  Internally
1477 giflib 4.1.0 has code:
1478
1479   static char *GifVersionPrefix = GIF87_STAMP;
1480
1481 and the code that sets the version internally does:
1482
1483   strncpy(&GifVersionPrefix[3], Version, 3);
1484
1485 which is very broken.
1486
1487 Failing to set the correct GIF version doesn't seem to cause a problem
1488 with readers.
1489
1490 =cut
1491 */
1492
1493 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1494   /* the following crashed giflib
1495      the EGifSetGifVersion() is seriously borked in giflib
1496      it's less borked in the ungiflib beta, but we don't have a mechanism
1497      to distinguish them
1498      Needs to be updated to support tags.
1499      if (opts->delay_count
1500      || opts->user_input_count
1501      || opts->disposal_count
1502      || opts->loop_count
1503      || quant->transp != tr_none)
1504      EGifSetGifVersion("89a");
1505      else
1506      EGifSetGifVersion("87a");
1507   */
1508 }
1509
1510 static int 
1511 in_palette(i_color *c, i_quantize *quant, int size) {
1512   int i;
1513
1514   for (i = 0; i < size; ++i) {
1515     if (c->channel[0] == quant->mc_colors[i].channel[0]
1516         && c->channel[1] == quant->mc_colors[i].channel[1]
1517         && c->channel[2] == quant->mc_colors[i].channel[2]) {
1518       return i;
1519     }
1520   }
1521
1522   return -1;
1523 }
1524
1525 /*
1526 =item has_common_palette(imgs, count, quant, want_trans)
1527
1528 Tests if all the given images are paletted and have a common palette,
1529 if they do it builds that palette.
1530
1531 A possible improvement might be to eliminate unused colors in the
1532 images palettes.
1533
1534 =cut
1535 */
1536 static int
1537 has_common_palette(i_img **imgs, int count, i_quantize *quant, 
1538                    int want_trans) {
1539   int size = quant->mc_count;
1540   int i;
1541   int imgn;
1542   char used[256];
1543
1544   /* we try to build a common palette here, if we can manage that, then
1545      that's the palette we use */
1546   for (imgn = 0; imgn < count; ++imgn) {
1547     int eliminate_unused;
1548     if (imgs[imgn]->type != i_palette_type)
1549       return 0;
1550
1551     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0, 
1552                         &eliminate_unused)) {
1553       eliminate_unused = 1;
1554     }
1555
1556     if (eliminate_unused) {
1557       i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1558       int x, y;
1559       memset(used, 0, sizeof(used));
1560
1561       for (y = 0; y < imgs[imgn]->ysize; ++y) {
1562         i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1563         for (x = 0; x < imgs[imgn]->xsize; ++x)
1564           used[line[x]] = 1;
1565       }
1566
1567       myfree(line);
1568     }
1569     else {
1570       /* assume all are in use */
1571       memset(used, 1, sizeof(used));
1572     }
1573
1574     for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1575       i_color c;
1576       
1577       i_getcolors(imgs[imgn], i, &c, 1);
1578       if (used[i]) {
1579         if (in_palette(&c, quant, size) < 0) {
1580           if (size < quant->mc_size) {
1581             quant->mc_colors[size++] = c;
1582           }
1583           else {
1584             /* oops, too many colors */
1585             return 0;
1586           }
1587         }
1588       }
1589     }
1590   }
1591
1592   quant->mc_count = size;
1593
1594   return 1;
1595 }
1596
1597 static i_palidx *
1598 quant_paletted(i_quantize *quant, i_img *img) {
1599   i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1600   i_palidx *p = data;
1601   i_palidx trans[256];
1602   int i;
1603   int x, y;
1604
1605   /* build a translation table */
1606   for (i = 0; i < i_colorcount(img); ++i) {
1607     i_color c;
1608     i_getcolors(img, i, &c, 1);
1609     trans[i] = in_palette(&c, quant, quant->mc_count);
1610   }
1611
1612   for (y = 0; y < img->ysize; ++y) {
1613     i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1614     for (x = 0; x < img->xsize; ++x) {
1615       *p = trans[*p];
1616       ++p;
1617     }
1618   }
1619
1620   return data;
1621 }
1622
1623 /*
1624 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1625
1626 Internal.  Low-level function that does the high-level GIF processing
1627 :)
1628
1629 Returns non-zero on success.
1630
1631 =cut
1632 */
1633
1634 static undef_int
1635 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1636   unsigned char *result;
1637   int color_bits;
1638   ColorMapObject *map;
1639   int scrw = 0, scrh = 0;
1640   int imgn, orig_count, orig_size;
1641   int posx, posy;
1642   int trans_index;
1643   i_mempool mp;
1644   int *localmaps;
1645   int anylocal;
1646   i_img **glob_imgs; /* images that will use the global color map */
1647   int glob_img_count;
1648   i_color *orig_colors = quant->mc_colors;
1649   i_color *glob_colors = NULL;
1650   int glob_color_count;
1651   int glob_want_trans;
1652   int glob_paletted; /* the global map was made from the image palettes */
1653   int colors_paletted;
1654   int want_trans;
1655   int interlace;
1656   int gif_background;
1657
1658   mm_log((1, "i_writegif_low(quant %p, gf  %p, imgs %p, count %d)\n", 
1659           quant, gf, imgs, count));
1660   
1661   /* *((char *)0) = 1; */ /* used to break into the debugger */
1662   
1663   if (count <= 0) {
1664     i_push_error(0, "No images provided to write");
1665     return 0; /* what are you smoking? */
1666   }
1667
1668   i_mempool_init(&mp);
1669
1670   /* sanity is nice */
1671   if (quant->mc_size > 256) 
1672     quant->mc_size = 256;
1673   if (quant->mc_count > quant->mc_size)
1674     quant->mc_count = quant->mc_size;
1675
1676   if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1677     scrw = 0;
1678   if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1679     scrw = 0;
1680
1681   anylocal = 0;
1682   localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1683   glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1684   glob_img_count = 0;
1685   glob_want_trans = 0;
1686   for (imgn = 0; imgn < count; ++imgn) {
1687     posx = posy = 0;
1688     i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1689     i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1690     if (imgs[imgn]->xsize + posx > scrw)
1691       scrw = imgs[imgn]->xsize + posx;
1692     if (imgs[imgn]->ysize + posy > scrh)
1693       scrh = imgs[imgn]->ysize + posy;
1694     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1695       localmaps[imgn] = 0;
1696     if (localmaps[imgn])
1697       anylocal = 1;
1698     else {
1699       if (imgs[imgn]->channels == 4) {
1700         glob_want_trans = 1;
1701       }
1702       glob_imgs[glob_img_count++] = imgs[imgn];
1703     }
1704   }
1705   glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1706
1707   orig_count = quant->mc_count;
1708   orig_size = quant->mc_size;
1709
1710   if (glob_img_count) {
1711     /* this is ugly */
1712     glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1713     quant->mc_colors = glob_colors;
1714     memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1715     /* we have some images that want to use the global map */
1716     if (glob_want_trans && quant->mc_count == 256) {
1717       mm_log((2, "  disabling transparency for global map - no space\n"));
1718       glob_want_trans = 0;
1719     }
1720     if (glob_want_trans && quant->mc_size == 256) {
1721       mm_log((2, "  reserving color for transparency\n"));
1722       --quant->mc_size;
1723     }
1724     if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1725       glob_paletted = 1;
1726     }
1727     else {
1728       glob_paletted = 0;
1729       i_quant_makemap(quant, glob_imgs, glob_img_count);
1730     }
1731     glob_color_count = quant->mc_count;
1732     quant->mc_colors = orig_colors;
1733   }
1734
1735   /* use the global map if we have one, otherwise use the local map */
1736   gif_background = 0;
1737   if (glob_colors) {
1738     quant->mc_colors = glob_colors;
1739     quant->mc_count = glob_color_count;
1740     want_trans = glob_want_trans && imgs[0]->channels == 4;
1741
1742     if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1743       gif_background = 0;
1744     if (gif_background < 0)
1745       gif_background = 0;
1746     if (gif_background >= glob_color_count)
1747       gif_background = 0;
1748   }
1749   else {
1750     want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1751     if (has_common_palette(imgs, 1, quant, want_trans)) {
1752       colors_paletted = 1;
1753     }
1754     else {
1755       colors_paletted = 0;
1756       i_quant_makemap(quant, imgs, 1);
1757     }
1758   }
1759   if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1760     i_mempool_destroy(&mp);
1761     quant->mc_colors = orig_colors;
1762     EGifCloseFile(gf);
1763     mm_log((1, "Error in MakeMapObject"));
1764     return 0;
1765   }
1766   color_bits = 1;
1767   if (anylocal) {
1768     /* since we don't know how big some the local palettes could be
1769        we need to base the bits on the maximum number of colors */
1770     while (orig_size > (1 << color_bits))
1771       ++color_bits;
1772   }
1773   else {
1774     int count = quant->mc_count;
1775     if (want_trans)
1776       ++count;
1777     while (count > (1 << color_bits))
1778       ++color_bits;
1779   }
1780   
1781   if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 
1782                         gif_background, map) == GIF_ERROR) {
1783     i_mempool_destroy(&mp);
1784     quant->mc_colors = orig_colors;
1785     gif_push_error();
1786     i_push_error(0, "Could not save screen descriptor");
1787     FreeMapObject(map);
1788     myfree(result);
1789     EGifCloseFile(gf);
1790     mm_log((1, "Error in EGifPutScreenDesc."));
1791     return 0;
1792   }
1793   FreeMapObject(map);
1794
1795   if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1796     posx = 0;
1797   if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1798     posy = 0;
1799
1800   if (!localmaps[0]) {
1801     map = NULL;
1802     colors_paletted = glob_paletted;
1803   }
1804   else {
1805     /* if this image has a global map the colors in quant don't
1806        belong to this image, so build a palette */
1807     if (glob_colors) {
1808       /* generate the local map for this image */
1809       quant->mc_colors = orig_colors;
1810       quant->mc_size = orig_size;
1811       quant->mc_count = orig_count;
1812       want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1813
1814       /* if the caller gives us too many colours we can't do transparency */
1815       if (want_trans && quant->mc_count == 256)
1816         want_trans = 0;
1817       /* if they want transparency but give us a big size, make it smaller
1818          to give room for a transparency colour */
1819       if (want_trans && quant->mc_size == 256)
1820         --quant->mc_size;
1821       if (has_common_palette(imgs, 1, quant, want_trans)) {
1822         colors_paletted = 1;
1823       }
1824       else {
1825         colors_paletted = 0;
1826         i_quant_makemap(quant, imgs, 1);
1827       }
1828       if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1829         i_mempool_destroy(&mp);
1830         EGifCloseFile(gf);
1831         quant->mc_colors = orig_colors;
1832         mm_log((1, "Error in MakeMapObject"));
1833         return 0;
1834       }
1835     }
1836     else {
1837       /* the map we wrote was the map for this image - don't set the local 
1838          map */
1839       map = NULL;
1840     }
1841   }
1842
1843   if (colors_paletted)
1844     result = quant_paletted(quant, imgs[0]);
1845   else
1846     result = i_quant_translate(quant, imgs[0]);
1847   if (!result) {
1848     i_mempool_destroy(&mp);
1849     quant->mc_colors = orig_colors;
1850     EGifCloseFile(gf);
1851     return 0;
1852   }
1853   if (want_trans) {
1854     i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1855     trans_index = quant->mc_count;
1856   }
1857
1858   if (!do_ns_loop(gf, imgs[0])) {
1859     i_mempool_destroy(&mp);
1860     quant->mc_colors = orig_colors;
1861     return 0;
1862   }
1863
1864   if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1865     i_mempool_destroy(&mp);
1866     quant->mc_colors = orig_colors;
1867     myfree(result);
1868     EGifCloseFile(gf);
1869     return 0;
1870   }
1871
1872   if (!do_comments(gf, imgs[0])) {
1873     i_mempool_destroy(&mp);
1874     quant->mc_colors = orig_colors;
1875     myfree(result);
1876     EGifCloseFile(gf);
1877     return 0;
1878   }
1879
1880   if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1881     interlace = 0;
1882   if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize, 
1883                        interlace, map) == GIF_ERROR) {
1884     i_mempool_destroy(&mp);
1885     quant->mc_colors = orig_colors;
1886     gif_push_error();
1887     i_push_error(0, "Could not save image descriptor");
1888     EGifCloseFile(gf);
1889     mm_log((1, "Error in EGifPutImageDesc."));
1890     return 0;
1891   }
1892   if (map)
1893     FreeMapObject(map);
1894
1895   if (!do_write(gf, interlace, imgs[0], result)) {
1896     i_mempool_destroy(&mp);
1897     quant->mc_colors = orig_colors;
1898     EGifCloseFile(gf);
1899     myfree(result);
1900     return 0;
1901   }
1902   myfree(result);
1903
1904   /* that first awful image is out of the way, do the rest */
1905   for (imgn = 1; imgn < count; ++imgn) {
1906     if (localmaps[imgn]) {
1907       quant->mc_colors = orig_colors;
1908       quant->mc_count = orig_count;
1909       quant->mc_size = orig_size;
1910
1911       want_trans = quant->transp != tr_none 
1912         && imgs[imgn]->channels == 4;
1913       /* if the caller gives us too many colours we can't do transparency */
1914       if (want_trans && quant->mc_count == 256)
1915         want_trans = 0;
1916       /* if they want transparency but give us a big size, make it smaller
1917          to give room for a transparency colour */
1918       if (want_trans && quant->mc_size == 256)
1919         --quant->mc_size;
1920
1921       if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1922         result = quant_paletted(quant, imgs[imgn]);
1923       }
1924       else {
1925         i_quant_makemap(quant, imgs+imgn, 1);
1926         result = i_quant_translate(quant, imgs[imgn]);
1927       }
1928       if (!result) {
1929         i_mempool_destroy(&mp);
1930         quant->mc_colors = orig_colors;
1931         EGifCloseFile(gf);
1932         mm_log((1, "error in i_quant_translate()"));
1933         return 0;
1934       }
1935       if (want_trans) {
1936         i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1937         trans_index = quant->mc_count;
1938       }
1939
1940       if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1941         i_mempool_destroy(&mp);
1942         quant->mc_colors = orig_colors;
1943         myfree(result);
1944         EGifCloseFile(gf);
1945         mm_log((1, "Error in MakeMapObject."));
1946         return 0;
1947       }
1948     }
1949     else {
1950       quant->mc_colors = glob_colors;
1951       quant->mc_count = glob_color_count;
1952       if (glob_paletted)
1953         result = quant_paletted(quant, imgs[imgn]);
1954       else
1955         result = i_quant_translate(quant, imgs[imgn]);
1956       want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1957       if (want_trans) {
1958         i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1959         trans_index = quant->mc_count;
1960       }
1961       map = NULL;
1962     }
1963
1964     if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1965       i_mempool_destroy(&mp);
1966       quant->mc_colors = orig_colors;
1967       myfree(result);
1968       EGifCloseFile(gf);
1969       return 0;
1970     }
1971
1972     if (!do_comments(gf, imgs[imgn])) {
1973       i_mempool_destroy(&mp);
1974       quant->mc_colors = orig_colors;
1975       myfree(result);
1976       EGifCloseFile(gf);
1977       return 0;
1978     }
1979
1980     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1981       posx = 0;
1982     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1983       posy = 0;
1984
1985     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1986       interlace = 0;
1987     if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize, 
1988                          imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1989       i_mempool_destroy(&mp);
1990       quant->mc_colors = orig_colors;
1991       gif_push_error();
1992       i_push_error(0, "Could not save image descriptor");
1993       myfree(result);
1994       if (map)
1995         FreeMapObject(map);
1996       EGifCloseFile(gf);
1997       mm_log((1, "Error in EGifPutImageDesc."));
1998       return 0;
1999     }
2000     if (map)
2001       FreeMapObject(map);
2002     
2003     if (!do_write(gf, interlace, imgs[imgn], result)) {
2004       i_mempool_destroy(&mp);
2005       quant->mc_colors = orig_colors;
2006       EGifCloseFile(gf);
2007       myfree(result);
2008       return 0;
2009     }
2010     myfree(result);
2011   }
2012
2013   if (EGifCloseFile(gf) == GIF_ERROR) {
2014     i_mempool_destroy(&mp);
2015     gif_push_error();
2016     i_push_error(0, "Could not close GIF file");
2017     mm_log((1, "Error in EGifCloseFile\n"));
2018     return 0;
2019   }
2020   if (glob_colors) {
2021     int i;
2022     for (i = 0; i < glob_color_count; ++i)
2023       orig_colors[i] = glob_colors[i];
2024   }
2025
2026   i_mempool_destroy(&mp);
2027   quant->mc_colors = orig_colors;
2028
2029   return 1;
2030 }
2031
2032 /*
2033 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2034
2035 General high-level function to write a GIF to a file.
2036
2037 Writes the GIF images to the specified file handle using the options
2038 in quant and opts.  See L<image.h/i_quantize> and
2039 L<image.h/i_gif_opts>.
2040
2041 Returns non-zero on success.
2042
2043 =cut
2044 */
2045
2046 undef_int
2047 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2048   GifFileType *gf;
2049
2050   i_clear_error();
2051   mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n", 
2052           quant, fd, imgs, count));
2053
2054   gif_set_version(quant, imgs, count);
2055
2056   if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2057     gif_push_error();
2058     i_push_error(0, "Cannot create GIF file object");
2059     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2060     return 0;
2061   }
2062
2063   return i_writegif_low(quant, gf, imgs, count);
2064 }
2065
2066 #if IM_GIFMAJOR >= 4
2067
2068 /*
2069 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2070
2071 Internal.  Wrapper for the user write callback function.
2072
2073 =cut
2074 */
2075
2076 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2077 {
2078   i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2079
2080   return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2081 }
2082
2083 #endif
2084
2085 /*
2086 =item i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata, int maxlength, i_img **imgs, int count, i_gif_opts *opts)
2087
2088 General high-level function to write a GIF using callbacks to send
2089 back the data.
2090
2091 Returns non-zero on success.
2092
2093 =cut
2094 */
2095
2096 undef_int
2097 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2098                     int maxlength, i_img **imgs, int count)
2099 {
2100 #if IM_GIFMAJOR >= 4
2101   GifFileType *gf;
2102   i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2103   int result;
2104
2105   i_clear_error();
2106
2107   mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n", 
2108           quant, cb, userdata, maxlength, imgs, count));
2109   
2110   if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2111     gif_push_error();
2112     i_push_error(0, "Cannot create GIF file object");
2113     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2114     i_free_gen_write_data(gwd, 0);
2115     return 0;
2116   }
2117
2118   result = i_writegif_low(quant, gf, imgs, count);
2119   return i_free_gen_write_data(gwd, result);
2120 #else
2121   i_clear_error();
2122   i_push_error(0, "callbacks not supported with giflib3");
2123
2124   return 0;
2125 #endif
2126 }
2127
2128 #if IM_GIFMAJOR >= 4
2129
2130 static int
2131 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2132   io_glue *ig = (io_glue *)gft->UserData;
2133
2134   return ig->writecb(ig, data, length);
2135 }
2136
2137 #endif
2138
2139 /*
2140 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2141
2142 =cut
2143 */
2144 undef_int
2145 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2146                 int count) {
2147   io_glue_commit_types(ig);
2148
2149   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2150     int fd = dup(ig->source.fdseek.fd);
2151     if (fd < 0) {
2152       i_push_error(errno, "dup() failed");
2153       return 0;
2154     }
2155     /* giflib opens the fd with fdopen(), which is then closed when fclose()
2156        is called - dup it so the caller's fd isn't closed */
2157     return i_writegif_gen(quant, fd, imgs, count);
2158   }
2159   else {
2160 #if IM_GIFMAJOR >= 4
2161     GifFileType *GifFile;
2162     int result;
2163
2164     i_clear_error();
2165
2166     gif_set_version(quant, imgs, count);
2167
2168     if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2169       gif_push_error();
2170       i_push_error(0, "Cannot create giflib callback object");
2171       mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2172       return 0;
2173     }
2174     
2175     result = i_writegif_low(quant, GifFile, imgs, count);
2176     
2177     ig->closecb(ig);
2178
2179     return result;
2180 #else
2181     i_clear_error();
2182     i_push_error(0, "callbacks not supported with giflib3");
2183     
2184     return 0;
2185 #endif
2186   }
2187 }
2188
2189 /*
2190 =item gif_error_msg(int code)
2191
2192 Grabs the most recent giflib error code from GifLastError() and 
2193 returns a string that describes that error.
2194
2195 The returned pointer points to a static buffer, either from a literal
2196 C string or a static buffer.
2197
2198 =cut
2199 */
2200
2201 static char const *gif_error_msg(int code) {
2202   static char msg[80];
2203
2204   switch (code) {
2205   case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2206     return "Failed to open given file";
2207     
2208   case E_GIF_ERR_WRITE_FAILED:
2209     return "Write failed";
2210
2211   case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2212     return "Screen descriptor already passed to giflib";
2213
2214   case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2215     return "Image descriptor already passed to giflib";
2216     
2217   case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2218     return "Neither global nor local color map set";
2219
2220   case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2221     return "Too much pixel data passed to giflib";
2222
2223   case E_GIF_ERR_NOT_ENOUGH_MEM:
2224     return "Out of memory";
2225     
2226   case E_GIF_ERR_DISK_IS_FULL:
2227     return "Disk is full";
2228     
2229   case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2230     return "File close failed";
2231  
2232   case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2233     return "File not writable";
2234
2235   case D_GIF_ERR_OPEN_FAILED:
2236     return "Failed to open file";
2237     
2238   case D_GIF_ERR_READ_FAILED:
2239     return "Failed to read from file";
2240
2241   case D_GIF_ERR_NOT_GIF_FILE:
2242     return "File is not a GIF file";
2243
2244   case D_GIF_ERR_NO_SCRN_DSCR:
2245     return "No screen descriptor detected - invalid file";
2246
2247   case D_GIF_ERR_NO_IMAG_DSCR:
2248     return "No image descriptor detected - invalid file";
2249
2250   case D_GIF_ERR_NO_COLOR_MAP:
2251     return "No global or local color map found";
2252
2253   case D_GIF_ERR_WRONG_RECORD:
2254     return "Wrong record type detected - invalid file?";
2255
2256   case D_GIF_ERR_DATA_TOO_BIG:
2257     return "Data in file too big for image";
2258
2259   case D_GIF_ERR_NOT_ENOUGH_MEM:
2260     return "Out of memory";
2261
2262   case D_GIF_ERR_CLOSE_FAILED:
2263     return "Close failed";
2264
2265   case D_GIF_ERR_NOT_READABLE:
2266     return "File not opened for read";
2267
2268   case D_GIF_ERR_IMAGE_DEFECT:
2269     return "Defective image";
2270
2271   case D_GIF_ERR_EOF_TOO_SOON:
2272     return "Unexpected EOF - invalid file";
2273
2274   default:
2275     sprintf(msg, "Unknown giflib error code %d", code);
2276     return msg;
2277   }
2278 }
2279
2280 /*
2281 =item gif_push_error()
2282
2283 Utility function that takes the current GIF error code, converts it to
2284 an error message and pushes it on the error stack.
2285
2286 =cut
2287 */
2288
2289 static void gif_push_error(void) {
2290   int code = GifLastError(); /* clears saved error */
2291
2292   i_push_error(code, gif_error_msg(code));
2293 }
2294
2295 /*
2296 =head1 BUGS
2297
2298 The Netscape loop extension isn't implemented.  Giflib's extension
2299 writing code doesn't seem to support writing named extensions in this 
2300 form.
2301
2302 A bug in giflib is tickled by the i_writegif_callback().  This isn't a
2303 problem on ungiflib, but causes a SEGV on giflib.  A patch is provided
2304 in t/t10formats.t
2305
2306 The GIF file tag (GIF87a vs GIF89a) currently isn't set.  Using the
2307 supplied interface in giflib 4.1.0 causes a SEGV in
2308 EGifSetGifVersion().  See L<gif_set_version> for an explanation.
2309
2310 =head1 AUTHOR
2311
2312 Arnar M. Hrafnkelsson, addi@umich.edu
2313
2314 =head1 SEE ALSO
2315
2316 perl(1), Imager(3)
2317
2318 =cut
2319
2320 */