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