16 imgif.c - read and write gif files for Imager
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
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,
49 This source file provides the C level interface to reading and writing
52 This has been tested with giflib 3 and 4, though you lose the callback
53 functionality with giflib3.
63 #define IMGIFLIB_API_VERSION (GIFLIB_MAJOR * 100 + GIFLIB_MINOR)
65 /* only matters for pre-5.0 which we either reject, or which contains
66 no significant API changes */
67 #define IMGIFLIB_API_VERSION 0
70 #if IMGIFLIB_API_VERSION >= 500
71 #define POST_SET_VERSION
72 #define myDGifOpen(userPtr, readFunc, Error) DGifOpen((userPtr), (readFunc), (Error))
73 #define myEGifOpen(userPtr, readFunc, Error) EGifOpen((userPtr), (readFunc), (Error))
74 #define myGifError(gif) ((gif)->Error)
75 #define MakeMapObject GifMakeMapObject
76 #define FreeMapObject GifFreeMapObject
77 #define gif_mutex_lock(mutex)
78 #define gif_mutex_unlock(mutex)
80 #define PRE_SET_VERSION
82 myDGifOpen(void *userPtr, InputFunc readFunc, int *error) {
83 GifFileType *result = DGifOpen(userPtr, readFunc);
85 *error = GifLastError();
90 myEGifOpen(void *userPtr, OutputFunc outputFunc, int *error) {
91 GifFileType *result = EGifOpen(userPtr, outputFunc);
93 *error = GifLastError();
97 #define myGifError(gif) GifLastError()
98 #define gif_mutex_lock(mutex) i_mutex_lock(mutex)
99 #define gif_mutex_unlock(mutex) i_mutex_unlock(mutex)
103 #if IMGIFLIB_API_VERSION >= 501
104 #define myDGifCloseFile(gif, perror) (DGifCloseFile((gif), (perror)))
105 #define myEGifCloseFile(gif, perror) (EGifCloseFile((gif), (perror)))
108 myDGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
109 int result = DGifCloseFile(GifFile);
110 if (result == GIF_ERROR) {
112 *ErrorCode = myGifError(GifFile);
113 free(GifFile->Private);
121 myEGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
122 int result = EGifCloseFile(GifFile);
123 if (result == GIF_ERROR) {
125 *ErrorCode = myGifError(GifFile);
126 free(GifFile->Private);
134 static char const *gif_error_msg(int code);
135 static void gif_push_error(int code);
137 /* Make some variables global, so we could access them faster: */
140 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
141 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
143 #if IMGIFLIB_API_VERSION < 500
144 static i_mutex_t mutex;
149 #if IMGIFLIB_API_VERSION < 500
150 mutex = i_mutex_new();
156 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
157 GifColorType *mapentry;
159 int colourmapsize = colourmap->ColorCount;
161 if(colours) *colours = colourmapsize;
162 if(!colour_table) return;
164 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
165 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
167 for(q=0; q<colourmapsize; q++) {
168 mapentry = &colourmap->Colors[q];
169 (*colour_table)[q*3 + 0] = mapentry->Red;
170 (*colour_table)[q*3 + 1] = mapentry->Green;
171 (*colour_table)[q*3 + 2] = mapentry->Blue;
175 #ifdef GIF_LIB_VERSION
178 char gif_version_str[] = GIF_LIB_VERSION;
181 i_giflib_version(void) {
182 const char *p = gif_version_str;
184 while (*p && (*p < '0' || *p > '9'))
190 return strtod(p, NULL);
196 i_giflib_version(void) {
197 return GIFLIB_MAJOR + GIFLIB_MINOR * 0.1;
203 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
205 Internal. Low-level function for reading a GIF file. The caller must
206 create the appropriate GifFileType object and pass it in.
212 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
214 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
215 int cmapcnt = 0, ImageNum = 0;
216 ColorMapObject *ColorMap;
218 GifRecordType RecordType;
219 GifByteType *Extension;
222 GifColorType *ColorMapEntry;
226 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
228 /* it's possible that the caller has called us with *colour_table being
229 non-NULL, but we check that to see if we need to free an allocated
230 colour table on error.
232 if (colour_table) *colour_table = NULL;
234 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
237 i_colortable_copy(colour_table, colours, ColorMap);
241 if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
242 if (colour_table && *colour_table) {
243 myfree(*colour_table);
244 *colour_table = NULL;
246 (void)myDGifCloseFile(GifFile, NULL);
247 mm_log((1, "i_readgif: image size exceeds limits\n"));
251 im = i_img_8_new(GifFile->SWidth, GifFile->SHeight, 3);
253 if (colour_table && *colour_table) {
254 myfree(*colour_table);
255 *colour_table = NULL;
257 (void)myDGifCloseFile(GifFile, NULL);
261 Size = GifFile->SWidth * sizeof(GifPixelType);
263 GifRow = mymalloc(Size);
265 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
267 /* Scan the content of the GIF file and load the image(s) in: */
269 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
270 gif_push_error(myGifError(GifFile));
271 i_push_error(0, "Unable to get record type");
272 if (colour_table && *colour_table) {
273 myfree(*colour_table);
274 *colour_table = NULL;
278 (void)myDGifCloseFile(GifFile, NULL);
282 switch (RecordType) {
283 case IMAGE_DESC_RECORD_TYPE:
284 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
285 gif_push_error(myGifError(GifFile));
286 i_push_error(0, "Unable to get image descriptor");
287 if (colour_table && *colour_table) {
288 myfree(*colour_table);
289 *colour_table = NULL;
293 (void)myDGifCloseFile(GifFile, NULL);
297 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
298 mm_log((1, "Adding local colormap\n"));
300 i_colortable_copy(colour_table, colours, ColorMap);
304 /* No colormap and we are about to read in the image - abandon for now */
305 mm_log((1, "Going in with no colormap\n"));
306 i_push_error(0, "Image does not have a local or a global color map");
307 /* we can't have allocated a colour table here */
310 (void)myDGifCloseFile(GifFile, NULL);
314 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
315 Col = GifFile->Image.Left;
316 Width = GifFile->Image.Width;
317 Height = GifFile->Image.Height;
319 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
321 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
322 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
323 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
324 if (colour_table && *colour_table) {
325 myfree(*colour_table);
326 *colour_table = NULL;
330 (void)myDGifCloseFile(GifFile, NULL);
333 if (GifFile->Image.Interlace) {
335 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[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;
346 (void)myDGifCloseFile(GifFile, NULL);
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,j,&col);
361 for (i = 0; i < Height; i++) {
362 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
363 gif_push_error(myGifError(GifFile));
364 i_push_error(0, "Reading GIF line");
365 if (colour_table && *colour_table) {
366 myfree(*colour_table);
367 *colour_table = NULL;
371 (void)myDGifCloseFile(GifFile, NULL);
375 for (x = 0; x < Width; x++) {
376 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
377 col.rgb.r = ColorMapEntry->Red;
378 col.rgb.g = ColorMapEntry->Green;
379 col.rgb.b = ColorMapEntry->Blue;
380 i_ppix(im, Col+x, Row, &col);
386 case EXTENSION_RECORD_TYPE:
387 /* Skip any extension blocks in file: */
388 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
389 gif_push_error(myGifError(GifFile));
390 i_push_error(0, "Reading extension record");
391 if (colour_table && *colour_table) {
392 myfree(*colour_table);
393 *colour_table = NULL;
397 (void)myDGifCloseFile(GifFile, NULL);
400 while (Extension != NULL) {
401 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
402 gif_push_error(myGifError(GifFile));
403 i_push_error(0, "reading next block of extension");
404 if (colour_table && *colour_table) {
405 myfree(*colour_table);
406 *colour_table = NULL;
410 (void)myDGifCloseFile(GifFile, NULL);
415 case TERMINATE_RECORD_TYPE:
417 default: /* Should be traps by DGifGetRecordType. */
420 } while (RecordType != TERMINATE_RECORD_TYPE);
424 if (myDGifCloseFile(GifFile, &error) == GIF_ERROR) {
425 gif_push_error(error);
426 i_push_error(0, "Closing GIF file object");
427 if (colour_table && *colour_table) {
428 myfree(*colour_table);
429 *colour_table = NULL;
435 i_tags_set(&im->tags, "i_format", "gif", -1);
442 Internal function called by i_readgif_multi_low() in error handling
446 free_images(i_img **imgs, int count) {
450 for (i = 0; i < count; ++i)
451 i_img_destroy(imgs[i]);
457 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
459 Reads one of more gif images from the given GIF file.
461 Returns a pointer to an array of i_img *, and puts the count into
464 If page is not -1 then the given image _only_ is returned from the
465 file, where the first image is 0, the second 1 and so on.
467 Unlike the normal i_readgif*() functions the images are paletted
468 images rather than a combined RGB image.
470 This functions sets tags on the images returned:
476 the offset of the image from the left of the "screen" ("Image Left
481 the offset of the image from the top of the "screen" ("Image Top Position")
485 non-zero if the image was interlaced ("Interlace Flag")
487 =item gif_screen_width
489 =item gif_screen_height
491 the size of the logical screen ("Logical Screen Width",
492 "Logical Screen Height")
496 Non-zero if this image had a local color map.
500 The index in the global colormap of the logical screen's background
501 color. This is only set if the current image uses the global
504 =item gif_trans_index
506 The index of the color in the colormap used for transparency. If the
507 image has a transparency then it is returned as a 4 channel image with
508 the alpha set to zero in this palette entry. ("Transparent Color Index")
512 The delay until the next frame is displayed, in 1/100 of a second.
517 whether or not a user input is expected before continuing (view dependent)
522 how the next frame is displayed ("Disposal Method")
526 the number of loops from the Netscape Loop extension. This may be zero.
530 the first block of the first gif comment before each image.
534 Where applicable, the ("name") is the name of that field from the GIF89
541 i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
543 int i, j, Size, Width, Height, ExtCode, Count;
544 int ImageNum = 0, ColorMapSize = 0;
545 ColorMapObject *ColorMap;
547 GifRecordType RecordType;
548 GifByteType *Extension;
552 int trans_index = 0; /* transparent index if we see a GCE */
553 int gif_delay = 0; /* delay from a GCE */
554 int user_input = 0; /* user input flag from a GCE */
555 int disposal = 0; /* disposal method from a GCE */
558 char *comment = NULL; /* a comment */
559 i_img **results = NULL;
560 int result_alloc = 0;
562 int image_colors = 0;
563 i_color black; /* used to expand the palette if needed */
566 for (i = 0; i < MAXCHANNELS; ++i)
567 black.channel[i] = 0;
571 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
573 Size = GifFile->SWidth * sizeof(GifPixelType);
575 GifRow = (GifRowType) mymalloc(Size);
577 /* Scan the content of the GIF file and load the image(s) in: */
579 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
580 gif_push_error(myGifError(GifFile));
581 i_push_error(0, "Unable to get record type");
582 free_images(results, *count);
583 (void)myDGifCloseFile(GifFile, NULL);
590 switch (RecordType) {
591 case IMAGE_DESC_RECORD_TYPE:
592 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
593 gif_push_error(myGifError(GifFile));
594 i_push_error(0, "Unable to get image descriptor");
595 free_images(results, *count);
596 (void)myDGifCloseFile(GifFile, NULL);
603 Width = GifFile->Image.Width;
604 Height = GifFile->Image.Height;
605 if (page == -1 || page == ImageNum) {
606 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
607 mm_log((1, "Adding local colormap\n"));
608 ColorMapSize = ColorMap->ColorCount;
610 /* No colormap and we are about to read in the image -
612 mm_log((1, "Going in with no colormap\n"));
613 i_push_error(0, "Image does not have a local or a global color map");
614 free_images(results, *count);
615 (void)myDGifCloseFile(GifFile, NULL);
623 if (got_gce && trans_index >= 0)
625 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
626 free_images(results, *count);
627 mm_log((1, "i_readgif: image size exceeds limits\n"));
628 (void)myDGifCloseFile(GifFile, NULL);
634 img = i_img_pal_new(Width, Height, channels, 256);
636 free_images(results, *count);
637 (void)myDGifCloseFile(GifFile, NULL);
643 /* populate the palette of the new image */
644 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
645 for (i = 0; i < ColorMapSize; ++i) {
647 col.rgba.r = ColorMap->Colors[i].Red;
648 col.rgba.g = ColorMap->Colors[i].Green;
649 col.rgba.b = ColorMap->Colors[i].Blue;
650 if (channels == 4 && trans_index == i)
655 i_addcolors(img, &col, 1);
657 image_colors = ColorMapSize;
659 if (*count > result_alloc) {
660 if (result_alloc == 0) {
662 results = mymalloc(result_alloc * sizeof(i_img *));
665 /* myrealloc never fails (it just dies if it can't allocate) */
667 results = myrealloc(results, result_alloc * sizeof(i_img *));
670 results[*count-1] = img;
671 i_tags_set(&img->tags, "i_format", "gif", -1);
672 i_tags_setn(&img->tags, "gif_left", GifFile->Image.Left);
674 i_tags_setn(&img->tags, "gif_top", GifFile->Image.Top);
675 i_tags_setn(&img->tags, "gif_interlace", GifFile->Image.Interlace);
676 i_tags_setn(&img->tags, "gif_screen_width", GifFile->SWidth);
677 i_tags_setn(&img->tags, "gif_screen_height", GifFile->SHeight);
678 i_tags_setn(&img->tags, "gif_colormap_size", ColorMapSize);
679 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
680 i_tags_setn(&img->tags, "gif_background",
681 GifFile->SBackGroundColor);
683 if (GifFile->Image.ColorMap) {
684 i_tags_setn(&img->tags, "gif_localmap", 1);
687 if (trans_index >= 0) {
689 i_tags_setn(&img->tags, "gif_trans_index", trans_index);
690 i_getcolors(img, trans_index, &trans, 1);
691 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
693 i_tags_setn(&img->tags, "gif_delay", gif_delay);
694 i_tags_setn(&img->tags, "gif_user_input", user_input);
695 i_tags_setn(&img->tags, "gif_disposal", disposal);
699 i_tags_setn(&img->tags, "gif_loop", ns_loop);
701 i_tags_set(&img->tags, "gif_comment", comment, strlen(comment));
706 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
707 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
709 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
710 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
711 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
712 free_images(results, *count);
713 (void)myDGifCloseFile(GifFile, NULL);
720 if (GifFile->Image.Interlace) {
721 for (Count = i = 0; i < 4; i++) {
722 for (j = InterlacedOffset[i]; j < Height;
723 j += InterlacedJumps[i]) {
725 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
726 gif_push_error(myGifError(GifFile));
727 i_push_error(0, "Reading GIF line");
728 free_images(results, *count);
729 (void)myDGifCloseFile(GifFile, NULL);
736 /* range check the scanline if needed */
737 if (image_colors != 256) {
739 for (x = 0; x < Width; ++x) {
740 while (GifRow[x] >= image_colors) {
741 /* expand the palette since a palette index is too big */
742 i_addcolors(img, &black, 1);
748 i_ppal(img, 0, Width, j, GifRow);
753 for (i = 0; i < Height; i++) {
754 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
755 gif_push_error(myGifError(GifFile));
756 i_push_error(0, "Reading GIF line");
757 free_images(results, *count);
758 (void)myDGifCloseFile(GifFile, NULL);
765 /* range check the scanline if needed */
766 if (image_colors != 256) {
768 for (x = 0; x < Width; ++x) {
769 while (GifRow[x] >= image_colors) {
770 /* expand the palette since a palette index is too big */
771 i_addcolors(img, &black, 1);
777 i_ppal(img, 0, Width, i, GifRow);
781 /* must be only one image wanted and that was it */
784 (void)myDGifCloseFile(GifFile, NULL);
792 /* whether interlaced or not, it has the same number of lines */
793 /* giflib does't have an interface to skip the image data */
794 for (i = 0; i < Height; i++) {
795 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
796 gif_push_error(myGifError(GifFile));
797 i_push_error(0, "Reading GIF line");
798 free_images(results, *count);
800 (void)myDGifCloseFile(GifFile, NULL);
807 /* kill the comment so we get the right comment for the page */
815 case EXTENSION_RECORD_TYPE:
816 /* Skip any extension blocks in file: */
817 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
818 gif_push_error(myGifError(GifFile));
819 i_push_error(0, "Reading extension record");
820 free_images(results, *count);
822 (void)myDGifCloseFile(GifFile, NULL);
827 /* possibly this should be an error, but "be liberal in what you accept" */
830 if (ExtCode == 0xF9) {
832 if (Extension[1] & 1)
833 trans_index = Extension[4];
836 gif_delay = Extension[2] + 256 * Extension[3];
837 user_input = (Extension[1] & 2) != 0;
838 disposal = (Extension[1] >> 2) & 7;
840 if (ExtCode == 0xFF && *Extension == 11) {
841 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
842 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
843 gif_push_error(myGifError(GifFile));
844 i_push_error(0, "reading loop extension");
845 free_images(results, *count);
847 (void)myDGifCloseFile(GifFile, NULL);
852 if (Extension && *Extension == 3) {
854 ns_loop = Extension[2] + 256 * Extension[3];
858 else if (ExtCode == 0xFE) {
859 /* while it's possible for a GIF file to contain more than one
860 comment, I'm only implementing a single comment per image,
861 with the comment saved into the following image.
862 If someone wants more than that they can implement it.
863 I also don't handle comments that take more than one block.
866 comment = mymalloc(*Extension+1);
867 memcpy(comment, Extension+1, *Extension);
868 comment[*Extension] = '\0';
871 while (Extension != NULL) {
872 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
873 gif_push_error(myGifError(GifFile));
874 i_push_error(0, "reading next block of extension");
875 free_images(results, *count);
877 (void)myDGifCloseFile(GifFile, NULL);
884 case TERMINATE_RECORD_TYPE:
886 default: /* Should be trapped by DGifGetRecordType. */
889 } while (RecordType != TERMINATE_RECORD_TYPE);
893 i_tags_set(&(results[*count-1]->tags), "gif_comment", comment,
901 if (myDGifCloseFile(GifFile, &error) == GIF_ERROR) {
902 gif_push_error(error);
903 i_push_error(0, "Closing GIF file object");
904 free_images(results, *count);
908 if (ImageNum && page != -1) {
909 /* there were images, but the page selected wasn't found */
910 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
911 free_images(results, *count);
918 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
921 =item i_readgif_multi_wiol(ig, int *count)
927 i_readgif_multi_wiol(io_glue *ig, int *count) {
928 GifFileType *GifFile;
932 gif_mutex_lock(mutex);
936 if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
937 gif_push_error(gif_error);
938 i_push_error(0, "Cannot create giflib callback object");
939 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
940 gif_mutex_unlock(mutex);
944 result = i_readgif_multi_low(GifFile, count, -1);
946 gif_mutex_unlock(mutex);
952 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
953 io_glue *ig = (io_glue *)gft->UserData;
955 return i_io_read(ig, buf, length);
959 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
960 GifFileType *GifFile;
964 gif_mutex_lock(mutex);
968 if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
969 gif_push_error(gif_error);
970 i_push_error(0, "Cannot create giflib callback object");
971 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
972 gif_mutex_unlock(mutex);
976 result = i_readgif_low(GifFile, color_table, colors);
978 gif_mutex_unlock(mutex);
984 =item i_readgif_single_low(GifFile, page)
986 Lower level function to read a single image from a GIF.
988 page must be non-negative.
993 i_readgif_single_low(GifFileType *GifFile, int page) {
997 imgs = i_readgif_multi_low(GifFile, &count, page);
1000 i_img *result = imgs[0];
1006 /* i_readgif_multi_low() handles the errors appropriately */
1012 =item i_readgif_single_wiol(ig, page)
1014 Read a single page from a GIF image file, where the page is indexed
1017 Returns NULL if the page isn't found.
1023 i_readgif_single_wiol(io_glue *ig, int page) {
1024 GifFileType *GifFile;
1030 i_push_error(0, "page must be non-negative");
1034 gif_mutex_lock(mutex);
1036 if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
1037 gif_push_error(gif_error);
1038 i_push_error(0, "Cannot create giflib callback object");
1039 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1040 gif_mutex_unlock(mutex);
1044 result = i_readgif_single_low(GifFile, page);
1046 gif_mutex_unlock(mutex);
1052 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1054 Internal. Low level image write function. Writes in interlace if
1055 that was requested in the GIF options.
1057 Returns non-zero on success.
1062 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1065 for (i = 0; i < 4; ++i) {
1066 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1067 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1068 gif_push_error(myGifError(gf));
1069 i_push_error(0, "Could not save image data:");
1070 mm_log((1, "Error in EGifPutLine\n"));
1078 for (y = 0; y < img->ysize; ++y) {
1079 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1080 gif_push_error(myGifError(gf));
1081 i_push_error(0, "Could not save image data:");
1082 mm_log((1, "Error in EGifPutLine\n"));
1093 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1095 Internal. Writes the GIF graphics control extension, if necessary.
1097 Returns non-zero on success.
1103 do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1105 unsigned char gce[4] = {0};
1109 int disposal_method;
1113 gce[3] = trans_index;
1116 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1117 gce[1] = delay % 256;
1118 gce[2] = delay / 256;
1121 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1126 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1127 gce[0] |= (disposal_method & 3) << 2;
1131 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1132 gif_push_error(myGifError(gf));
1133 i_push_error(0, "Could not save GCE");
1140 =item do_comments(gf, img)
1142 Write any comments in the image.
1148 do_comments(GifFileType *gf, i_img *img) {
1151 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1152 if (img->tags.tags[pos].data) {
1153 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1159 #ifdef IMAGER_SNPRINTF
1160 snprintf(buf, sizeof(buf), "%d", img->tags.tags[pos].idata);
1162 sprintf(buf, "%d", img->tags.tags[pos].idata);
1164 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1174 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1176 Internal. Add the Netscape2.0 loop extension block, if requested.
1178 Giflib/libungif prior to 4.1.1 didn't support writing application
1179 extension blocks, so we don't attempt to write them for older versions.
1181 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1182 writing extension blocks so that they could only be written to files.
1188 do_ns_loop(GifFileType *gf, i_img *img)
1190 /* EGifPutExtension() doesn't appear to handle application
1191 extension blocks in any way
1192 Since giflib wraps the fd with a FILE * (and puts that in its
1193 private data), we can't do an end-run and write the data
1195 There's no open interface that takes a FILE * either, so we
1196 can't workaround it that way either.
1197 If giflib's callback interface wasn't broken by default, I'd
1198 force file writes to use callbacks, but it is broken by default.
1200 /* yes this was another attempt at supporting the loop extension */
1202 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1203 unsigned char nsle[12] = "NETSCAPE2.0";
1204 unsigned char subblock[3];
1207 subblock[1] = loop_count % 256;
1208 subblock[2] = loop_count / 256;
1210 #if IMGIFLIB_API_VERSION >= 500
1211 if (EGifPutExtensionLeader(gf, APPLICATION_EXT_FUNC_CODE) == GIF_ERROR
1212 || EGifPutExtensionBlock(gf, 11, nsle) == GIF_ERROR
1213 || EGifPutExtensionBlock(gf, 3, subblock) == GIF_ERROR
1214 || EGifPutExtensionTrailer(gf) == GIF_ERROR) {
1215 gif_push_error(myGifError(gf));
1216 i_push_error(0, "writing loop extension");
1221 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1222 gif_push_error(myGifError(gf));
1223 i_push_error(0, "writing loop extension");
1226 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1227 gif_push_error(myGifError(gf));
1228 i_push_error(0, "writing loop extension sub-block");
1238 =item make_gif_map(i_quantize *quant, int want_trans)
1240 Create a giflib color map object from an Imager color map.
1245 static ColorMapObject *
1246 make_gif_map(i_quantize *quant, i_img *img, int want_trans) {
1247 GifColorType colors[256];
1249 int size = quant->mc_count;
1251 ColorMapObject *map;
1254 for (i = 0; i < quant->mc_count; ++i) {
1255 colors[i].Red = quant->mc_colors[i].rgb.r;
1256 colors[i].Green = quant->mc_colors[i].rgb.g;
1257 colors[i].Blue = quant->mc_colors[i].rgb.b;
1260 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1261 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1262 colors[size].Red = trans.rgb.r;
1263 colors[size].Green = trans.rgb.g;
1264 colors[size].Blue = trans.rgb.b;
1268 while (map_size < size)
1270 /* giflib spews for 1 colour maps, reasonable, I suppose */
1273 while (i < map_size) {
1274 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1278 map = MakeMapObject(map_size, colors);
1279 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1281 i_push_error(0, "Could not create color map object");
1284 #if IMGIFLIB_API_VERSION >= 500
1291 =item need_version_89a(i_quantize *quant, i_img *imgs, int count)
1293 Return true if the file we're creating on these images needs a GIF89a
1300 need_version_89a(i_quantize *quant, i_img **imgs, int count) {
1305 for (i = 0; i < count; ++i) {
1306 if (quant->transp != tr_none &&
1307 (imgs[i]->channels == 2 || imgs[i]->channels == 4)) {
1311 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1315 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1319 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1323 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1333 in_palette(i_color *c, i_quantize *quant, int size) {
1336 for (i = 0; i < size; ++i) {
1337 if (c->channel[0] == quant->mc_colors[i].channel[0]
1338 && c->channel[1] == quant->mc_colors[i].channel[1]
1339 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1348 =item has_common_palette(imgs, count, quant)
1350 Tests if all the given images are paletted and their colors are in the
1353 Previously this would build a consolidated palette from the source,
1354 but that meant that if the caller supplied a static palette (or
1355 specified a fixed palette like "webmap") then we wouldn't be
1356 quantizing to the caller specified palette.
1362 has_common_palette(i_img **imgs, int count, i_quantize *quant) {
1368 /* we try to build a common palette here, if we can manage that, then
1369 that's the palette we use */
1370 for (imgn = 0; imgn < count; ++imgn) {
1371 int eliminate_unused;
1372 if (imgs[imgn]->type != i_palette_type)
1375 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1376 &eliminate_unused)) {
1377 eliminate_unused = 1;
1380 if (eliminate_unused) {
1381 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1383 memset(used, 0, sizeof(used));
1385 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1386 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1387 for (x = 0; x < imgs[imgn]->xsize; ++x)
1394 /* assume all are in use */
1395 memset(used, 1, sizeof(used));
1398 col_count = i_colorcount(imgs[imgn]);
1399 for (i = 0; i < col_count; ++i) {
1402 i_getcolors(imgs[imgn], i, &c, 1);
1404 if (in_palette(&c, quant, quant->mc_count) < 0) {
1405 mm_log((1, " color not found in palette, no palette shortcut\n"));
1413 mm_log((1, " all colors found in palette, palette shortcut\n"));
1419 quant_paletted(i_quantize *quant, i_img *img) {
1420 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1422 i_palidx trans[256];
1426 /* build a translation table */
1427 for (i = 0; i < i_colorcount(img); ++i) {
1429 i_getcolors(img, i, &c, 1);
1430 trans[i] = in_palette(&c, quant, quant->mc_count);
1433 for (y = 0; y < img->ysize; ++y) {
1434 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1435 for (x = 0; x < img->xsize; ++x) {
1445 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1447 Internal. Low-level function that does the high-level GIF processing
1450 Returns non-zero on success.
1456 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1457 unsigned char *result = NULL;
1459 ColorMapObject *map;
1460 int scrw = 0, scrh = 0;
1461 int imgn, orig_count, orig_size;
1463 int trans_index = -1;
1466 i_img **glob_imgs; /* images that will use the global color map */
1468 i_color *orig_colors = quant->mc_colors;
1469 i_color *glob_colors = NULL;
1470 int glob_color_count = 0;
1471 int glob_want_trans;
1472 int glob_paletted = 0; /* the global map was made from the image palettes */
1473 int colors_paletted = 0;
1479 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1480 quant, gf, imgs, count));
1482 /* *((char *)0) = 1; */ /* used to break into the debugger */
1485 i_push_error(0, "No images provided to write");
1486 return 0; /* what are you smoking? */
1489 /* sanity is nice */
1490 if (quant->mc_size > 256)
1491 quant->mc_size = 256;
1492 if (quant->mc_count > quant->mc_size)
1493 quant->mc_count = quant->mc_size;
1495 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1497 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
1501 localmaps = mymalloc(sizeof(int) * count);
1502 glob_imgs = mymalloc(sizeof(i_img *) * count);
1504 glob_want_trans = 0;
1505 for (imgn = 0; imgn < count; ++imgn) {
1506 i_img *im = imgs[imgn];
1507 if (im->xsize > 0xFFFF || im->ysize > 0xFFFF) {
1508 i_push_error(0, "image too large for GIF");
1513 i_tags_get_int(&im->tags, "gif_left", 0, &posx);
1514 if (posx < 0) posx = 0;
1515 i_tags_get_int(&im->tags, "gif_top", 0, &posy);
1516 if (posy < 0) posy = 0;
1517 if (im->xsize + posx > scrw)
1518 scrw = im->xsize + posx;
1519 if (im->ysize + posy > scrh)
1520 scrh = im->ysize + posy;
1521 if (!i_tags_get_int(&im->tags, "gif_local_map", 0, localmaps+imgn))
1522 localmaps[imgn] = 0;
1523 if (localmaps[imgn])
1526 if (im->channels == 4) {
1527 glob_want_trans = 1;
1529 glob_imgs[glob_img_count++] = im;
1532 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1534 if (scrw > 0xFFFF || scrh > 0xFFFF) {
1535 i_push_error(0, "screen size too large for GIF");
1539 orig_count = quant->mc_count;
1540 orig_size = quant->mc_size;
1542 if (glob_img_count) {
1544 glob_colors = mymalloc(sizeof(i_color) * quant->mc_size);
1545 quant->mc_colors = glob_colors;
1546 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1547 /* we have some images that want to use the global map */
1548 if (glob_want_trans && quant->mc_count == 256) {
1549 mm_log((2, " disabling transparency for global map - no space\n"));
1550 glob_want_trans = 0;
1552 if (glob_want_trans && quant->mc_size == 256) {
1553 mm_log((2, " reserving color for transparency\n"));
1557 i_quant_makemap(quant, glob_imgs, glob_img_count);
1558 glob_paletted = has_common_palette(glob_imgs, glob_img_count, quant);
1559 glob_color_count = quant->mc_count;
1560 quant->mc_colors = orig_colors;
1563 /* use the global map if we have one, otherwise use the local map */
1566 quant->mc_colors = glob_colors;
1567 quant->mc_count = glob_color_count;
1568 want_trans = glob_want_trans && imgs[0]->channels == 4;
1570 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1572 if (gif_background < 0)
1574 if (gif_background >= glob_color_count)
1578 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1579 i_quant_makemap(quant, imgs, 1);
1580 colors_paletted = has_common_palette(imgs, 1, quant);
1583 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1584 myfree(glob_colors);
1587 quant->mc_colors = orig_colors;
1588 (void)myEGifCloseFile(gf, NULL);
1589 mm_log((1, "Error in MakeMapObject"));
1594 /* since we don't know how big some the local palettes could be
1595 we need to base the bits on the maximum number of colors */
1596 while (orig_size > (1 << color_bits))
1600 int count = quant->mc_count;
1603 while (count > (1 << color_bits))
1607 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1608 gif_background, map) == GIF_ERROR) {
1609 myfree(glob_colors);
1612 quant->mc_colors = orig_colors;
1613 gif_push_error(myGifError(gf));
1614 i_push_error(0, "Could not save screen descriptor");
1617 (void)myEGifCloseFile(gf, NULL);
1618 mm_log((1, "Error in EGifPutScreenDesc."));
1623 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1625 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1628 if (!localmaps[0]) {
1630 colors_paletted = glob_paletted;
1633 /* if this image has a global map the colors in quant don't
1634 belong to this image, so build a palette */
1636 /* generate the local map for this image */
1637 quant->mc_colors = orig_colors;
1638 quant->mc_size = orig_size;
1639 quant->mc_count = orig_count;
1640 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1642 /* if the caller gives us too many colours we can't do transparency */
1643 if (want_trans && quant->mc_count == 256)
1645 /* if they want transparency but give us a big size, make it smaller
1646 to give room for a transparency colour */
1647 if (want_trans && quant->mc_size == 256)
1649 i_quant_makemap(quant, imgs, 1);
1650 colors_paletted = has_common_palette(imgs, 1, quant);
1651 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1652 myfree(glob_colors);
1655 (void)myEGifCloseFile(gf, NULL);
1656 quant->mc_colors = orig_colors;
1657 mm_log((1, "Error in MakeMapObject"));
1662 /* the map we wrote was the map for this image - don't set the local
1668 if (colors_paletted)
1669 result = quant_paletted(quant, imgs[0]);
1671 result = i_quant_translate(quant, imgs[0]);
1673 myfree(glob_colors);
1676 quant->mc_colors = orig_colors;
1677 (void)myEGifCloseFile(gf, NULL);
1681 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1682 trans_index = quant->mc_count;
1685 if (!do_ns_loop(gf, imgs[0])) {
1686 myfree(glob_colors);
1689 quant->mc_colors = orig_colors;
1693 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1694 myfree(glob_colors);
1697 quant->mc_colors = orig_colors;
1699 (void)myEGifCloseFile(gf, NULL);
1703 if (!do_comments(gf, imgs[0])) {
1704 myfree(glob_colors);
1707 quant->mc_colors = orig_colors;
1709 (void)myEGifCloseFile(gf, NULL);
1713 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1715 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1716 interlace, map) == GIF_ERROR) {
1717 myfree(glob_colors);
1720 quant->mc_colors = orig_colors;
1721 gif_push_error(myGifError(gf));
1722 i_push_error(0, "Could not save image descriptor");
1723 (void)myEGifCloseFile(gf, NULL);
1724 mm_log((1, "Error in EGifPutImageDesc."));
1730 if (!do_write(gf, interlace, imgs[0], result)) {
1731 myfree(glob_colors);
1734 quant->mc_colors = orig_colors;
1735 (void)myEGifCloseFile(gf, NULL);
1741 /* that first awful image is out of the way, do the rest */
1742 for (imgn = 1; imgn < count; ++imgn) {
1743 if (localmaps[imgn]) {
1744 quant->mc_colors = orig_colors;
1745 quant->mc_count = orig_count;
1746 quant->mc_size = orig_size;
1748 want_trans = quant->transp != tr_none
1749 && imgs[imgn]->channels == 4;
1750 /* if the caller gives us too many colours we can't do transparency */
1751 if (want_trans && quant->mc_count == 256)
1753 /* if they want transparency but give us a big size, make it smaller
1754 to give room for a transparency colour */
1755 if (want_trans && quant->mc_size == 256)
1758 if (has_common_palette(imgs+imgn, 1, quant)) {
1759 result = quant_paletted(quant, imgs[imgn]);
1762 i_quant_makemap(quant, imgs+imgn, 1);
1763 result = i_quant_translate(quant, imgs[imgn]);
1766 myfree(glob_colors);
1769 quant->mc_colors = orig_colors;
1770 (void)myEGifCloseFile(gf, NULL);
1771 mm_log((1, "error in i_quant_translate()"));
1775 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1776 trans_index = quant->mc_count;
1779 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1780 myfree(glob_colors);
1783 quant->mc_colors = orig_colors;
1785 (void)myEGifCloseFile(gf, NULL);
1786 mm_log((1, "Error in MakeMapObject."));
1791 quant->mc_colors = glob_colors;
1792 quant->mc_count = glob_color_count;
1794 result = quant_paletted(quant, imgs[imgn]);
1796 result = i_quant_translate(quant, imgs[imgn]);
1797 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1799 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1800 trans_index = quant->mc_count;
1805 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1806 myfree(glob_colors);
1809 quant->mc_colors = orig_colors;
1811 (void)myEGifCloseFile(gf, NULL);
1815 if (!do_comments(gf, imgs[imgn])) {
1816 myfree(glob_colors);
1819 quant->mc_colors = orig_colors;
1821 (void)myEGifCloseFile(gf, NULL);
1825 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1827 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1830 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1832 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1833 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1834 myfree(glob_colors);
1837 quant->mc_colors = orig_colors;
1838 gif_push_error(myGifError(gf));
1839 i_push_error(0, "Could not save image descriptor");
1843 (void)myEGifCloseFile(gf, NULL);
1844 mm_log((1, "Error in EGifPutImageDesc."));
1850 if (!do_write(gf, interlace, imgs[imgn], result)) {
1851 myfree(glob_colors);
1854 quant->mc_colors = orig_colors;
1855 (void)myEGifCloseFile(gf, NULL);
1862 if (myEGifCloseFile(gf, &error) == GIF_ERROR) {
1863 myfree(glob_colors);
1866 gif_push_error(error);
1867 i_push_error(0, "Could not close GIF file");
1868 mm_log((1, "Error in EGifCloseFile\n"));
1873 for (i = 0; i < glob_color_count; ++i)
1874 orig_colors[i] = glob_colors[i];
1877 myfree(glob_colors);
1880 quant->mc_colors = orig_colors;
1886 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1887 io_glue *ig = (io_glue *)gft->UserData;
1889 return i_io_write(ig, data, length);
1894 =item i_writegif_wiol(ig, quant, opts, imgs, count)
1899 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
1901 GifFileType *GifFile;
1905 gif_mutex_lock(mutex);
1909 #ifdef PRE_SET_VERSION
1910 EGifSetGifVersion(need_version_89a(quant, imgs, count) ? "89a" : "87a");
1913 if ((GifFile = myEGifOpen((void *)ig, io_glue_write_cb, &gif_error )) == NULL) {
1914 gif_push_error(gif_error);
1915 i_push_error(0, "Cannot create giflib callback object");
1916 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
1917 gif_mutex_unlock(mutex);
1921 #ifdef POST_SET_VERSION
1922 EGifSetGifVersion(GifFile, need_version_89a(quant, imgs, count));
1925 result = i_writegif_low(quant, GifFile, imgs, count);
1927 gif_mutex_unlock(mutex);
1936 =item gif_error_msg(int code)
1938 Grabs the most recent giflib error code from GifLastError() and
1939 returns a string that describes that error.
1941 Returns NULL for unknown error codes.
1947 gif_error_msg(int code) {
1948 #if IMGIFLIB_API_VERSION >= 500
1949 return GifErrorString(code);
1952 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1953 return "Failed to open given file";
1955 case E_GIF_ERR_WRITE_FAILED:
1956 return "Write failed";
1958 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1959 return "Screen descriptor already passed to giflib";
1961 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1962 return "Image descriptor already passed to giflib";
1964 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1965 return "Neither global nor local color map set";
1967 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1968 return "Too much pixel data passed to giflib";
1970 case E_GIF_ERR_NOT_ENOUGH_MEM:
1971 return "Out of memory";
1973 case E_GIF_ERR_DISK_IS_FULL:
1974 return "Disk is full";
1976 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1977 return "File close failed";
1979 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1980 return "File not writable";
1982 case D_GIF_ERR_OPEN_FAILED:
1983 return "Failed to open file";
1985 case D_GIF_ERR_READ_FAILED:
1986 return "Failed to read from file";
1988 case D_GIF_ERR_NOT_GIF_FILE:
1989 return "File is not a GIF file";
1991 case D_GIF_ERR_NO_SCRN_DSCR:
1992 return "No screen descriptor detected - invalid file";
1994 case D_GIF_ERR_NO_IMAG_DSCR:
1995 return "No image descriptor detected - invalid file";
1997 case D_GIF_ERR_NO_COLOR_MAP:
1998 return "No global or local color map found";
2000 case D_GIF_ERR_WRONG_RECORD:
2001 return "Wrong record type detected - invalid file?";
2003 case D_GIF_ERR_DATA_TOO_BIG:
2004 return "Data in file too big for image";
2006 case D_GIF_ERR_NOT_ENOUGH_MEM:
2007 return "Out of memory";
2009 case D_GIF_ERR_CLOSE_FAILED:
2010 return "Close failed";
2012 case D_GIF_ERR_NOT_READABLE:
2013 return "File not opened for read";
2015 case D_GIF_ERR_IMAGE_DEFECT:
2016 return "Defective image";
2018 case D_GIF_ERR_EOF_TOO_SOON:
2019 return "Unexpected EOF - invalid file";
2028 =item gif_push_error(code)
2030 Utility function that takes the current GIF error code, converts it to
2031 an error message and pushes it on the error stack.
2037 gif_push_error(int code) {
2038 const char *msg = gif_error_msg(code);
2040 i_push_error(code, msg);
2042 i_push_errorf(code, "Unknown GIF error %d", code);
2048 Arnar M. Hrafnkelsson, addi@umich.edu
2050 Tony Cook <tonyc@cpan.org>