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