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