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