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