]> git.imager.perl.org - imager.git/blob - gif.c
API documentation (mostly)
[imager.git] / gif.c
1 #include "imageri.h"
2 #include <gif_lib.h>
3 #ifdef _MSC_VER
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 = 0; /* transparent index if we see a GCE */
523   int gif_delay = 0; /* delay from a GCE */
524   int user_input = 0; /* user input flag from a GCE */
525   int disposal = 0; /* disposal method from a GCE */
526   int got_ns_loop = 0;
527   int ns_loop = 0;
528   char *comment = NULL; /* a comment */
529   i_img **results = NULL;
530   int result_alloc = 0;
531   int channels;
532   int image_colors = 0;
533   i_color black; /* used to expand the palette if needed */
534
535   for (i = 0; i < MAXCHANNELS; ++i)
536     black.channel[i] = 0;
537   
538   *count = 0;
539
540   mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
541
542   BackGround = GifFile->SBackGroundColor;
543
544   Size = GifFile->SWidth * sizeof(GifPixelType);
545   
546   if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
547     i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
548
549   /* Scan the content of the GIF file and load the image(s) in: */
550   do {
551     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
552       gif_push_error();
553       i_push_error(0, "Unable to get record type");
554       free_images(results, *count);
555       DGifCloseFile(GifFile);
556       myfree(GifRow);
557       return NULL;
558     }
559     
560     switch (RecordType) {
561     case IMAGE_DESC_RECORD_TYPE:
562       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
563         gif_push_error();
564         i_push_error(0, "Unable to get image descriptor");
565         free_images(results, *count);
566         DGifCloseFile(GifFile);
567         myfree(GifRow);
568         return NULL;
569       }
570
571       Width = GifFile->Image.Width;
572       Height = GifFile->Image.Height;
573       if (page == -1 || page == ImageNum) {
574         if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
575           mm_log((1, "Adding local colormap\n"));
576           ColorMapSize = ColorMap->ColorCount;
577         } else {
578           /* No colormap and we are about to read in the image - 
579              abandon for now */
580           mm_log((1, "Going in with no colormap\n"));
581           i_push_error(0, "Image does not have a local or a global color map");
582           free_images(results, *count);
583           DGifCloseFile(GifFile);
584           myfree(GifRow);
585           return NULL;
586         }
587         
588         channels = 3;
589         if (got_gce && trans_index >= 0)
590           channels = 4;
591         if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
592           free_images(results, *count);
593           mm_log((1, "i_readgif: image size exceeds limits\n"));
594           DGifCloseFile(GifFile);
595           myfree(GifRow);
596           return NULL;
597         }
598         img = i_img_pal_new(Width, Height, channels, 256);
599         if (!img) {
600           free_images(results, *count);
601           DGifCloseFile(GifFile);
602           return NULL;
603         }
604         /* populate the palette of the new image */
605         mm_log((1, "ColorMapSize %d\n", ColorMapSize));
606         for (i = 0; i < ColorMapSize; ++i) {
607           i_color col;
608           col.rgba.r = ColorMap->Colors[i].Red;
609           col.rgba.g = ColorMap->Colors[i].Green;
610           col.rgba.b = ColorMap->Colors[i].Blue;
611           if (channels == 4 && trans_index == i)
612             col.rgba.a = 0;
613           else
614             col.rgba.a = 255;
615           
616           i_addcolors(img, &col, 1);
617         }
618         image_colors = ColorMapSize;
619         ++*count;
620         if (*count > result_alloc) {
621           if (result_alloc == 0) {
622             result_alloc = 5;
623             results = mymalloc(result_alloc * sizeof(i_img *));
624           }
625           else {
626             /* myrealloc never fails (it just dies if it can't allocate) */
627             result_alloc *= 2;
628             results = myrealloc(results, result_alloc * sizeof(i_img *));
629           }
630         }
631         results[*count-1] = img;
632         i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
633         i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
634         /**(char *)0 = 1;*/
635         i_tags_addn(&img->tags, "gif_top",  0, GifFile->Image.Top);
636         i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
637         i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
638         i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
639         i_tags_addn(&img->tags, "gif_colormap_size", 0, ColorMapSize);
640         if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
641           i_tags_addn(&img->tags, "gif_background", 0, 
642                       GifFile->SBackGroundColor);
643         }
644         if (GifFile->Image.ColorMap) {
645           i_tags_addn(&img->tags, "gif_localmap", 0, 1);
646         }
647         if (got_gce) {
648           if (trans_index >= 0) {
649             i_color trans;
650             i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
651             i_getcolors(img, trans_index, &trans, 1);
652             i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
653           }
654           i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
655           i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
656           i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
657         }
658         got_gce = 0;
659         if (got_ns_loop)
660           i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
661         if (comment) {
662           i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
663           myfree(comment);
664           comment = NULL;
665         }
666         
667         mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
668                 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
669         
670         if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
671             GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
672           i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
673           free_images(results, *count);        
674           DGifCloseFile(GifFile);
675           myfree(GifRow);
676           return(0);
677         }
678         
679         if (GifFile->Image.Interlace) {
680           for (Count = i = 0; i < 4; i++) {
681             for (j = InterlacedOffset[i]; j < Height; 
682                  j += InterlacedJumps[i]) {
683               Count++;
684               if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
685                 gif_push_error();
686                 i_push_error(0, "Reading GIF line");
687                 free_images(results, *count);
688                 DGifCloseFile(GifFile);
689                 myfree(GifRow);
690                 return NULL;
691               }
692
693               /* range check the scanline if needed */
694               if (image_colors != 256) {
695                 int x;
696                 for (x = 0; x < Width; ++x) {
697                   while (GifRow[x] >= image_colors) {
698                     /* expand the palette since a palette index is too big */
699                     i_addcolors(img, &black, 1);
700                     ++image_colors;
701                   }
702                 }
703               }
704
705               i_ppal(img, 0, Width, j, GifRow);
706             }
707           }
708         }
709         else {
710           for (i = 0; i < Height; i++) {
711             if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
712               gif_push_error();
713               i_push_error(0, "Reading GIF line");
714               free_images(results, *count);
715               DGifCloseFile(GifFile);
716               myfree(GifRow);
717               return NULL;
718             }
719             
720             /* range check the scanline if needed */
721             if (image_colors != 256) {
722               int x;
723               for (x = 0; x < Width; ++x) {
724                 while (GifRow[x] >= image_colors) {
725                   /* expand the palette since a palette index is too big */
726                   i_addcolors(img, &black, 1);
727                   ++image_colors;
728                 }
729               }
730             }
731
732             i_ppal(img, 0, Width, i, GifRow);
733           }
734         }
735
736         /* must be only one image wanted and that was it */
737         if (page != -1) {
738           myfree(GifRow);
739           DGifCloseFile(GifFile);
740           return results;
741         }
742       }
743       else {
744         /* skip the image */
745         /* whether interlaced or not, it has the same number of lines */
746         /* giflib does't have an interface to skip the image data */
747         for (i = 0; i < Height; i++) {
748           if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
749             gif_push_error();
750             i_push_error(0, "Reading GIF line");
751             free_images(results, *count);
752             myfree(GifRow);
753             DGifCloseFile(GifFile);
754             return NULL;
755           }
756         }
757
758         /* kill the comment so we get the right comment for the page */
759         if (comment) {
760           myfree(comment);
761           comment = NULL;
762         }
763       }
764       ImageNum++;
765       break;
766     case EXTENSION_RECORD_TYPE:
767       /* Skip any extension blocks in file: */
768       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
769         gif_push_error();
770         i_push_error(0, "Reading extension record");
771         free_images(results, *count);
772         DGifCloseFile(GifFile);
773         return NULL;
774       }
775       if (ExtCode == 0xF9) {
776         got_gce = 1;
777         if (Extension[1] & 1)
778           trans_index = Extension[4];
779         else
780           trans_index = -1;
781         gif_delay = Extension[2] + 256 * Extension[3];
782         user_input = (Extension[1] & 2) != 0;
783         disposal = (Extension[1] >> 2) & 7;
784       }
785       if (ExtCode == 0xFF && *Extension == 11) {
786         if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
787           if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
788             gif_push_error();
789             i_push_error(0, "reading loop extension");
790             free_images(results, *count);
791             DGifCloseFile(GifFile);
792             return NULL;
793           }
794           if (Extension && *Extension == 3) {
795             got_ns_loop = 1;
796             ns_loop = Extension[2] + 256 * Extension[3];
797           }
798         }
799       }
800       else if (ExtCode == 0xFE) {
801         /* while it's possible for a GIF file to contain more than one
802            comment, I'm only implementing a single comment per image, 
803            with the comment saved into the following image.
804            If someone wants more than that they can implement it.
805            I also don't handle comments that take more than one block.
806         */
807         if (!comment) {
808           comment = mymalloc(*Extension+1);
809           memcpy(comment, Extension+1, *Extension);
810           comment[*Extension] = '\0';
811         }
812       }
813       while (Extension != NULL) {
814         if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
815           gif_push_error();
816           i_push_error(0, "reading next block of extension");
817           free_images(results, *count);
818           DGifCloseFile(GifFile);
819           return NULL;
820         }
821       }
822       break;
823     case TERMINATE_RECORD_TYPE:
824       break;
825     default:                /* Should be trapped by DGifGetRecordType. */
826       break;
827     }
828   } while (RecordType != TERMINATE_RECORD_TYPE);
829
830   if (comment) {
831     if (*count) {
832       i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment, 
833                  strlen(comment), 0);
834     }
835     myfree(comment);
836   }
837   
838   myfree(GifRow);
839   
840   if (DGifCloseFile(GifFile) == GIF_ERROR) {
841     gif_push_error();
842     i_push_error(0, "Closing GIF file object");
843     free_images(results, *count);
844     return NULL;
845   }
846
847   if (ImageNum && page != -1) {
848     /* there were images, but the page selected wasn't found */
849     i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
850     free_images(results, *count);
851     return NULL;
852   }
853
854   return results;
855 }
856
857 #if IM_GIFMAJOR >= 4
858 /* giflib declares this incorrectly as EgifOpen */
859 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
860
861 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
862 #endif
863
864 /*
865 =item i_readgif_multi_wiol(ig, int *count)
866
867 =cut
868 */
869
870 i_img **
871 i_readgif_multi_wiol(io_glue *ig, int *count) {
872   io_glue_commit_types(ig);
873
874   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
875     return i_readgif_multi(ig->source.fdseek.fd, count);
876   }
877   else {
878 #if IM_GIFMAJOR >= 4
879     GifFileType *GifFile;
880
881     i_clear_error();
882
883     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
884       gif_push_error();
885       i_push_error(0, "Cannot create giflib callback object");
886       mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
887       return NULL;
888     }
889     
890     return i_readgif_multi_low(GifFile, count, -1);
891 #else
892     i_clear_error();
893     i_push_error(0, "callbacks not supported with giflib3");
894     
895     return NULL;
896 #endif
897   }
898 }
899
900 /*
901 =item i_readgif_multi(int fd, int *count)
902
903 =cut
904 */
905 i_img **
906 i_readgif_multi(int fd, int *count) {
907   GifFileType *GifFile;
908
909   i_clear_error();
910   
911   mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
912
913   if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
914     gif_push_error();
915     i_push_error(0, "Cannot create giflib file object");
916     mm_log((1,"i_readgif: Unable to open file\n"));
917     return NULL;
918   }
919
920   return i_readgif_multi_low(GifFile, count, -1);
921 }
922
923 /*
924 =item i_readgif_multi_scalar(char *data, int length, int *count)
925
926 =cut
927 */
928 i_img **
929 i_readgif_multi_scalar(char *data, int length, int *count) {
930 #if IM_GIFMAJOR >= 4
931   GifFileType *GifFile;
932   struct gif_scalar_info gsi;
933
934   i_clear_error();
935   
936   gsi.cpos=0;
937   gsi.length=length;
938   gsi.data=data;
939
940   mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n", 
941           data, length, count));
942
943   if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
944     gif_push_error();
945     i_push_error(0, "Cannot create giflib callback object");
946     mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
947     return NULL;
948   }
949
950   return i_readgif_multi_low(GifFile, count, -1);
951 #else
952   return NULL;
953 #endif
954 }
955
956 /*
957 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
958
959 Read a GIF file into an Imager RGB file, the data of the GIF file is
960 retreived by callin the user supplied callback function.
961
962 This function is only used with giflib 4 and higher.
963
964 =cut
965 */
966
967 i_img**
968 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
969 #if IM_GIFMAJOR >= 4
970   GifFileType *GifFile;
971   i_img **result;
972
973   i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
974
975   i_clear_error();
976   
977   mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
978   if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
979     gif_push_error();
980     i_push_error(0, "Cannot create giflib callback object");
981     mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
982     myfree(gci);
983     return NULL;
984   }
985
986   result = i_readgif_multi_low(GifFile, count, -1);
987   i_free_gen_read_data(gci);
988
989   return result;
990 #else
991   return NULL;
992 #endif
993 }
994
995 /*
996 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
997
998 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
999 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
1000 colours taken from I<fixed>.
1001
1002 Returns non-zero on success.
1003
1004 =cut
1005 */
1006
1007 undef_int
1008 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
1009   i_color colors[256];
1010   i_quantize quant;
1011   
1012   memset(&quant, 0, sizeof(quant));
1013   quant.make_colors = mc_addi;
1014   quant.mc_colors = colors;
1015   quant.mc_size = 1<<max_colors;
1016   quant.mc_count = fixedlen;
1017   memcpy(colors, fixed, fixedlen * sizeof(i_color));
1018   quant.translate = pt_perturb;
1019   quant.perturb = pixdev;
1020   return i_writegif_gen(&quant, fd, &im, 1);
1021 }
1022
1023 /*
1024 =item i_writegifmc(i_img *im, int fd, int max_colors)
1025
1026 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
1027 maximum of 1<<I<max_colours> colours.
1028
1029 Returns non-zero on success.
1030
1031 =cut
1032 */
1033
1034 undef_int
1035 i_writegifmc(i_img *im, int fd, int max_colors) {
1036   i_color colors[256];
1037   i_quantize quant;
1038
1039 /*    *(char *)0 = 1; */
1040   
1041   memset(&quant, 0, sizeof(quant));
1042   quant.make_colors = mc_none; /* ignored for pt_giflib */
1043   quant.mc_colors = colors;
1044   quant.mc_size = 1 << max_colors;
1045   quant.mc_count = 0;
1046   quant.translate = pt_giflib;
1047   return i_writegif_gen(&quant, fd, &im, 1);
1048 }
1049
1050
1051 /*
1052 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1053
1054 Reads a GIF file from an in memory copy of the file.  This can be used
1055 if you get the 'file' from some source other than an actual file (or
1056 some other file handle).
1057
1058 This function is only available with giflib 4 and higher.
1059
1060 =cut
1061 */
1062 i_img*
1063 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1064 #if IM_GIFMAJOR >= 4
1065   GifFileType *GifFile;
1066   struct gif_scalar_info gsi;
1067
1068   i_clear_error();
1069
1070   gsi.cpos=0;
1071   gsi.length=length;
1072   gsi.data=data;
1073
1074   mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1075   if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1076     gif_push_error();
1077     i_push_error(0, "Cannot create giflib callback object");
1078     mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1079     return NULL;
1080   }
1081
1082   return i_readgif_low(GifFile, colour_table, colours);
1083 #else
1084   return NULL;
1085 #endif
1086 }
1087
1088 #if IM_GIFMAJOR >= 4
1089
1090 /*
1091 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1092
1093 Internal.  The reader callback wrapper passed to giflib.
1094
1095 This function is only used with giflib 4 and higher.
1096
1097 =cut
1098 */
1099
1100 static int
1101 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1102   return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1103 }
1104
1105 #endif
1106
1107
1108 /*
1109 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1110
1111 Read a GIF file into an Imager RGB file, the data of the GIF file is
1112 retreived by callin the user supplied callback function.
1113
1114 This function is only used with giflib 4 and higher.
1115
1116 =cut
1117 */
1118
1119 i_img*
1120 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1121 #if IM_GIFMAJOR >= 4
1122   GifFileType *GifFile;
1123   i_img *result;
1124
1125   i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1126
1127   i_clear_error();
1128   
1129   mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1130   if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1131     gif_push_error();
1132     i_push_error(0, "Cannot create giflib callback object");
1133     mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1134     myfree(gci);
1135     return NULL;
1136   }
1137
1138   result = i_readgif_low(GifFile, colour_table, colours);
1139   i_free_gen_read_data(gci);
1140
1141   return result;
1142 #else
1143   i_clear_error();
1144   i_push_error(0, "callbacks not supported with giflib3");
1145
1146   return NULL;
1147 #endif
1148 }
1149
1150 #if IM_GIFMAJOR >= 4
1151
1152 static int
1153 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1154   io_glue *ig = (io_glue *)gft->UserData;
1155
1156   return ig->readcb(ig, buf, length);
1157 }
1158
1159 #endif
1160
1161 i_img *
1162 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1163   io_glue_commit_types(ig);
1164
1165   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1166     int fd = dup(ig->source.fdseek.fd);
1167     if (fd < 0) {
1168       i_push_error(errno, "dup() failed");
1169       return 0;
1170     }
1171     return i_readgif(fd, color_table, colors);
1172   }
1173   else {
1174 #if IM_GIFMAJOR >= 4
1175     GifFileType *GifFile;
1176
1177     i_clear_error();
1178
1179     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1180       gif_push_error();
1181       i_push_error(0, "Cannot create giflib callback object");
1182       mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1183       return NULL;
1184     }
1185     
1186     return i_readgif_low(GifFile, color_table, colors);
1187   
1188 #else
1189   i_clear_error();
1190   i_push_error(0, "callbacks not supported with giflib3");
1191
1192   return NULL;
1193 #endif
1194   }
1195 }
1196
1197 /*
1198 =item i_readgif_single_low(GifFile, page)
1199
1200 Lower level function to read a single image from a GIF.
1201
1202 page must be non-negative.
1203
1204 =cut
1205 */
1206 static i_img *
1207 i_readgif_single_low(GifFileType *GifFile, int page) {
1208   int count = 0;
1209   i_img **imgs;
1210
1211   imgs = i_readgif_multi_low(GifFile, &count, page);
1212
1213   if (imgs && count) {
1214     i_img *result = imgs[0];
1215
1216     myfree(imgs);
1217     return result;
1218   }
1219   else {
1220     /* i_readgif_multi_low() handles the errors appropriately */
1221     return NULL;
1222   }
1223 }
1224
1225 /*
1226 =item i_readgif_single_wiol(ig, page)
1227
1228 Read a single page from a GIF image file, where the page is indexed
1229 from 0.
1230
1231 Returns NULL if the page isn't found.
1232
1233 =cut
1234 */
1235
1236 i_img *
1237 i_readgif_single_wiol(io_glue *ig, int page) {
1238   io_glue_commit_types(ig);
1239
1240   i_clear_error();
1241
1242   if (page < 0) {
1243     i_push_error(0, "page must be non-negative");
1244     return NULL;
1245   }
1246
1247   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1248     GifFileType *GifFile;
1249     int fd = dup(ig->source.fdseek.fd);
1250     if (fd < 0) {
1251       i_push_error(errno, "dup() failed");
1252       return NULL;
1253     }
1254     if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1255       gif_push_error();
1256       i_push_error(0, "Cannot create giflib file object");
1257       mm_log((1,"i_readgif: Unable to open file\n"));
1258       return NULL;
1259     }
1260     return i_readgif_single_low(GifFile, page);
1261   }
1262   else {
1263 #if IM_GIFMAJOR >= 4
1264     GifFileType *GifFile;
1265
1266     if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1267       gif_push_error();
1268       i_push_error(0, "Cannot create giflib callback object");
1269       mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1270       return NULL;
1271     }
1272     
1273     return i_readgif_single_low(GifFile, page);
1274 #else
1275     i_push_error(0, "callbacks not supported with giflib3");
1276
1277     return NULL;
1278 #endif
1279   }
1280 }
1281
1282 /*
1283 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1284
1285 Internal.  Low level image write function.  Writes in interlace if
1286 that was requested in the GIF options.
1287
1288 Returns non-zero on success.
1289
1290 =cut
1291 */
1292 static undef_int 
1293 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1294   if (interlace) {
1295     int i, j;
1296     for (i = 0; i < 4; ++i) {
1297       for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1298         if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1299           gif_push_error();
1300           i_push_error(0, "Could not save image data:");
1301           mm_log((1, "Error in EGifPutLine\n"));
1302           EGifCloseFile(gf);
1303           return 0;
1304         }
1305       }
1306     }
1307   }
1308   else {
1309     int y;
1310     for (y = 0; y < img->ysize; ++y) {
1311       if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1312         gif_push_error();
1313         i_push_error(0, "Could not save image data:");
1314         mm_log((1, "Error in EGifPutLine\n"));
1315         EGifCloseFile(gf);
1316         return 0;
1317       }
1318       data += img->xsize;
1319     }
1320   }
1321
1322   return 1;
1323 }
1324
1325 /*
1326 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1327
1328 Internal. Writes the GIF graphics control extension, if necessary.
1329
1330 Returns non-zero on success.
1331
1332 =cut
1333 */
1334 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1335 {
1336   unsigned char gce[4] = {0};
1337   int want_gce = 0;
1338   int delay;
1339   int user_input;
1340   int disposal_method;
1341
1342   if (want_trans) {
1343     gce[0] |= 1;
1344     gce[3] = trans_index;
1345     ++want_gce;
1346   }
1347   if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1348     gce[1] = delay % 256;
1349     gce[2] = delay / 256;
1350     ++want_gce;
1351   }
1352   if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input) 
1353       && user_input) {
1354     gce[0] |= 2;
1355     ++want_gce;
1356   }
1357   if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1358     gce[0] |= (disposal_method & 3) << 2;
1359     ++want_gce;
1360   }
1361   if (want_gce) {
1362     if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1363       gif_push_error();
1364       i_push_error(0, "Could not save GCE");
1365     }
1366   }
1367   return 1;
1368 }
1369
1370 /*
1371 =item do_comments(gf, img)
1372
1373 Write any comments in the image.
1374
1375 =cut
1376 */
1377 static int do_comments(GifFileType *gf, i_img *img) {
1378   int pos = -1;
1379
1380   while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1381     if (img->tags.tags[pos].data) {
1382       if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1383         return 0;
1384       }
1385     }
1386     else {
1387       char buf[50];
1388       sprintf(buf, "%d", img->tags.tags[pos].idata);
1389       if (EGifPutComment(gf, buf) == GIF_ERROR) {
1390         return 0;
1391       }
1392     }
1393   }
1394
1395   return 1;
1396 }
1397
1398 /*
1399 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1400
1401 Internal.  Add the Netscape2.0 loop extension block, if requested.
1402
1403 Giflib/libungif prior to 4.1.1 didn't support writing application
1404 extension blocks, so we don't attempt to write them for older versions.
1405
1406 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1407 writing extension blocks so that they could only be written to files.
1408
1409 =cut
1410 */
1411 static int do_ns_loop(GifFileType *gf, i_img *img)
1412 {
1413   /* EGifPutExtension() doesn't appear to handle application 
1414      extension blocks in any way
1415      Since giflib wraps the fd with a FILE * (and puts that in its
1416      private data), we can't do an end-run and write the data 
1417      directly to the fd.
1418      There's no open interface that takes a FILE * either, so we 
1419      can't workaround it that way either.
1420      If giflib's callback interface wasn't broken by default, I'd 
1421      force file writes to use callbacks, but it is broken by default.
1422   */
1423   /* yes this was another attempt at supporting the loop extension */
1424 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1425   int loop_count;
1426   if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1427     unsigned char nsle[12] = "NETSCAPE2.0";
1428     unsigned char subblock[3];
1429     if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1430       gif_push_error();
1431       i_push_error(0, "writing loop extension");
1432       return 0;
1433     }
1434     subblock[0] = 1;
1435     subblock[1] = loop_count % 256;
1436     subblock[2] = loop_count / 256;
1437     if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1438       gif_push_error();
1439       i_push_error(0, "writing loop extention sub-block");
1440       return 0;
1441     }
1442   }
1443 #endif
1444
1445   return 1;
1446 }
1447
1448 /*
1449 =item make_gif_map(i_quantize *quant, int want_trans)
1450
1451 Create a giflib color map object from an Imager color map.
1452
1453 =cut
1454 */
1455
1456 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img, 
1457                                     int want_trans) {
1458   GifColorType colors[256];
1459   int i;
1460   int size = quant->mc_count;
1461   int map_size;
1462   ColorMapObject *map;
1463   i_color trans;
1464
1465   for (i = 0; i < quant->mc_count; ++i) {
1466     colors[i].Red = quant->mc_colors[i].rgb.r;
1467     colors[i].Green = quant->mc_colors[i].rgb.g;
1468     colors[i].Blue = quant->mc_colors[i].rgb.b;
1469   }
1470   if (want_trans) {
1471     if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1472       trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1473     colors[size].Red = trans.rgb.r;
1474     colors[size].Green = trans.rgb.g;
1475     colors[size].Blue = trans.rgb.b;
1476     ++size;
1477   }
1478   map_size = 1;
1479   while (map_size < size)
1480     map_size <<= 1;
1481   /* giflib spews for 1 colour maps, reasonable, I suppose */
1482   if (map_size == 1)
1483     map_size = 2;
1484   while (i < map_size) {
1485     colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1486     ++i;
1487   }
1488   
1489   map = MakeMapObject(map_size, colors);
1490   mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1491   if (!map) {
1492     gif_push_error();
1493     i_push_error(0, "Could not create color map object");
1494     return NULL;
1495   }
1496   return map;
1497 }
1498
1499 /*
1500 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1501
1502 We need to call EGifSetGifVersion() before opening the file - put that
1503 common code here.
1504
1505 Unfortunately giflib 4.1.0 crashes when we use this.  Internally
1506 giflib 4.1.0 has code:
1507
1508   static char *GifVersionPrefix = GIF87_STAMP;
1509
1510 and the code that sets the version internally does:
1511
1512   strncpy(&GifVersionPrefix[3], Version, 3);
1513
1514 which is very broken.
1515
1516 Failing to set the correct GIF version doesn't seem to cause a problem
1517 with readers.
1518
1519 Modern versions (4.1.4 anyway) of giflib/libungif handle
1520 EGifSetGifVersion correctly.
1521
1522 If t/t105gif.t crashes here then run Makefile.PL with
1523 --nogifsetversion, eg.:
1524
1525   perl Makefile.PL --nogifsetversion
1526
1527 or install a less buggy giflib.
1528
1529 =cut
1530 */
1531
1532 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1533 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1534         && !defined(IM_NO_SET_GIF_VERSION)
1535   int need_89a = 0;
1536   int temp;
1537   int i;
1538
1539   if (quant->transp != tr_none)
1540     need_89a = 1;
1541   else {
1542     for (i = 0; i < count; ++i) {
1543       if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) { 
1544         need_89a = 1; 
1545         break;
1546       }
1547       if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1548         need_89a = 1; 
1549         break;
1550       }
1551       if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1552         need_89a = 1;
1553         break;
1554       }
1555       if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1556         need_89a = 1;
1557         break;
1558       }
1559     }
1560   }
1561   if (need_89a)
1562      EGifSetGifVersion("89a");
1563   else
1564      EGifSetGifVersion("87a");
1565 #endif
1566 }
1567
1568 static int 
1569 in_palette(i_color *c, i_quantize *quant, int size) {
1570   int i;
1571
1572   for (i = 0; i < size; ++i) {
1573     if (c->channel[0] == quant->mc_colors[i].channel[0]
1574         && c->channel[1] == quant->mc_colors[i].channel[1]
1575         && c->channel[2] == quant->mc_colors[i].channel[2]) {
1576       return i;
1577     }
1578   }
1579
1580   return -1;
1581 }
1582
1583 /*
1584 =item has_common_palette(imgs, count, quant, want_trans)
1585
1586 Tests if all the given images are paletted and have a common palette,
1587 if they do it builds that palette.
1588
1589 A possible improvement might be to eliminate unused colors in the
1590 images palettes.
1591
1592 =cut
1593 */
1594 static int
1595 has_common_palette(i_img **imgs, int count, i_quantize *quant, 
1596                    int want_trans) {
1597   int size = quant->mc_count;
1598   int i;
1599   int imgn;
1600   char used[256];
1601
1602   /* we try to build a common palette here, if we can manage that, then
1603      that's the palette we use */
1604   for (imgn = 0; imgn < count; ++imgn) {
1605     int eliminate_unused;
1606     if (imgs[imgn]->type != i_palette_type)
1607       return 0;
1608
1609     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0, 
1610                         &eliminate_unused)) {
1611       eliminate_unused = 1;
1612     }
1613
1614     if (eliminate_unused) {
1615       i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1616       int x, y;
1617       memset(used, 0, sizeof(used));
1618
1619       for (y = 0; y < imgs[imgn]->ysize; ++y) {
1620         i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1621         for (x = 0; x < imgs[imgn]->xsize; ++x)
1622           used[line[x]] = 1;
1623       }
1624
1625       myfree(line);
1626     }
1627     else {
1628       /* assume all are in use */
1629       memset(used, 1, sizeof(used));
1630     }
1631
1632     for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1633       i_color c;
1634       
1635       i_getcolors(imgs[imgn], i, &c, 1);
1636       if (used[i]) {
1637         if (in_palette(&c, quant, size) < 0) {
1638           if (size < quant->mc_size) {
1639             quant->mc_colors[size++] = c;
1640           }
1641           else {
1642             /* oops, too many colors */
1643             return 0;
1644           }
1645         }
1646       }
1647     }
1648   }
1649
1650   quant->mc_count = size;
1651
1652   return 1;
1653 }
1654
1655 static i_palidx *
1656 quant_paletted(i_quantize *quant, i_img *img) {
1657   i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1658   i_palidx *p = data;
1659   i_palidx trans[256];
1660   int i;
1661   int x, y;
1662
1663   /* build a translation table */
1664   for (i = 0; i < i_colorcount(img); ++i) {
1665     i_color c;
1666     i_getcolors(img, i, &c, 1);
1667     trans[i] = in_palette(&c, quant, quant->mc_count);
1668   }
1669
1670   for (y = 0; y < img->ysize; ++y) {
1671     i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1672     for (x = 0; x < img->xsize; ++x) {
1673       *p = trans[*p];
1674       ++p;
1675     }
1676   }
1677
1678   return data;
1679 }
1680
1681 /*
1682 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1683
1684 Internal.  Low-level function that does the high-level GIF processing
1685 :)
1686
1687 Returns non-zero on success.
1688
1689 =cut
1690 */
1691
1692 static undef_int
1693 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1694   unsigned char *result = NULL;
1695   int color_bits;
1696   ColorMapObject *map;
1697   int scrw = 0, scrh = 0;
1698   int imgn, orig_count, orig_size;
1699   int posx, posy;
1700   int trans_index = -1;
1701   i_mempool mp;
1702   int *localmaps;
1703   int anylocal;
1704   i_img **glob_imgs; /* images that will use the global color map */
1705   int glob_img_count;
1706   i_color *orig_colors = quant->mc_colors;
1707   i_color *glob_colors = NULL;
1708   int glob_color_count = 0;
1709   int glob_want_trans;
1710   int glob_paletted = 0; /* the global map was made from the image palettes */
1711   int colors_paletted = 0;
1712   int want_trans = 0;
1713   int interlace;
1714   int gif_background;
1715
1716   mm_log((1, "i_writegif_low(quant %p, gf  %p, imgs %p, count %d)\n", 
1717           quant, gf, imgs, count));
1718   
1719   /* *((char *)0) = 1; */ /* used to break into the debugger */
1720   
1721   if (count <= 0) {
1722     i_push_error(0, "No images provided to write");
1723     return 0; /* what are you smoking? */
1724   }
1725
1726   i_mempool_init(&mp);
1727
1728   /* sanity is nice */
1729   if (quant->mc_size > 256) 
1730     quant->mc_size = 256;
1731   if (quant->mc_count > quant->mc_size)
1732     quant->mc_count = quant->mc_size;
1733
1734   if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1735     scrw = 0;
1736   if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
1737     scrw = 0;
1738
1739   anylocal = 0;
1740   localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1741   glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1742   glob_img_count = 0;
1743   glob_want_trans = 0;
1744   for (imgn = 0; imgn < count; ++imgn) {
1745     posx = posy = 0;
1746     i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1747     i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1748     if (imgs[imgn]->xsize + posx > scrw)
1749       scrw = imgs[imgn]->xsize + posx;
1750     if (imgs[imgn]->ysize + posy > scrh)
1751       scrh = imgs[imgn]->ysize + posy;
1752     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1753       localmaps[imgn] = 0;
1754     if (localmaps[imgn])
1755       anylocal = 1;
1756     else {
1757       if (imgs[imgn]->channels == 4) {
1758         glob_want_trans = 1;
1759       }
1760       glob_imgs[glob_img_count++] = imgs[imgn];
1761     }
1762   }
1763   glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1764
1765   orig_count = quant->mc_count;
1766   orig_size = quant->mc_size;
1767
1768   if (glob_img_count) {
1769     /* this is ugly */
1770     glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1771     quant->mc_colors = glob_colors;
1772     memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1773     /* we have some images that want to use the global map */
1774     if (glob_want_trans && quant->mc_count == 256) {
1775       mm_log((2, "  disabling transparency for global map - no space\n"));
1776       glob_want_trans = 0;
1777     }
1778     if (glob_want_trans && quant->mc_size == 256) {
1779       mm_log((2, "  reserving color for transparency\n"));
1780       --quant->mc_size;
1781     }
1782     if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1783       glob_paletted = 1;
1784     }
1785     else {
1786       glob_paletted = 0;
1787       i_quant_makemap(quant, glob_imgs, glob_img_count);
1788     }
1789     glob_color_count = quant->mc_count;
1790     quant->mc_colors = orig_colors;
1791   }
1792
1793   /* use the global map if we have one, otherwise use the local map */
1794   gif_background = 0;
1795   if (glob_colors) {
1796     quant->mc_colors = glob_colors;
1797     quant->mc_count = glob_color_count;
1798     want_trans = glob_want_trans && imgs[0]->channels == 4;
1799
1800     if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1801       gif_background = 0;
1802     if (gif_background < 0)
1803       gif_background = 0;
1804     if (gif_background >= glob_color_count)
1805       gif_background = 0;
1806   }
1807   else {
1808     want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1809     if (has_common_palette(imgs, 1, quant, want_trans)) {
1810       colors_paletted = 1;
1811     }
1812     else {
1813       colors_paletted = 0;
1814       i_quant_makemap(quant, imgs, 1);
1815     }
1816   }
1817   if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1818     i_mempool_destroy(&mp);
1819     quant->mc_colors = orig_colors;
1820     EGifCloseFile(gf);
1821     mm_log((1, "Error in MakeMapObject"));
1822     return 0;
1823   }
1824   color_bits = 1;
1825   if (anylocal) {
1826     /* since we don't know how big some the local palettes could be
1827        we need to base the bits on the maximum number of colors */
1828     while (orig_size > (1 << color_bits))
1829       ++color_bits;
1830   }
1831   else {
1832     int count = quant->mc_count;
1833     if (want_trans)
1834       ++count;
1835     while (count > (1 << color_bits))
1836       ++color_bits;
1837   }
1838   
1839   if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 
1840                         gif_background, map) == GIF_ERROR) {
1841     i_mempool_destroy(&mp);
1842     quant->mc_colors = orig_colors;
1843     gif_push_error();
1844     i_push_error(0, "Could not save screen descriptor");
1845     FreeMapObject(map);
1846     myfree(result);
1847     EGifCloseFile(gf);
1848     mm_log((1, "Error in EGifPutScreenDesc."));
1849     return 0;
1850   }
1851   FreeMapObject(map);
1852
1853   if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1854     posx = 0;
1855   if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1856     posy = 0;
1857
1858   if (!localmaps[0]) {
1859     map = NULL;
1860     colors_paletted = glob_paletted;
1861   }
1862   else {
1863     /* if this image has a global map the colors in quant don't
1864        belong to this image, so build a palette */
1865     if (glob_colors) {
1866       /* generate the local map for this image */
1867       quant->mc_colors = orig_colors;
1868       quant->mc_size = orig_size;
1869       quant->mc_count = orig_count;
1870       want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1871
1872       /* if the caller gives us too many colours we can't do transparency */
1873       if (want_trans && quant->mc_count == 256)
1874         want_trans = 0;
1875       /* if they want transparency but give us a big size, make it smaller
1876          to give room for a transparency colour */
1877       if (want_trans && quant->mc_size == 256)
1878         --quant->mc_size;
1879       if (has_common_palette(imgs, 1, quant, want_trans)) {
1880         colors_paletted = 1;
1881       }
1882       else {
1883         colors_paletted = 0;
1884         i_quant_makemap(quant, imgs, 1);
1885       }
1886       if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1887         i_mempool_destroy(&mp);
1888         EGifCloseFile(gf);
1889         quant->mc_colors = orig_colors;
1890         mm_log((1, "Error in MakeMapObject"));
1891         return 0;
1892       }
1893     }
1894     else {
1895       /* the map we wrote was the map for this image - don't set the local 
1896          map */
1897       map = NULL;
1898     }
1899   }
1900
1901   if (colors_paletted)
1902     result = quant_paletted(quant, imgs[0]);
1903   else
1904     result = i_quant_translate(quant, imgs[0]);
1905   if (!result) {
1906     i_mempool_destroy(&mp);
1907     quant->mc_colors = orig_colors;
1908     EGifCloseFile(gf);
1909     return 0;
1910   }
1911   if (want_trans) {
1912     i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1913     trans_index = quant->mc_count;
1914   }
1915
1916   if (!do_ns_loop(gf, imgs[0])) {
1917     i_mempool_destroy(&mp);
1918     quant->mc_colors = orig_colors;
1919     return 0;
1920   }
1921
1922   if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1923     i_mempool_destroy(&mp);
1924     quant->mc_colors = orig_colors;
1925     myfree(result);
1926     EGifCloseFile(gf);
1927     return 0;
1928   }
1929
1930   if (!do_comments(gf, imgs[0])) {
1931     i_mempool_destroy(&mp);
1932     quant->mc_colors = orig_colors;
1933     myfree(result);
1934     EGifCloseFile(gf);
1935     return 0;
1936   }
1937
1938   if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1939     interlace = 0;
1940   if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize, 
1941                        interlace, map) == GIF_ERROR) {
1942     i_mempool_destroy(&mp);
1943     quant->mc_colors = orig_colors;
1944     gif_push_error();
1945     i_push_error(0, "Could not save image descriptor");
1946     EGifCloseFile(gf);
1947     mm_log((1, "Error in EGifPutImageDesc."));
1948     return 0;
1949   }
1950   if (map)
1951     FreeMapObject(map);
1952
1953   if (!do_write(gf, interlace, imgs[0], result)) {
1954     i_mempool_destroy(&mp);
1955     quant->mc_colors = orig_colors;
1956     EGifCloseFile(gf);
1957     myfree(result);
1958     return 0;
1959   }
1960   myfree(result);
1961
1962   /* that first awful image is out of the way, do the rest */
1963   for (imgn = 1; imgn < count; ++imgn) {
1964     if (localmaps[imgn]) {
1965       quant->mc_colors = orig_colors;
1966       quant->mc_count = orig_count;
1967       quant->mc_size = orig_size;
1968
1969       want_trans = quant->transp != tr_none 
1970         && imgs[imgn]->channels == 4;
1971       /* if the caller gives us too many colours we can't do transparency */
1972       if (want_trans && quant->mc_count == 256)
1973         want_trans = 0;
1974       /* if they want transparency but give us a big size, make it smaller
1975          to give room for a transparency colour */
1976       if (want_trans && quant->mc_size == 256)
1977         --quant->mc_size;
1978
1979       if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1980         result = quant_paletted(quant, imgs[imgn]);
1981       }
1982       else {
1983         i_quant_makemap(quant, imgs+imgn, 1);
1984         result = i_quant_translate(quant, imgs[imgn]);
1985       }
1986       if (!result) {
1987         i_mempool_destroy(&mp);
1988         quant->mc_colors = orig_colors;
1989         EGifCloseFile(gf);
1990         mm_log((1, "error in i_quant_translate()"));
1991         return 0;
1992       }
1993       if (want_trans) {
1994         i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1995         trans_index = quant->mc_count;
1996       }
1997
1998       if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1999         i_mempool_destroy(&mp);
2000         quant->mc_colors = orig_colors;
2001         myfree(result);
2002         EGifCloseFile(gf);
2003         mm_log((1, "Error in MakeMapObject."));
2004         return 0;
2005       }
2006     }
2007     else {
2008       quant->mc_colors = glob_colors;
2009       quant->mc_count = glob_color_count;
2010       if (glob_paletted)
2011         result = quant_paletted(quant, imgs[imgn]);
2012       else
2013         result = i_quant_translate(quant, imgs[imgn]);
2014       want_trans = glob_want_trans && imgs[imgn]->channels == 4;
2015       if (want_trans) {
2016         i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2017         trans_index = quant->mc_count;
2018       }
2019       map = NULL;
2020     }
2021
2022     if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
2023       i_mempool_destroy(&mp);
2024       quant->mc_colors = orig_colors;
2025       myfree(result);
2026       EGifCloseFile(gf);
2027       return 0;
2028     }
2029
2030     if (!do_comments(gf, imgs[imgn])) {
2031       i_mempool_destroy(&mp);
2032       quant->mc_colors = orig_colors;
2033       myfree(result);
2034       EGifCloseFile(gf);
2035       return 0;
2036     }
2037
2038     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2039       posx = 0;
2040     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2041       posy = 0;
2042
2043     if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2044       interlace = 0;
2045     if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize, 
2046                          imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2047       i_mempool_destroy(&mp);
2048       quant->mc_colors = orig_colors;
2049       gif_push_error();
2050       i_push_error(0, "Could not save image descriptor");
2051       myfree(result);
2052       if (map)
2053         FreeMapObject(map);
2054       EGifCloseFile(gf);
2055       mm_log((1, "Error in EGifPutImageDesc."));
2056       return 0;
2057     }
2058     if (map)
2059       FreeMapObject(map);
2060     
2061     if (!do_write(gf, interlace, imgs[imgn], result)) {
2062       i_mempool_destroy(&mp);
2063       quant->mc_colors = orig_colors;
2064       EGifCloseFile(gf);
2065       myfree(result);
2066       return 0;
2067     }
2068     myfree(result);
2069   }
2070
2071   if (EGifCloseFile(gf) == GIF_ERROR) {
2072     i_mempool_destroy(&mp);
2073     gif_push_error();
2074     i_push_error(0, "Could not close GIF file");
2075     mm_log((1, "Error in EGifCloseFile\n"));
2076     return 0;
2077   }
2078   if (glob_colors) {
2079     int i;
2080     for (i = 0; i < glob_color_count; ++i)
2081       orig_colors[i] = glob_colors[i];
2082   }
2083
2084   i_mempool_destroy(&mp);
2085   quant->mc_colors = orig_colors;
2086
2087   return 1;
2088 }
2089
2090 /*
2091 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2092
2093 General high-level function to write a GIF to a file.
2094
2095 Writes the GIF images to the specified file handle using the options
2096 in quant and opts.  See L<image.h/i_quantize> and
2097 L<image.h/i_gif_opts>.
2098
2099 Returns non-zero on success.
2100
2101 =cut
2102 */
2103
2104 undef_int
2105 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2106   GifFileType *gf;
2107
2108   i_clear_error();
2109   mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n", 
2110           quant, fd, imgs, count));
2111
2112   gif_set_version(quant, imgs, count);
2113
2114   if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2115     gif_push_error();
2116     i_push_error(0, "Cannot create GIF file object");
2117     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2118     return 0;
2119   }
2120
2121   return i_writegif_low(quant, gf, imgs, count);
2122 }
2123
2124 #if IM_GIFMAJOR >= 4
2125
2126 /*
2127 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2128
2129 Internal.  Wrapper for the user write callback function.
2130
2131 =cut
2132 */
2133
2134 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2135 {
2136   i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2137
2138   return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2139 }
2140
2141 #endif
2142
2143 /*
2144 =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)
2145
2146 General high-level function to write a GIF using callbacks to send
2147 back the data.
2148
2149 Returns non-zero on success.
2150
2151 =cut
2152 */
2153
2154 undef_int
2155 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2156                     int maxlength, i_img **imgs, int count)
2157 {
2158 #if IM_GIFMAJOR >= 4
2159   GifFileType *gf;
2160   i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2161   int result;
2162
2163   i_clear_error();
2164
2165   mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n", 
2166           quant, cb, userdata, maxlength, imgs, count));
2167   
2168   if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2169     gif_push_error();
2170     i_push_error(0, "Cannot create GIF file object");
2171     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2172     i_free_gen_write_data(gwd, 0);
2173     return 0;
2174   }
2175
2176   result = i_writegif_low(quant, gf, imgs, count);
2177   return i_free_gen_write_data(gwd, result);
2178 #else
2179   i_clear_error();
2180   i_push_error(0, "callbacks not supported with giflib3");
2181
2182   return 0;
2183 #endif
2184 }
2185
2186 #if IM_GIFMAJOR >= 4
2187
2188 static int
2189 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2190   io_glue *ig = (io_glue *)gft->UserData;
2191
2192   return ig->writecb(ig, data, length);
2193 }
2194
2195 #endif
2196
2197 /*
2198 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2199
2200 =cut
2201 */
2202 undef_int
2203 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2204                 int count) {
2205   io_glue_commit_types(ig);
2206
2207   if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2208     int fd = dup(ig->source.fdseek.fd);
2209     if (fd < 0) {
2210       i_push_error(errno, "dup() failed");
2211       return 0;
2212     }
2213     /* giflib opens the fd with fdopen(), which is then closed when fclose()
2214        is called - dup it so the caller's fd isn't closed */
2215     return i_writegif_gen(quant, fd, imgs, count);
2216   }
2217   else {
2218 #if IM_GIFMAJOR >= 4
2219     GifFileType *GifFile;
2220     int result;
2221
2222     i_clear_error();
2223
2224     gif_set_version(quant, imgs, count);
2225
2226     if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2227       gif_push_error();
2228       i_push_error(0, "Cannot create giflib callback object");
2229       mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2230       return 0;
2231     }
2232     
2233     result = i_writegif_low(quant, GifFile, imgs, count);
2234     
2235     ig->closecb(ig);
2236
2237     return result;
2238 #else
2239     i_clear_error();
2240     i_push_error(0, "callbacks not supported with giflib3");
2241     
2242     return 0;
2243 #endif
2244   }
2245 }
2246
2247 /*
2248 =item gif_error_msg(int code)
2249
2250 Grabs the most recent giflib error code from GifLastError() and 
2251 returns a string that describes that error.
2252
2253 The returned pointer points to a static buffer, either from a literal
2254 C string or a static buffer.
2255
2256 =cut
2257 */
2258
2259 static char const *gif_error_msg(int code) {
2260   static char msg[80];
2261
2262   switch (code) {
2263   case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2264     return "Failed to open given file";
2265     
2266   case E_GIF_ERR_WRITE_FAILED:
2267     return "Write failed";
2268
2269   case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2270     return "Screen descriptor already passed to giflib";
2271
2272   case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2273     return "Image descriptor already passed to giflib";
2274     
2275   case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2276     return "Neither global nor local color map set";
2277
2278   case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2279     return "Too much pixel data passed to giflib";
2280
2281   case E_GIF_ERR_NOT_ENOUGH_MEM:
2282     return "Out of memory";
2283     
2284   case E_GIF_ERR_DISK_IS_FULL:
2285     return "Disk is full";
2286     
2287   case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2288     return "File close failed";
2289  
2290   case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2291     return "File not writable";
2292
2293   case D_GIF_ERR_OPEN_FAILED:
2294     return "Failed to open file";
2295     
2296   case D_GIF_ERR_READ_FAILED:
2297     return "Failed to read from file";
2298
2299   case D_GIF_ERR_NOT_GIF_FILE:
2300     return "File is not a GIF file";
2301
2302   case D_GIF_ERR_NO_SCRN_DSCR:
2303     return "No screen descriptor detected - invalid file";
2304
2305   case D_GIF_ERR_NO_IMAG_DSCR:
2306     return "No image descriptor detected - invalid file";
2307
2308   case D_GIF_ERR_NO_COLOR_MAP:
2309     return "No global or local color map found";
2310
2311   case D_GIF_ERR_WRONG_RECORD:
2312     return "Wrong record type detected - invalid file?";
2313
2314   case D_GIF_ERR_DATA_TOO_BIG:
2315     return "Data in file too big for image";
2316
2317   case D_GIF_ERR_NOT_ENOUGH_MEM:
2318     return "Out of memory";
2319
2320   case D_GIF_ERR_CLOSE_FAILED:
2321     return "Close failed";
2322
2323   case D_GIF_ERR_NOT_READABLE:
2324     return "File not opened for read";
2325
2326   case D_GIF_ERR_IMAGE_DEFECT:
2327     return "Defective image";
2328
2329   case D_GIF_ERR_EOF_TOO_SOON:
2330     return "Unexpected EOF - invalid file";
2331
2332   default:
2333     sprintf(msg, "Unknown giflib error code %d", code);
2334     return msg;
2335   }
2336 }
2337
2338 /*
2339 =item gif_push_error()
2340
2341 Utility function that takes the current GIF error code, converts it to
2342 an error message and pushes it on the error stack.
2343
2344 =cut
2345 */
2346
2347 static void gif_push_error(void) {
2348   int code = GifLastError(); /* clears saved error */
2349
2350   i_push_error(code, gif_error_msg(code));
2351 }
2352
2353 /*
2354 =head1 BUGS
2355
2356 The Netscape loop extension isn't implemented.  Giflib's extension
2357 writing code doesn't seem to support writing named extensions in this 
2358 form.
2359
2360 A bug in giflib is tickled by the i_writegif_callback().  This isn't a
2361 problem on ungiflib, but causes a SEGV on giflib.  A patch is provided
2362 in t/t10formats.t
2363
2364 The GIF file tag (GIF87a vs GIF89a) currently isn't set.  Using the
2365 supplied interface in giflib 4.1.0 causes a SEGV in
2366 EGifSetGifVersion().  See L<gif_set_version> for an explanation.
2367
2368 =head1 AUTHOR
2369
2370 Arnar M. Hrafnkelsson, addi@umich.edu
2371
2372 =head1 SEE ALSO
2373
2374 perl(1), Imager(3)
2375
2376 =cut
2377
2378 */