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