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