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