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