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