Added checks if there is no global colormap and a local colormap to avoid
[imager.git] / gif.c
1 #include "image.h"
2 #include <gif_lib.h>
3
4 /*
5 =head1 NAME
6
7 gif.c - read and write gif files for Imager
8
9 =head1 SYNOPSIS
10
11   i_img *img;
12   i_img *imgs[count];
13   int fd;
14   int *colour_table,
15   int colours;
16   int max_colours; // number of bits per colour
17   int pixdev;  // how much noise to add 
18   i_color fixed[N]; // fixed palette entries 
19   int fixedlen; // number of fixed colours 
20   int success; // non-zero on success
21   char *data; // a GIF file in memory
22   int length; // how big data is 
23   int reader(char *, char *, int, int);
24   int writer(char *, char *, int);
25   char *userdata; // user's data, whatever it is
26   i_quantize quant;
27   i_gif_opts opts;
28
29   img = i_readgif(fd, &colour_table, &colours);
30   success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
31   success = i_writegifmc(img, fd, max_colours);
32   img = i_readgif_scalar(data, length, &colour_table, &colours);
33   img = i_readgif_callback(cb, userdata, &colour_table, &colours);
34   success = i_writegif_gen(&quant, fd, imgs, count, &opts);
35   success = i_writegif_callback(&quant, writer, userdata, maxlength, 
36                                 imgs, count, &opts);
37
38 =head1 DESCRIPTION
39
40 This source file provides the C level interface to reading and writing
41 GIF files for Imager.
42
43 This has been tested with giflib 3 and 4, though you lose the callback
44 functionality with giflib3.
45
46 =head1 REFERENCE
47
48 =over
49
50 =cut
51 */
52
53 static char const *gif_error_msg(int code);
54 static void gif_push_error();
55
56 #if IM_GIFMAJOR >= 4
57
58 /*
59 =item gif_scalar_info
60
61 Internal.  A structure passed to the reader function used for reading
62 GIFs from scalars.
63
64 Used with giflib 4 and later.
65
66 =cut
67 */
68
69 struct gif_scalar_info {
70   char *data;
71   int length;
72   int cpos;
73 };
74
75 /*
76 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
77
78 Internal.  The reader callback passed to giflib.
79
80 Used with giflib 4 and later.
81
82 =cut
83 */
84
85 int
86 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
87   struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
88   /*   fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
89
90   if (gsi->cpos == gsi->length) return 0;
91   if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
92   memcpy(buf,gsi->data+gsi->cpos,length);
93   gsi->cpos+=length;
94   return length;
95 }
96
97 #endif
98
99 /*
100   This file needs a complete rewrite 
101
102   This file needs a complete rewrite 
103
104   Maybe not anymore, though reading still needs to support reading
105   all those gif properties.
106 */
107
108 /* Make some variables global, so we could access them faster: */
109
110 static int
111     ImageNum = 0,
112     BackGround = 0,
113     ColorMapSize = 0,
114     InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
115     InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
116
117 static ColorMapObject *ColorMap;
118
119
120 static
121 void
122 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colormap) {
123   GifColorType *mapentry;
124   int q;
125   int colormapsize = colormap->ColorCount;
126
127   if(colours) *colours = colormapsize;
128   if(!colour_table) return;
129   
130   *colour_table = mymalloc(sizeof(int *) * colormapsize * 3);
131   memset(*colour_table, 0, sizeof(int *) * colormapsize * 3);
132
133   for(q=0; q<ColorMapSize; q++) {
134     mapentry = &colormap->Colors[q];
135     (*colour_table)[q*3 + 0] = mapentry->Red;
136     (*colour_table)[q*3 + 1] = mapentry->Green;
137     (*colour_table)[q*3 + 2] = mapentry->Blue;
138   }
139 }
140
141
142 /*
143 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
144
145 Internal.  Low-level function for reading a GIF file.  The caller must
146 create the appropriate GifFileType object and pass it in.
147
148 =cut
149 */
150
151 i_img *
152 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
153   i_img *im;
154   int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
155   int cmapcnt = 0;
156   
157   GifRecordType RecordType;
158   GifByteType *Extension;
159   
160   GifRowType GifRow;
161   static GifColorType *ColorMapEntry;
162   i_color col;
163
164   mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
165
166   BackGround = GifFile->SBackGroundColor;
167   ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
168
169   if (ColorMap) {
170     ColorMapSize = ColorMap->ColorCount;
171     i_colortable_copy(colour_table, colours, ColorMap);
172     cmapcnt++;
173   }
174   
175
176   im = i_img_empty_ch(NULL,GifFile->SWidth,GifFile->SHeight,3);
177
178   Size = GifFile->SWidth * sizeof(GifPixelType); 
179   
180   if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
181     m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
182
183   for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
184   
185   /* Scan the content of the GIF file and load the image(s) in: */
186   do {
187     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
188       gif_push_error();
189       i_push_error(0, "Unable to get record type");
190       if (colour_table) free(colour_table); /* FIXME: Isn't this an error? */
191       i_img_destroy(im);
192       DGifCloseFile(GifFile);
193       return NULL;
194     }
195     
196     switch (RecordType) {
197     case IMAGE_DESC_RECORD_TYPE:
198       if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
199         gif_push_error();
200         i_push_error(0, "Unable to get image descriptor");
201         if (colour_table) free(colour_table); /* FIXME: Isn't this an error? */
202         i_img_destroy(im);
203         DGifCloseFile(GifFile);
204         return NULL;
205       }
206
207       if ( cmapcnt == 0) {
208         if (ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) ) {
209           mm_log((1, "Adding local colormap\n"));
210           ColorMapSize = ColorMap->ColorCount;
211           i_colortable_copy(colour_table, colours, ColorMap);
212           cmapcnt++;
213         } else {
214           /* No colormap and we are about to read in the image - abandon for now */
215           mm_log((1, "Going in with no colormap\n"));
216           i_push_error(0, "Image does not have a local or a global color map");
217           if (colour_table) free(colour_table); /* FIXME: Isn't this an error? */
218           i_img_destroy(im);
219           DGifCloseFile(GifFile);
220           return NULL;
221         }
222       }
223       Row = GifFile->Image.Top; /* Image Position relative to Screen. */
224       Col = GifFile->Image.Left;
225       Width = GifFile->Image.Width;
226       Height = GifFile->Image.Height;
227       ImageNum++;
228       mm_log((1,"i_readgif: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
229
230       if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
231           GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
232         i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
233         if (colour_table) free(colour_table); /* FIXME: Yet again */
234         i_img_destroy(im);
235         DGifCloseFile(GifFile);
236         return(0);
237       }
238       if (GifFile->Image.Interlace) {
239
240         for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
241           Count++;
242           if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
243             gif_push_error();
244             i_push_error(0, "Reading GIF line");
245             if (colour_table)
246               free(colour_table);
247             i_img_destroy(im);
248             DGifCloseFile(GifFile);
249             return NULL;
250           }
251           
252           for (x = 0; x < GifFile->SWidth; x++) {
253             ColorMapEntry = &ColorMap->Colors[GifRow[x]];
254             col.rgb.r = ColorMapEntry->Red;
255             col.rgb.g = ColorMapEntry->Green;
256             col.rgb.b = ColorMapEntry->Blue;
257             i_ppix(im,x,j,&col);
258           }
259           
260         }
261       }
262       else {
263         for (i = 0; i < Height; i++) {
264           if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
265             gif_push_error();
266             i_push_error(0, "Reading GIF line");
267             if (colour_table)
268               free(colour_table);
269             i_img_destroy(im);
270             DGifCloseFile(GifFile);
271             return NULL;
272           }
273
274           for (x = 0; x < GifFile->SWidth; x++) {
275             ColorMapEntry = &ColorMap->Colors[GifRow[x]];
276             col.rgb.r = ColorMapEntry->Red;
277             col.rgb.g = ColorMapEntry->Green;
278             col.rgb.b = ColorMapEntry->Blue;
279             i_ppix(im,x,Row,&col);
280           }
281           Row++;
282         }
283       }
284       break;
285     case EXTENSION_RECORD_TYPE:
286       /* Skip any extension blocks in file: */
287       if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
288         gif_push_error();
289         i_push_error(0, "Reading extension record");
290         if (colour_table)
291           free(colour_table);
292         i_img_destroy(im);
293         DGifCloseFile(GifFile);
294         return NULL;
295       }
296       while (Extension != NULL) {
297         if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
298           gif_push_error();
299           i_push_error(0, "reading next block of extension");
300           if (colour_table)
301             free(colour_table);
302           i_img_destroy(im);
303           DGifCloseFile(GifFile);
304           return NULL;
305         }
306       }
307       break;
308     case TERMINATE_RECORD_TYPE:
309       break;
310     default:                /* Should be traps by DGifGetRecordType. */
311       break;
312     }
313   } while (RecordType != TERMINATE_RECORD_TYPE);
314   
315   myfree(GifRow);
316   
317   if (DGifCloseFile(GifFile) == GIF_ERROR) {
318     gif_push_error();
319     i_push_error(0, "Closing GIF file object");
320     if (colour_table)
321       free(colour_table);
322     i_img_destroy(im);
323     return NULL;
324   }
325   return im;
326 }
327
328 /*
329 =item i_readgif(int fd, int **colour_table, int *colours)
330
331 Reads in a GIF file from a file handle and converts it to an Imager
332 RGB image object.
333
334 Returns the palette for the object in colour_table for colours
335 colours.
336
337 Returns NULL on failure.
338
339 =cut
340 */
341
342 i_img *
343 i_readgif(int fd, int **colour_table, int *colours) {
344   GifFileType *GifFile;
345
346   i_clear_error();
347   
348   mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
349
350   if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
351     gif_push_error();
352     i_push_error(0, "Cannot create giflib file object");
353     mm_log((1,"i_readgif: Unable to open file\n"));
354     return NULL;
355   }
356
357   return i_readgif_low(GifFile, colour_table, colours);
358 }
359
360 /*
361 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
362
363 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
364 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
365 colours taken from I<fixed>.
366
367 Returns non-zero on success.
368
369 =cut
370 */
371
372 undef_int
373 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
374 {
375   i_color colors[256];
376   i_quantize quant;
377   i_gif_opts opts;
378   
379   memset(&quant, 0, sizeof(quant));
380   memset(&opts, 0, sizeof(opts));
381   quant.make_colors = mc_addi;
382   quant.mc_colors = colors;
383   quant.mc_size = 1<<max_colors;
384   quant.mc_count = fixedlen;
385   memcpy(colors, fixed, fixedlen * sizeof(i_color));
386   quant.translate = pt_perturb;
387   quant.perturb = pixdev;
388   return i_writegif_gen(&quant, fd, &im, 1, &opts);
389 }
390
391 /*
392 =item i_writegifmc(i_img *im, int fd, int max_colors)
393
394 Write I<img> to the file handle I<fd>.  The resulting GIF will use a
395 maximum of 1<<I<max_colours> colours.
396
397 Returns non-zero on success.
398
399 =cut
400 */
401
402 undef_int
403 i_writegifmc(i_img *im, int fd, int max_colors) {
404   i_color colors[256];
405   i_quantize quant;
406   i_gif_opts opts;
407   
408   memset(&quant, 0, sizeof(quant));
409   memset(&opts, 0, sizeof(opts));
410   quant.make_colors = mc_none; /* ignored for pt_giflib */
411   quant.mc_colors = colors;
412   quant.mc_size = 1 << max_colors;
413   quant.mc_count = 0;
414   quant.translate = pt_giflib;
415   return i_writegif_gen(&quant, fd, &im, 1, &opts);
416 }
417
418 #if 1
419
420 undef_int
421 i_writegifex(i_img *im, int fd) {
422   return 0;
423 }
424
425 #else
426
427 /* I don't think this works
428    note that RedBuffer is never allocated - TC
429 */
430
431 undef_int
432 i_writegifex(i_img *im,int fd) {
433   int colors, xsize, ysize, channels;
434   int x,y,ColorMapSize;
435   unsigned long Size;
436
437   struct octt *ct;
438
439   GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,*OutputBuffer = NULL;
440   ColorMapObject *OutputColorMap = NULL;
441   GifFileType *GifFile;
442   GifByteType *Ptr;
443   
444   i_color val;
445
446   mm_log((1,"i_writegif(0x%x,fd %d)\n",im,fd));
447   
448   if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }
449
450   xsize=im->xsize;
451   ysize=im->ysize;
452   channels=im->channels;
453
454   colors=0;
455   ct=octt_new();
456
457   colors=0;
458   for(x=0;x<xsize;x++) for(y=0;y<ysize;y++) {
459     i_gpix(im,x,y,&val);
460     colors+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
461     /*  if (colors > maxc) { octt_delete(ct); } 
462         We'll just bite the bullet */
463   }
464
465   ColorMapSize = (colors > 256) ? 256 : colors;
466   
467   Size = ((long) im->xsize) * im->ysize * sizeof(GifByteType);
468   
469   if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
470     m_fatal(0,"Failed to allocate memory for Output colormap.");
471   if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
472     m_fatal(0,"Failed to allocate memory for output buffer.");
473   
474   if (QuantizeBuffer(im->xsize, im->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
475                      OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
476     mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
477     return(0);
478   }
479
480
481   myfree(RedBuffer);
482   if (im->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
483   
484   if ((GifFile = EGifOpenFileHandle(fd)) == NULL) {
485     mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
486     return(0);
487   }
488   
489   if (EGifPutScreenDesc(GifFile,im->xsize, im->ysize, colors, 0,OutputColorMap) == GIF_ERROR ||
490       EGifPutImageDesc(GifFile,0, 0, im->xsize, im->ysize, FALSE, NULL) == GIF_ERROR) {
491     mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
492     if (GifFile != NULL) EGifCloseFile(GifFile);
493     return(0);
494   }
495
496   Ptr = OutputBuffer;
497
498   for (y = 0; y < im->ysize; y++) {
499     if (EGifPutLine(GifFile, Ptr, im->xsize) == GIF_ERROR) {
500       mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
501       if (GifFile != NULL) EGifCloseFile(GifFile);
502       return(0);
503     }
504     
505     Ptr += im->xsize;
506   }
507   
508   if (EGifCloseFile(GifFile) == GIF_ERROR) {
509     mm_log((1,"Error in EGifCloseFile, unable to write image.\n"));
510     return(0);
511   }
512   return(1);
513 }
514
515 #endif
516
517 /*
518 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
519
520 Reads a GIF file from an in memory copy of the file.  This can be used
521 if you get the 'file' from some source other than an actual file (or
522 some other file handle).
523
524 This function is only available with giflib 4 and higher.
525
526 =cut
527 */
528 i_img*
529 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
530 #if IM_GIFMAJOR >= 4
531   GifFileType *GifFile;
532   struct gif_scalar_info gsi;
533
534   i_clear_error();
535
536   gsi.cpos=0;
537   gsi.length=length;
538   gsi.data=data;
539
540   mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
541   if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
542     gif_push_error();
543     i_push_error(0, "Cannot create giflib callback object");
544     mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
545     return NULL;
546   }
547
548   return i_readgif_low(GifFile, colour_table, colours);
549 #else
550   return NULL;
551 #endif
552 }
553
554 #if IM_GIFMAJOR >= 4
555
556 /*
557 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
558
559 Internal.  The reader callback wrapper passed to giflib.
560
561 This function is only used with giflib 4 and higher.
562
563 =cut
564 */
565
566 static int
567 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
568   return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
569 }
570
571 #endif
572
573
574 /*
575 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
576
577 Read a GIF file into an Imager RGB file, the data of the GIF file is
578 retreived by callin the user supplied callback function.
579
580 This function is only used with giflib 4 and higher.
581
582 =cut
583 */
584
585 i_img*
586 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
587 #if IM_GIFMAJOR >= 4
588   GifFileType *GifFile;
589   i_img *result;
590
591   i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
592
593   i_clear_error();
594   
595   mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
596   if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
597     gif_push_error();
598     i_push_error(0, "Cannot create giflib callback object");
599     mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
600     myfree(gci);
601     return NULL;
602   }
603
604   result = i_readgif_low(GifFile, colour_table, colours);
605   free_gen_read_data(gci);
606
607   return result;
608 #else
609   return NULL;
610 #endif
611 }
612
613 /*
614 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
615
616 Internal.  Low level image write function.  Writes in interlace if
617 that was requested in the GIF options.
618
619 Returns non-zero on success.
620
621 =cut
622 */
623 static undef_int 
624 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
625   if (opts->interlace) {
626     int i, j;
627     for (i = 0; i < 4; ++i) {
628       for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
629         if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
630           gif_push_error();
631           i_push_error(0, "Could not save image data:");
632           mm_log((1, "Error in EGifPutLine\n"));
633           EGifCloseFile(gf);
634           return 0;
635         }
636       }
637     }
638   }
639   else {
640     int y;
641     for (y = 0; y < img->ysize; ++y) {
642       if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
643         gif_push_error();
644         i_push_error(0, "Could not save image data:");
645         mm_log((1, "Error in EGifPutLine\n"));
646         EGifCloseFile(gf);
647         return 0;
648       }
649       data += img->xsize;
650     }
651   }
652
653   return 1;
654 }
655
656 /*
657 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
658
659 Internal. Writes the GIF graphics control extension, if necessary.
660
661 Returns non-zero on success.
662
663 =cut
664 */
665 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
666 {
667   unsigned char gce[4] = {0};
668   int want_gce = 0;
669   if (want_trans) {
670     gce[0] |= 1;
671     gce[3] = trans_index;
672     ++want_gce;
673   }
674   if (index < opts->delay_count) {
675     gce[1] = opts->delays[index] % 256;
676     gce[2] = opts->delays[index] / 256;
677     ++want_gce;
678   }
679   if (index < opts->user_input_count) {
680     if (opts->user_input_flags[index])
681       gce[0] |= 2;
682     ++want_gce;
683   }
684   if (index < opts->disposal_count) {
685     gce[0] |= (opts->disposal[index] & 3) << 2;
686     ++want_gce;
687   }
688   if (want_gce) {
689     if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
690       gif_push_error();
691       i_push_error(0, "Could not save GCE");
692     }
693   }
694   return 1;
695 }
696
697 /*
698 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
699
700 Internal.  Add the Netscape2.0 loop extension block, if requested.
701
702 The code for this function is currently "#if 0"ed out since the giflib
703 extension writing code currently doesn't seem to support writing
704 application extension blocks.
705
706 =cut
707 */
708 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
709 {
710   /* EGifPutExtension() doesn't appear to handle application 
711      extension blocks in any way
712      Since giflib wraps the fd with a FILE * (and puts that in its
713      private data), we can't do an end-run and write the data 
714      directly to the fd.
715      There's no open interface that takes a FILE * either, so we 
716      can't workaround it that way either.
717      If giflib's callback interface wasn't broken by default, I'd 
718      force file writes to use callbacks, but it is broken by default.
719   */
720 #if 0
721   /* yes this was another attempt at supporting the loop extension */
722   if (opts->loop_count) {
723     unsigned char nsle[12] = "NETSCAPE2.0";
724     unsigned char subblock[3];
725     if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
726       gif_push_error();
727       i_push_error(0, "writing loop extension");
728       return 0;
729     }
730     subblock[0] = 1;
731     subblock[1] = opts->loop_count % 256;
732     subblock[2] = opts->loop_count / 256;
733     if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
734       gif_push_error();
735       i_push_error(0, "writing loop extention sub-block");
736       return 0;
737     }
738     if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
739       gif_push_error();
740       i_push_error(0, "writing loop extension terminator");
741       return 0;
742     }
743   }
744 #endif
745   return 1;
746 }
747
748 /*
749 =item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
750
751 Create a giflib color map object from an Imager color map.
752
753 =cut
754 */
755
756 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
757                                     int want_trans) {
758   GifColorType colors[256];
759   int i;
760   int size = quant->mc_count;
761   int map_size;
762   ColorMapObject *map;
763
764   for (i = 0; i < quant->mc_count; ++i) {
765     colors[i].Red = quant->mc_colors[i].rgb.r;
766     colors[i].Green = quant->mc_colors[i].rgb.g;
767     colors[i].Blue = quant->mc_colors[i].rgb.b;
768   }
769   if (want_trans) {
770     colors[size].Red = opts->tran_color.rgb.r;
771     colors[size].Green = opts->tran_color.rgb.g;
772     colors[size].Blue = opts->tran_color.rgb.b;
773     ++size;
774   }
775   map_size = 1;
776   while (map_size < size)
777     map_size <<= 1;
778   /* giflib spews for 1 colour maps, reasonable, I suppose */
779   if (map_size == 1)
780     map_size = 2;
781   map = MakeMapObject(map_size, colors);
782   if (!map) {
783     gif_push_error();
784     i_push_error(0, "Could not create color map object");
785     return NULL;
786   }
787   return map;
788 }
789
790 /*
791 =item gif_set_version(i_quantize *quant, i_gif_opts *opts)
792
793 We need to call EGifSetGifVersion() before opening the file - put that
794 common code here.
795
796 Unfortunately giflib 4.1.0 crashes when we use this.  Internally
797 giflib 4.1.0 has code:
798
799   static char *GifVersionPrefix = GIF87_STAMP;
800
801 and the code that sets the version internally does:
802
803   strncpy(&GifVersionPrefix[3], Version, 3);
804
805 which is very broken.
806
807 Failing to set the correct GIF version doesn't seem to cause a problem
808 with readers.
809
810 =cut
811 */
812
813 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
814   /* the following crashed giflib
815      the EGifSetGifVersion() is seriously borked in giflib
816      it's less borked in the ungiflib beta, but we don't have a mechanism
817      to distinguish them
818      if (opts->delay_count
819      || opts->user_input_count
820      || opts->disposal_count
821      || opts->loop_count
822      || quant->transp != tr_none)
823      EGifSetGifVersion("89a");
824      else
825      EGifSetGifVersion("87a");
826   */
827 }
828
829 /*
830 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
831
832 Internal.  Low-level function that does the high-level GIF processing
833 :)
834
835 Returns non-zero on success.
836
837 =cut
838 */
839
840 static undef_int
841 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
842                i_gif_opts *opts) {
843   unsigned char *result;
844   int color_bits;
845   ColorMapObject *map;
846   int scrw = 0, scrh = 0;
847   int imgn, orig_count, orig_size;
848   int posx, posy;
849
850   mm_log((1, "i_writegif_low(quant %p, gf  %p, imgs %p, count %d, opts %p)\n", 
851           quant, gf, imgs, count, opts));
852
853   /**((char *)0) = 1;*/
854   /* sanity is nice */
855   if (quant->mc_size > 256) 
856     quant->mc_size = 256;
857   if (quant->mc_count > quant->mc_size)
858     quant->mc_count = quant->mc_size;
859
860   for (imgn = 0; imgn < count; ++imgn) {
861     if (imgn < opts->position_count) {
862       if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
863         scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
864       if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
865         scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
866     }
867     else {
868       if (imgs[imgn]->xsize > scrw)
869         scrw = imgs[imgn]->xsize;
870       if (imgs[imgn]->ysize > scrh)
871         scrh = imgs[imgn]->ysize;
872     }
873   }
874
875   if (count <= 0) {
876     i_push_error(0, "No images provided to write");
877     return 0; /* what are you smoking? */
878   }
879
880   orig_count = quant->mc_count;
881   orig_size = quant->mc_size;
882
883   if (opts->each_palette) {
884     int want_trans = quant->transp != tr_none 
885       && imgs[0]->channels == 4;
886
887     /* if the caller gives us too many colours we can't do transparency */
888     if (want_trans && quant->mc_count == 256)
889       want_trans = 0;
890     /* if they want transparency but give us a big size, make it smaller
891        to give room for a transparency colour */
892     if (want_trans && quant->mc_size == 256)
893       --quant->mc_size;
894
895     /* we always generate a global palette - this lets systems with a 
896        broken giflib work */
897     quant_makemap(quant, imgs, 1);
898     result = quant_translate(quant, imgs[0]);
899
900     if (want_trans)
901       quant_transparent(quant, result, imgs[0], quant->mc_count);
902     
903     if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
904       myfree(result);
905       EGifCloseFile(gf);
906       mm_log((1, "Error in MakeMapObject."));
907       return 0;
908     }
909
910     color_bits = 1;
911     while (quant->mc_size > (1 << color_bits))
912       ++color_bits;
913   
914     if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
915       gif_push_error();
916       i_push_error(0, "Could not save screen descriptor");
917       FreeMapObject(map);
918       myfree(result);
919       EGifCloseFile(gf);
920       mm_log((1, "Error in EGifPutScreenDesc."));
921       return 0;
922     }
923     FreeMapObject(map);
924
925     if (!do_ns_loop(gf, opts))
926       return 0;
927
928     if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
929       myfree(result);
930       EGifCloseFile(gf);
931       return 0;
932     }
933     if (opts->position_count) {
934       posx = opts->positions[0].x;
935       posy = opts->positions[0].y;
936     }
937     else
938       posx = posy = 0;
939     if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize, 
940                          opts->interlace, NULL) == GIF_ERROR) {
941       gif_push_error();
942       i_push_error(0, "Could not save image descriptor");
943       EGifCloseFile(gf);
944       mm_log((1, "Error in EGifPutImageDesc."));
945       return 0;
946     }
947     if (!do_write(gf, opts, imgs[0], result)) {
948       EGifCloseFile(gf);
949       myfree(result);
950       return 0;
951     }
952     for (imgn = 1; imgn < count; ++imgn) {
953       quant->mc_count = orig_count;
954       quant->mc_size = orig_size;
955       want_trans = quant->transp != tr_none 
956         && imgs[0]->channels == 4;
957       /* if the caller gives us too many colours we can't do transparency */
958       if (want_trans && quant->mc_count == 256)
959         want_trans = 0;
960       /* if they want transparency but give us a big size, make it smaller
961          to give room for a transparency colour */
962       if (want_trans && quant->mc_size == 256)
963         --quant->mc_size;
964
965       quant_makemap(quant, imgs+imgn, 1);
966       result = quant_translate(quant, imgs[imgn]);
967       if (want_trans)
968         quant_transparent(quant, result, imgs[imgn], quant->mc_count);
969       
970       if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
971         myfree(result);
972         EGifCloseFile(gf);
973         return 0;
974       }
975       if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
976         myfree(result);
977         EGifCloseFile(gf);
978         mm_log((1, "Error in MakeMapObject."));
979         return 0;
980       }
981       if (imgn < opts->position_count) {
982         posx = opts->positions[imgn].x;
983         posy = opts->positions[imgn].y;
984       }
985       else
986         posx = posy = 0;
987       if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize, 
988                            imgs[imgn]->ysize, opts->interlace, 
989                            map) == GIF_ERROR) {
990         gif_push_error();
991         i_push_error(0, "Could not save image descriptor");
992         myfree(result);
993         FreeMapObject(map);
994         EGifCloseFile(gf);
995         mm_log((1, "Error in EGifPutImageDesc."));
996         return 0;
997       }
998       FreeMapObject(map);
999       
1000       if (!do_write(gf, opts, imgs[imgn], result)) {
1001         EGifCloseFile(gf);
1002         myfree(result);
1003         return 0;
1004       }
1005       myfree(result);
1006     }
1007   }
1008   else {
1009     int want_trans;
1010
1011     /* get a palette entry for the transparency iff we have an image
1012        with an alpha channel */
1013     want_trans = 0;
1014     for (imgn = 0; imgn < count; ++imgn) {
1015       if (imgs[imgn]->channels == 4) {
1016         ++want_trans;
1017         break;
1018       }
1019     }
1020     want_trans = want_trans && quant->transp != tr_none 
1021       && quant->mc_count < 256;
1022     if (want_trans && quant->mc_size == 256)
1023       --quant->mc_size;
1024
1025     /* handle the first image separately - since we allow giflib
1026        conversion and giflib doesn't give us a separate function to build
1027        the colormap. */
1028      
1029     /* produce a colour map */
1030     quant_makemap(quant, imgs, count);
1031     result = quant_translate(quant, imgs[0]);
1032
1033     if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1034       myfree(result);
1035       EGifCloseFile(gf);
1036       mm_log((1, "Error in MakeMapObject"));
1037       return 0;
1038     }
1039     color_bits = 1;
1040     while (quant->mc_count > (1 << color_bits))
1041       ++color_bits;
1042
1043     if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
1044       gif_push_error();
1045       i_push_error(0, "Could not save screen descriptor");
1046       FreeMapObject(map);
1047       myfree(result);
1048       EGifCloseFile(gf);
1049       mm_log((1, "Error in EGifPutScreenDesc."));
1050       return 0;
1051     }
1052     FreeMapObject(map);
1053
1054     if (!do_ns_loop(gf, opts))
1055       return 0;
1056
1057     if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
1058       myfree(result);
1059       EGifCloseFile(gf);
1060       return 0;
1061     }
1062     if (opts->position_count) {
1063       posx = opts->positions[0].x;
1064       posy = opts->positions[0].y;
1065     }
1066     else
1067       posx = posy = 0;
1068     if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize, 
1069                          opts->interlace, NULL) == GIF_ERROR) {
1070       gif_push_error();
1071       i_push_error(0, "Could not save image descriptor");
1072       EGifCloseFile(gf);
1073       mm_log((1, "Error in EGifPutImageDesc."));
1074       return 0;
1075     }
1076     if (want_trans && imgs[0]->channels == 4) 
1077       quant_transparent(quant, result, imgs[0], quant->mc_count);
1078
1079     if (!do_write(gf, opts, imgs[0], result)) {
1080       EGifCloseFile(gf);
1081       myfree(result);
1082       return 0;
1083     }
1084     myfree(result);
1085
1086     for (imgn = 1; imgn < count; ++imgn) {
1087       int local_trans;
1088       result = quant_translate(quant, imgs[imgn]);
1089       local_trans = want_trans && imgs[imgn]->channels == 4;
1090       if (local_trans)
1091         quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1092       if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
1093         myfree(result);
1094         EGifCloseFile(gf);
1095         return 0;
1096       }
1097       if (imgn < opts->position_count) {
1098         posx = opts->positions[imgn].x;
1099         posy = opts->positions[imgn].y;
1100       }
1101       else
1102         posx = posy = 0;
1103       if (EGifPutImageDesc(gf, posx, posy, 
1104                            imgs[imgn]->xsize, imgs[imgn]->ysize, 
1105                            opts->interlace, NULL) == GIF_ERROR) {
1106         gif_push_error();
1107         i_push_error(0, "Could not save image descriptor");
1108         myfree(result);
1109         EGifCloseFile(gf);
1110         mm_log((1, "Error in EGifPutImageDesc."));
1111         return 0;
1112       }
1113       if (!do_write(gf, opts, imgs[imgn], result)) {
1114         EGifCloseFile(gf);
1115         myfree(result);
1116         return 0;
1117       }
1118       myfree(result);
1119     }
1120   }
1121   if (EGifCloseFile(gf) == GIF_ERROR) {
1122     gif_push_error();
1123     i_push_error(0, "Could not close GIF file");
1124     mm_log((1, "Error in EGifCloseFile\n"));
1125     return 0;
1126   }
1127
1128   return 1;
1129 }
1130
1131 /*
1132 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1133
1134 General high-level function to write a GIF to a file.
1135
1136 Writes the GIF images to the specified file handle using the options
1137 in quant and opts.  See L<image.h/i_quantize> and
1138 L<image.h/i_gif_opts>.
1139
1140 Returns non-zero on success.
1141
1142 =cut
1143 */
1144
1145 undef_int
1146 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, 
1147                i_gif_opts *opts) {
1148   GifFileType *gf;
1149
1150   i_clear_error();
1151   mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n", 
1152           quant, fd, imgs, count, opts));
1153
1154   gif_set_version(quant, opts);
1155
1156   if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1157     gif_push_error();
1158     i_push_error(0, "Cannot create GIF file object");
1159     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1160     return 0;
1161   }
1162
1163   return i_writegif_low(quant, gf, imgs, count, opts);
1164 }
1165
1166 #if IM_GIFMAJOR >= 4
1167
1168 /*
1169 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1170
1171 Internal.  Wrapper for the user write callback function.
1172
1173 =cut
1174 */
1175
1176 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1177 {
1178   i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1179
1180   return i_gen_writer(gwd, data, size) ? size : 0;
1181 }
1182
1183 #endif
1184
1185 /*
1186 =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)
1187
1188 General high-level function to write a GIF using callbacks to send
1189 back the data.
1190
1191 Returns non-zero on success.
1192
1193 =cut
1194 */
1195
1196 undef_int
1197 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1198                     int maxlength, i_img **imgs, int count, i_gif_opts *opts)
1199 {
1200 #if IM_GIFMAJOR >= 4
1201   GifFileType *gf;
1202   i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1203   /* giflib declares this incorrectly as EgifOpen */
1204   extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
1205   int result;
1206
1207   i_clear_error();
1208
1209   mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n", 
1210           quant, cb, userdata, maxlength, imgs, count, opts));
1211   
1212   if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1213     gif_push_error();
1214     i_push_error(0, "Cannot create GIF file object");
1215     mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1216     free_gen_write_data(gwd, 0);
1217     return 0;
1218   }
1219
1220   result = i_writegif_low(quant, gf, imgs, count, opts);
1221   return free_gen_write_data(gwd, result);
1222 #else
1223   return 0;
1224 #endif
1225 }
1226
1227 /*
1228 =item gif_error_msg(int code)
1229
1230 Grabs the most recent giflib error code from GifLastError() and 
1231 returns a string that describes that error.
1232
1233 The returned pointer points to a static buffer, either from a literal
1234 C string or a static buffer.
1235
1236 =cut */
1237
1238 static char const *gif_error_msg(int code) {
1239   static char msg[80];
1240
1241   switch (code) {
1242   case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1243     return "Failed to open given file";
1244     
1245   case E_GIF_ERR_WRITE_FAILED:
1246     return "Write failed";
1247
1248   case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1249     return "Screen descriptor already passed to giflib";
1250
1251   case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1252     return "Image descriptor already passed to giflib";
1253     
1254   case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1255     return "Neither global nor local color map set";
1256
1257   case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1258     return "Too much pixel data passed to giflib";
1259
1260   case E_GIF_ERR_NOT_ENOUGH_MEM:
1261     return "Out of memory";
1262     
1263   case E_GIF_ERR_DISK_IS_FULL:
1264     return "Disk is full";
1265     
1266   case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1267     return "File close failed";
1268  
1269   case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1270     return "File not writable";
1271
1272   case D_GIF_ERR_OPEN_FAILED:
1273     return "Failed to open file";
1274     
1275   case D_GIF_ERR_READ_FAILED:
1276     return "Failed to read from file";
1277
1278   case D_GIF_ERR_NOT_GIF_FILE:
1279     return "File is not a GIF file";
1280
1281   case D_GIF_ERR_NO_SCRN_DSCR:
1282     return "No screen descriptor detected - invalid file";
1283
1284   case D_GIF_ERR_NO_IMAG_DSCR:
1285     return "No image descriptor detected - invalid file";
1286
1287   case D_GIF_ERR_NO_COLOR_MAP:
1288     return "No global or local color map found";
1289
1290   case D_GIF_ERR_WRONG_RECORD:
1291     return "Wrong record type detected - invalid file?";
1292
1293   case D_GIF_ERR_DATA_TOO_BIG:
1294     return "Data in file too big for image";
1295
1296   case D_GIF_ERR_NOT_ENOUGH_MEM:
1297     return "Out of memory";
1298
1299   case D_GIF_ERR_CLOSE_FAILED:
1300     return "Close failed";
1301
1302   case D_GIF_ERR_NOT_READABLE:
1303     return "File not opened for read";
1304
1305   case D_GIF_ERR_IMAGE_DEFECT:
1306     return "Defective image";
1307
1308   case D_GIF_ERR_EOF_TOO_SOON:
1309     return "Unexpected EOF - invalid file";
1310
1311   default:
1312     sprintf(msg, "Unknown giflib error code %d", code);
1313     return msg;
1314   }
1315 }
1316
1317 /*
1318 =item gif_push_error()
1319
1320 Utility function that takes the current GIF error code, converts it to
1321 an error message and pushes it on the error stack.
1322
1323 =cut
1324 */
1325
1326 static void gif_push_error() {
1327   int code = GifLastError(); /* clears saved error */
1328
1329   i_push_error(code, gif_error_msg(code));
1330 }
1331
1332 /*
1333 =head1 BUGS
1334
1335 The Netscape loop extension isn't implemented.  Giflib's extension
1336 writing code doesn't seem to support writing named extensions in this 
1337 form.
1338
1339 A bug in giflib is tickled by the i_writegif_callback().  This isn't a
1340 problem on ungiflib, but causes a SEGV on giflib.  A patch is provided
1341 in t/t10formats.t
1342
1343 The GIF file tag (GIF87a vs GIF89a) currently isn't set.  Using the
1344 supplied interface in giflib 4.1.0 causes a SEGV in
1345 EGifSetGifVersion().  See L<gif_set_version> for an explanation.
1346
1347 =head1 AUTHOR
1348
1349 Arnar M. Hrafnkelsson, addi@umich.edu
1350
1351 =head1 SEE ALSO
1352
1353 perl(1), Imager(3)
1354
1355 =cut
1356
1357 */