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