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