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