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