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