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