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