9 /* XXX: Reading still needs to support reading all those gif properties */
14 gif.c - read and write gif files for Imager
23 int max_colours; // number of bits per colour
24 int pixdev; // how much noise to add
25 i_color fixed[N]; // fixed palette entries
26 int fixedlen; // number of fixed colours
27 int success; // non-zero on success
28 char *data; // a GIF file in memory
29 int length; // how big data is
30 int reader(char *, char *, int, int);
31 int writer(char *, char *, int);
32 char *userdata; // user's data, whatever it is
36 img = i_readgif(fd, &colour_table, &colours);
37 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
38 success = i_writegifmc(img, fd, max_colours);
39 img = i_readgif_scalar(data, length, &colour_table, &colours);
40 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
41 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
42 success = i_writegif_callback(&quant, writer, userdata, maxlength,
47 This source file provides the C level interface to reading and writing
50 This has been tested with giflib 3 and 4, though you lose the callback
51 functionality with giflib3.
60 static char const *gif_error_msg(int code);
61 static void gif_push_error(void);
65 static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
70 Internal. A structure passed to the reader function used for reading
73 Used with giflib 4 and later.
78 struct gif_scalar_info {
85 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
87 Internal. The reader callback passed to giflib.
89 Used with giflib 4 and later.
95 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
96 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
97 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
99 if (gsi->cpos == gsi->length) return 0;
100 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
101 memcpy(buf,gsi->data+gsi->cpos,length);
110 /* Make some variables global, so we could access them faster: */
113 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
114 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
120 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
121 GifColorType *mapentry;
123 int colourmapsize = colourmap->ColorCount;
125 if(colours) *colours = colourmapsize;
126 if(!colour_table) return;
128 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
129 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
131 for(q=0; q<colourmapsize; q++) {
132 mapentry = &colourmap->Colors[q];
133 (*colour_table)[q*3 + 0] = mapentry->Red;
134 (*colour_table)[q*3 + 1] = mapentry->Green;
135 (*colour_table)[q*3 + 2] = mapentry->Blue;
141 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
143 Internal. Low-level function for reading a GIF file. The caller must
144 create the appropriate GifFileType object and pass it in.
150 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
152 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
153 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
154 ColorMapObject *ColorMap;
156 GifRecordType RecordType;
157 GifByteType *Extension;
160 static GifColorType *ColorMapEntry;
163 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
165 /* it's possible that the caller has called us with *colour_table being
166 non-NULL, but we check that to see if we need to free an allocated
167 colour table on error.
169 if (colour_table) *colour_table = NULL;
171 BackGround = GifFile->SBackGroundColor;
172 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
175 ColorMapSize = ColorMap->ColorCount;
176 i_colortable_copy(colour_table, colours, ColorMap);
180 if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
181 if (colour_table && *colour_table) {
182 myfree(*colour_table);
183 *colour_table = NULL;
185 DGifCloseFile(GifFile);
186 mm_log((1, "i_readgif: image size exceeds limits\n"));
190 im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
192 if (colour_table && *colour_table) {
193 myfree(*colour_table);
194 *colour_table = NULL;
196 DGifCloseFile(GifFile);
200 Size = GifFile->SWidth * sizeof(GifPixelType);
202 GifRow = mymalloc(Size);
204 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
206 /* Scan the content of the GIF file and load the image(s) in: */
208 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
210 i_push_error(0, "Unable to get record type");
211 if (colour_table && *colour_table) {
212 myfree(*colour_table);
213 *colour_table = NULL;
217 DGifCloseFile(GifFile);
221 switch (RecordType) {
222 case IMAGE_DESC_RECORD_TYPE:
223 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
225 i_push_error(0, "Unable to get image descriptor");
226 if (colour_table && *colour_table) {
227 myfree(*colour_table);
228 *colour_table = NULL;
232 DGifCloseFile(GifFile);
236 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
237 mm_log((1, "Adding local colormap\n"));
238 ColorMapSize = ColorMap->ColorCount;
240 i_colortable_copy(colour_table, colours, ColorMap);
244 /* No colormap and we are about to read in the image - abandon for now */
245 mm_log((1, "Going in with no colormap\n"));
246 i_push_error(0, "Image does not have a local or a global color map");
247 /* we can't have allocated a colour table here */
250 DGifCloseFile(GifFile);
254 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
255 Col = GifFile->Image.Left;
256 Width = GifFile->Image.Width;
257 Height = GifFile->Image.Height;
259 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
261 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
262 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
263 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
264 if (colour_table && *colour_table) {
265 myfree(*colour_table);
266 *colour_table = NULL;
270 DGifCloseFile(GifFile);
273 if (GifFile->Image.Interlace) {
275 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
277 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
279 i_push_error(0, "Reading GIF line");
280 if (colour_table && *colour_table) {
281 myfree(*colour_table);
282 *colour_table = NULL;
286 DGifCloseFile(GifFile);
290 for (x = 0; x < Width; x++) {
291 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
292 col.rgb.r = ColorMapEntry->Red;
293 col.rgb.g = ColorMapEntry->Green;
294 col.rgb.b = ColorMapEntry->Blue;
295 i_ppix(im,Col+x,j,&col);
301 for (i = 0; i < Height; i++) {
302 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
304 i_push_error(0, "Reading GIF line");
305 if (colour_table && *colour_table) {
306 myfree(*colour_table);
307 *colour_table = NULL;
311 DGifCloseFile(GifFile);
315 for (x = 0; x < Width; x++) {
316 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
317 col.rgb.r = ColorMapEntry->Red;
318 col.rgb.g = ColorMapEntry->Green;
319 col.rgb.b = ColorMapEntry->Blue;
320 i_ppix(im, Col+x, Row, &col);
326 case EXTENSION_RECORD_TYPE:
327 /* Skip any extension blocks in file: */
328 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
330 i_push_error(0, "Reading extension record");
331 if (colour_table && *colour_table) {
332 myfree(*colour_table);
333 *colour_table = NULL;
337 DGifCloseFile(GifFile);
340 while (Extension != NULL) {
341 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
343 i_push_error(0, "reading next block of extension");
344 if (colour_table && *colour_table) {
345 myfree(*colour_table);
346 *colour_table = NULL;
350 DGifCloseFile(GifFile);
355 case TERMINATE_RECORD_TYPE:
357 default: /* Should be traps by DGifGetRecordType. */
360 } while (RecordType != TERMINATE_RECORD_TYPE);
364 if (DGifCloseFile(GifFile) == GIF_ERROR) {
366 i_push_error(0, "Closing GIF file object");
367 if (colour_table && *colour_table) {
368 myfree(*colour_table);
369 *colour_table = NULL;
375 i_tags_add(&im->tags, "i_format", 0, "gif", -1, 0);
381 =item i_readgif(int fd, int **colour_table, int *colours)
383 Reads in a GIF file from a file handle and converts it to an Imager
386 Returns the palette for the object in colour_table for colours
389 Returns NULL on failure.
395 i_readgif(int fd, int **colour_table, int *colours) {
396 GifFileType *GifFile;
400 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
402 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
404 i_push_error(0, "Cannot create giflib file object");
405 mm_log((1,"i_readgif: Unable to open file\n"));
409 return i_readgif_low(GifFile, colour_table, colours);
414 Internal function called by i_readgif_multi_low() in error handling
417 static void free_images(i_img **imgs, int count) {
421 for (i = 0; i < count; ++i)
422 i_img_destroy(imgs[i]);
428 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
430 Reads one of more gif images from the given GIF file.
432 Returns a pointer to an array of i_img *, and puts the count into
435 If page is not -1 then the given image _only_ is returned from the
436 file, where the first image is 0, the second 1 and so on.
438 Unlike the normal i_readgif*() functions the images are paletted
439 images rather than a combined RGB image.
441 This functions sets tags on the images returned:
447 the offset of the image from the left of the "screen" ("Image Left
452 the offset of the image from the top of the "screen" ("Image Top Position")
456 non-zero if the image was interlaced ("Interlace Flag")
458 =item gif_screen_width
460 =item gif_screen_height
462 the size of the logical screen ("Logical Screen Width",
463 "Logical Screen Height")
467 Non-zero if this image had a local color map.
471 The index in the global colormap of the logical screen's background
472 color. This is only set if the current image uses the global
475 =item gif_trans_index
477 The index of the color in the colormap used for transparency. If the
478 image has a transparency then it is returned as a 4 channel image with
479 the alpha set to zero in this palette entry. ("Transparent Color Index")
483 The delay until the next frame is displayed, in 1/100 of a second.
488 whether or not a user input is expected before continuing (view dependent)
493 how the next frame is displayed ("Disposal Method")
497 the number of loops from the Netscape Loop extension. This may be zero.
501 the first block of the first gif comment before each image.
505 Where applicable, the ("name") is the name of that field from the GIF89
511 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
513 int i, j, Size, Width, Height, ExtCode, Count;
514 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
515 ColorMapObject *ColorMap;
517 GifRecordType RecordType;
518 GifByteType *Extension;
522 int trans_index = 0; /* transparent index if we see a GCE */
523 int gif_delay = 0; /* delay from a GCE */
524 int user_input = 0; /* user input flag from a GCE */
525 int disposal = 0; /* disposal method from a GCE */
528 char *comment = NULL; /* a comment */
529 i_img **results = NULL;
530 int result_alloc = 0;
532 int image_colors = 0;
533 i_color black; /* used to expand the palette if needed */
535 for (i = 0; i < MAXCHANNELS; ++i)
536 black.channel[i] = 0;
540 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
542 BackGround = GifFile->SBackGroundColor;
544 Size = GifFile->SWidth * sizeof(GifPixelType);
546 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
547 i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
549 /* Scan the content of the GIF file and load the image(s) in: */
551 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
553 i_push_error(0, "Unable to get record type");
554 free_images(results, *count);
555 DGifCloseFile(GifFile);
562 switch (RecordType) {
563 case IMAGE_DESC_RECORD_TYPE:
564 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
566 i_push_error(0, "Unable to get image descriptor");
567 free_images(results, *count);
568 DGifCloseFile(GifFile);
575 Width = GifFile->Image.Width;
576 Height = GifFile->Image.Height;
577 if (page == -1 || page == ImageNum) {
578 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
579 mm_log((1, "Adding local colormap\n"));
580 ColorMapSize = ColorMap->ColorCount;
582 /* No colormap and we are about to read in the image -
584 mm_log((1, "Going in with no colormap\n"));
585 i_push_error(0, "Image does not have a local or a global color map");
586 free_images(results, *count);
587 DGifCloseFile(GifFile);
595 if (got_gce && trans_index >= 0)
597 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
598 free_images(results, *count);
599 mm_log((1, "i_readgif: image size exceeds limits\n"));
600 DGifCloseFile(GifFile);
606 img = i_img_pal_new(Width, Height, channels, 256);
608 free_images(results, *count);
609 DGifCloseFile(GifFile);
615 /* populate the palette of the new image */
616 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
617 for (i = 0; i < ColorMapSize; ++i) {
619 col.rgba.r = ColorMap->Colors[i].Red;
620 col.rgba.g = ColorMap->Colors[i].Green;
621 col.rgba.b = ColorMap->Colors[i].Blue;
622 if (channels == 4 && trans_index == i)
627 i_addcolors(img, &col, 1);
629 image_colors = ColorMapSize;
631 if (*count > result_alloc) {
632 if (result_alloc == 0) {
634 results = mymalloc(result_alloc * sizeof(i_img *));
637 /* myrealloc never fails (it just dies if it can't allocate) */
639 results = myrealloc(results, result_alloc * sizeof(i_img *));
642 results[*count-1] = img;
643 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
644 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
646 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
647 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
648 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
649 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
650 i_tags_addn(&img->tags, "gif_colormap_size", 0, ColorMapSize);
651 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
652 i_tags_addn(&img->tags, "gif_background", 0,
653 GifFile->SBackGroundColor);
655 if (GifFile->Image.ColorMap) {
656 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
659 if (trans_index >= 0) {
661 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
662 i_getcolors(img, trans_index, &trans, 1);
663 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
665 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
666 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
667 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
671 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
673 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
678 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
679 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
681 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
682 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
683 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
684 free_images(results, *count);
685 DGifCloseFile(GifFile);
692 if (GifFile->Image.Interlace) {
693 for (Count = i = 0; i < 4; i++) {
694 for (j = InterlacedOffset[i]; j < Height;
695 j += InterlacedJumps[i]) {
697 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
699 i_push_error(0, "Reading GIF line");
700 free_images(results, *count);
701 DGifCloseFile(GifFile);
708 /* range check the scanline if needed */
709 if (image_colors != 256) {
711 for (x = 0; x < Width; ++x) {
712 while (GifRow[x] >= image_colors) {
713 /* expand the palette since a palette index is too big */
714 i_addcolors(img, &black, 1);
720 i_ppal(img, 0, Width, j, GifRow);
725 for (i = 0; i < Height; i++) {
726 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
728 i_push_error(0, "Reading GIF line");
729 free_images(results, *count);
730 DGifCloseFile(GifFile);
737 /* range check the scanline if needed */
738 if (image_colors != 256) {
740 for (x = 0; x < Width; ++x) {
741 while (GifRow[x] >= image_colors) {
742 /* expand the palette since a palette index is too big */
743 i_addcolors(img, &black, 1);
749 i_ppal(img, 0, Width, i, GifRow);
753 /* must be only one image wanted and that was it */
756 DGifCloseFile(GifFile);
764 /* whether interlaced or not, it has the same number of lines */
765 /* giflib does't have an interface to skip the image data */
766 for (i = 0; i < Height; i++) {
767 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
769 i_push_error(0, "Reading GIF line");
770 free_images(results, *count);
772 DGifCloseFile(GifFile);
779 /* kill the comment so we get the right comment for the page */
787 case EXTENSION_RECORD_TYPE:
788 /* Skip any extension blocks in file: */
789 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
791 i_push_error(0, "Reading extension record");
792 free_images(results, *count);
794 DGifCloseFile(GifFile);
799 /* possibly this should be an error, but "be liberal in what you accept" */
802 if (ExtCode == 0xF9) {
804 if (Extension[1] & 1)
805 trans_index = Extension[4];
808 gif_delay = Extension[2] + 256 * Extension[3];
809 user_input = (Extension[1] & 2) != 0;
810 disposal = (Extension[1] >> 2) & 7;
812 if (ExtCode == 0xFF && *Extension == 11) {
813 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
814 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
816 i_push_error(0, "reading loop extension");
817 free_images(results, *count);
819 DGifCloseFile(GifFile);
824 if (Extension && *Extension == 3) {
826 ns_loop = Extension[2] + 256 * Extension[3];
830 else if (ExtCode == 0xFE) {
831 /* while it's possible for a GIF file to contain more than one
832 comment, I'm only implementing a single comment per image,
833 with the comment saved into the following image.
834 If someone wants more than that they can implement it.
835 I also don't handle comments that take more than one block.
838 comment = mymalloc(*Extension+1);
839 memcpy(comment, Extension+1, *Extension);
840 comment[*Extension] = '\0';
843 while (Extension != NULL) {
844 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
846 i_push_error(0, "reading next block of extension");
847 free_images(results, *count);
849 DGifCloseFile(GifFile);
856 case TERMINATE_RECORD_TYPE:
858 default: /* Should be trapped by DGifGetRecordType. */
861 } while (RecordType != TERMINATE_RECORD_TYPE);
865 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
873 if (DGifCloseFile(GifFile) == GIF_ERROR) {
875 i_push_error(0, "Closing GIF file object");
876 free_images(results, *count);
880 if (ImageNum && page != -1) {
881 /* there were images, but the page selected wasn't found */
882 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
883 free_images(results, *count);
891 /* giflib declares this incorrectly as EgifOpen */
892 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
894 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
898 =item i_readgif_multi_wiol(ig, int *count)
904 i_readgif_multi_wiol(io_glue *ig, int *count) {
905 io_glue_commit_types(ig);
907 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
908 return i_readgif_multi(ig->source.fdseek.fd, count);
912 GifFileType *GifFile;
916 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
918 i_push_error(0, "Cannot create giflib callback object");
919 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
923 return i_readgif_multi_low(GifFile, count, -1);
926 i_push_error(0, "callbacks not supported with giflib3");
934 =item i_readgif_multi(int fd, int *count)
939 i_readgif_multi(int fd, int *count) {
940 GifFileType *GifFile;
944 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
946 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
948 i_push_error(0, "Cannot create giflib file object");
949 mm_log((1,"i_readgif: Unable to open file\n"));
953 return i_readgif_multi_low(GifFile, count, -1);
957 =item i_readgif_multi_scalar(char *data, int length, int *count)
962 i_readgif_multi_scalar(char *data, int length, int *count) {
964 GifFileType *GifFile;
965 struct gif_scalar_info gsi;
973 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
974 data, length, count));
976 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
978 i_push_error(0, "Cannot create giflib callback object");
979 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
983 return i_readgif_multi_low(GifFile, count, -1);
990 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
992 Read a GIF file into an Imager RGB file, the data of the GIF file is
993 retreived by callin the user supplied callback function.
995 This function is only used with giflib 4 and higher.
1001 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
1002 #if IM_GIFMAJOR >= 4
1003 GifFileType *GifFile;
1006 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1010 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
1011 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1013 i_push_error(0, "Cannot create giflib callback object");
1014 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1019 result = i_readgif_multi_low(GifFile, count, -1);
1020 i_free_gen_read_data(gci);
1029 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
1031 Write I<img> to the file handle I<fd>. The resulting GIF will use a
1032 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
1033 colours taken from I<fixed>.
1035 Returns non-zero on success.
1041 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
1042 i_color colors[256];
1045 memset(&quant, 0, sizeof(quant));
1046 quant.make_colors = mc_addi;
1047 quant.mc_colors = colors;
1048 quant.mc_size = 1<<max_colors;
1049 quant.mc_count = fixedlen;
1050 memcpy(colors, fixed, fixedlen * sizeof(i_color));
1051 quant.translate = pt_perturb;
1052 quant.perturb = pixdev;
1053 return i_writegif_gen(&quant, fd, &im, 1);
1057 =item i_writegifmc(i_img *im, int fd, int max_colors)
1059 Write I<img> to the file handle I<fd>. The resulting GIF will use a
1060 maximum of 1<<I<max_colours> colours.
1062 Returns non-zero on success.
1068 i_writegifmc(i_img *im, int fd, int max_colors) {
1069 i_color colors[256];
1072 /* *(char *)0 = 1; */
1074 memset(&quant, 0, sizeof(quant));
1075 quant.make_colors = mc_none; /* ignored for pt_giflib */
1076 quant.mc_colors = colors;
1077 quant.mc_size = 1 << max_colors;
1079 quant.translate = pt_giflib;
1080 return i_writegif_gen(&quant, fd, &im, 1);
1085 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1087 Reads a GIF file from an in memory copy of the file. This can be used
1088 if you get the 'file' from some source other than an actual file (or
1089 some other file handle).
1091 This function is only available with giflib 4 and higher.
1096 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1097 #if IM_GIFMAJOR >= 4
1098 GifFileType *GifFile;
1099 struct gif_scalar_info gsi;
1107 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1108 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1110 i_push_error(0, "Cannot create giflib callback object");
1111 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1115 return i_readgif_low(GifFile, colour_table, colours);
1121 #if IM_GIFMAJOR >= 4
1124 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1126 Internal. The reader callback wrapper passed to giflib.
1128 This function is only used with giflib 4 and higher.
1134 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1135 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1142 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1144 Read a GIF file into an Imager RGB file, the data of the GIF file is
1145 retreived by callin the user supplied callback function.
1147 This function is only used with giflib 4 and higher.
1153 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1154 #if IM_GIFMAJOR >= 4
1155 GifFileType *GifFile;
1158 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1162 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1163 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1165 i_push_error(0, "Cannot create giflib callback object");
1166 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1171 result = i_readgif_low(GifFile, colour_table, colours);
1172 i_free_gen_read_data(gci);
1177 i_push_error(0, "callbacks not supported with giflib3");
1183 #if IM_GIFMAJOR >= 4
1186 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1187 io_glue *ig = (io_glue *)gft->UserData;
1189 return ig->readcb(ig, buf, length);
1195 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1196 io_glue_commit_types(ig);
1198 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1199 int fd = dup(ig->source.fdseek.fd);
1201 i_push_error(errno, "dup() failed");
1204 return i_readgif(fd, color_table, colors);
1207 #if IM_GIFMAJOR >= 4
1208 GifFileType *GifFile;
1212 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1214 i_push_error(0, "Cannot create giflib callback object");
1215 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1219 return i_readgif_low(GifFile, color_table, colors);
1223 i_push_error(0, "callbacks not supported with giflib3");
1231 =item i_readgif_single_low(GifFile, page)
1233 Lower level function to read a single image from a GIF.
1235 page must be non-negative.
1240 i_readgif_single_low(GifFileType *GifFile, int page) {
1244 imgs = i_readgif_multi_low(GifFile, &count, page);
1246 if (imgs && count) {
1247 i_img *result = imgs[0];
1253 /* i_readgif_multi_low() handles the errors appropriately */
1259 =item i_readgif_single_wiol(ig, page)
1261 Read a single page from a GIF image file, where the page is indexed
1264 Returns NULL if the page isn't found.
1270 i_readgif_single_wiol(io_glue *ig, int page) {
1271 io_glue_commit_types(ig);
1276 i_push_error(0, "page must be non-negative");
1280 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1281 GifFileType *GifFile;
1282 int fd = dup(ig->source.fdseek.fd);
1284 i_push_error(errno, "dup() failed");
1287 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1289 i_push_error(0, "Cannot create giflib file object");
1290 mm_log((1,"i_readgif: Unable to open file\n"));
1293 return i_readgif_single_low(GifFile, page);
1296 #if IM_GIFMAJOR >= 4
1297 GifFileType *GifFile;
1299 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1301 i_push_error(0, "Cannot create giflib callback object");
1302 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1306 return i_readgif_single_low(GifFile, page);
1308 i_push_error(0, "callbacks not supported with giflib3");
1316 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1318 Internal. Low level image write function. Writes in interlace if
1319 that was requested in the GIF options.
1321 Returns non-zero on success.
1326 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1329 for (i = 0; i < 4; ++i) {
1330 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1331 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1333 i_push_error(0, "Could not save image data:");
1334 mm_log((1, "Error in EGifPutLine\n"));
1343 for (y = 0; y < img->ysize; ++y) {
1344 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1346 i_push_error(0, "Could not save image data:");
1347 mm_log((1, "Error in EGifPutLine\n"));
1359 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1361 Internal. Writes the GIF graphics control extension, if necessary.
1363 Returns non-zero on success.
1367 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1369 unsigned char gce[4] = {0};
1373 int disposal_method;
1377 gce[3] = trans_index;
1380 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1381 gce[1] = delay % 256;
1382 gce[2] = delay / 256;
1385 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1390 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1391 gce[0] |= (disposal_method & 3) << 2;
1395 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1397 i_push_error(0, "Could not save GCE");
1404 =item do_comments(gf, img)
1406 Write any comments in the image.
1410 static int do_comments(GifFileType *gf, i_img *img) {
1413 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1414 if (img->tags.tags[pos].data) {
1415 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1421 sprintf(buf, "%d", img->tags.tags[pos].idata);
1422 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1432 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1434 Internal. Add the Netscape2.0 loop extension block, if requested.
1436 Giflib/libungif prior to 4.1.1 didn't support writing application
1437 extension blocks, so we don't attempt to write them for older versions.
1439 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1440 writing extension blocks so that they could only be written to files.
1444 static int do_ns_loop(GifFileType *gf, i_img *img)
1446 /* EGifPutExtension() doesn't appear to handle application
1447 extension blocks in any way
1448 Since giflib wraps the fd with a FILE * (and puts that in its
1449 private data), we can't do an end-run and write the data
1451 There's no open interface that takes a FILE * either, so we
1452 can't workaround it that way either.
1453 If giflib's callback interface wasn't broken by default, I'd
1454 force file writes to use callbacks, but it is broken by default.
1456 /* yes this was another attempt at supporting the loop extension */
1457 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1459 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1460 unsigned char nsle[12] = "NETSCAPE2.0";
1461 unsigned char subblock[3];
1462 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1464 i_push_error(0, "writing loop extension");
1468 subblock[1] = loop_count % 256;
1469 subblock[2] = loop_count / 256;
1470 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1472 i_push_error(0, "writing loop extension sub-block");
1482 =item make_gif_map(i_quantize *quant, int want_trans)
1484 Create a giflib color map object from an Imager color map.
1489 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1491 GifColorType colors[256];
1493 int size = quant->mc_count;
1495 ColorMapObject *map;
1498 for (i = 0; i < quant->mc_count; ++i) {
1499 colors[i].Red = quant->mc_colors[i].rgb.r;
1500 colors[i].Green = quant->mc_colors[i].rgb.g;
1501 colors[i].Blue = quant->mc_colors[i].rgb.b;
1504 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1505 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1506 colors[size].Red = trans.rgb.r;
1507 colors[size].Green = trans.rgb.g;
1508 colors[size].Blue = trans.rgb.b;
1512 while (map_size < size)
1514 /* giflib spews for 1 colour maps, reasonable, I suppose */
1517 while (i < map_size) {
1518 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1522 map = MakeMapObject(map_size, colors);
1523 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1526 i_push_error(0, "Could not create color map object");
1533 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1535 We need to call EGifSetGifVersion() before opening the file - put that
1538 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1539 giflib 4.1.0 has code:
1541 static char *GifVersionPrefix = GIF87_STAMP;
1543 and the code that sets the version internally does:
1545 strncpy(&GifVersionPrefix[3], Version, 3);
1547 which is very broken.
1549 Failing to set the correct GIF version doesn't seem to cause a problem
1552 Modern versions (4.1.4 anyway) of giflib/libungif handle
1553 EGifSetGifVersion correctly.
1555 If t/t105gif.t crashes here then run Makefile.PL with
1556 --nogifsetversion, eg.:
1558 perl Makefile.PL --nogifsetversion
1560 or install a less buggy giflib.
1565 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1566 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1567 && !defined(IM_NO_SET_GIF_VERSION)
1572 if (quant->transp != tr_none)
1575 for (i = 0; i < count; ++i) {
1576 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1580 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1584 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1588 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1595 EGifSetGifVersion("89a");
1597 EGifSetGifVersion("87a");
1602 in_palette(i_color *c, i_quantize *quant, int size) {
1605 for (i = 0; i < size; ++i) {
1606 if (c->channel[0] == quant->mc_colors[i].channel[0]
1607 && c->channel[1] == quant->mc_colors[i].channel[1]
1608 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1617 =item has_common_palette(imgs, count, quant, want_trans)
1619 Tests if all the given images are paletted and have a common palette,
1620 if they do it builds that palette.
1622 A possible improvement might be to eliminate unused colors in the
1628 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1630 int size = quant->mc_count;
1635 /* we try to build a common palette here, if we can manage that, then
1636 that's the palette we use */
1637 for (imgn = 0; imgn < count; ++imgn) {
1638 int eliminate_unused;
1639 if (imgs[imgn]->type != i_palette_type)
1642 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1643 &eliminate_unused)) {
1644 eliminate_unused = 1;
1647 if (eliminate_unused) {
1648 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1650 memset(used, 0, sizeof(used));
1652 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1653 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1654 for (x = 0; x < imgs[imgn]->xsize; ++x)
1661 /* assume all are in use */
1662 memset(used, 1, sizeof(used));
1665 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1668 i_getcolors(imgs[imgn], i, &c, 1);
1670 if (in_palette(&c, quant, size) < 0) {
1671 if (size < quant->mc_size) {
1672 quant->mc_colors[size++] = c;
1675 /* oops, too many colors */
1683 quant->mc_count = size;
1689 quant_paletted(i_quantize *quant, i_img *img) {
1690 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1692 i_palidx trans[256];
1696 /* build a translation table */
1697 for (i = 0; i < i_colorcount(img); ++i) {
1699 i_getcolors(img, i, &c, 1);
1700 trans[i] = in_palette(&c, quant, quant->mc_count);
1703 for (y = 0; y < img->ysize; ++y) {
1704 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1705 for (x = 0; x < img->xsize; ++x) {
1715 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1717 Internal. Low-level function that does the high-level GIF processing
1720 Returns non-zero on success.
1726 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1727 unsigned char *result = NULL;
1729 ColorMapObject *map;
1730 int scrw = 0, scrh = 0;
1731 int imgn, orig_count, orig_size;
1733 int trans_index = -1;
1737 i_img **glob_imgs; /* images that will use the global color map */
1739 i_color *orig_colors = quant->mc_colors;
1740 i_color *glob_colors = NULL;
1741 int glob_color_count = 0;
1742 int glob_want_trans;
1743 int glob_paletted = 0; /* the global map was made from the image palettes */
1744 int colors_paletted = 0;
1749 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1750 quant, gf, imgs, count));
1752 /* *((char *)0) = 1; */ /* used to break into the debugger */
1755 i_push_error(0, "No images provided to write");
1756 return 0; /* what are you smoking? */
1759 i_mempool_init(&mp);
1761 /* sanity is nice */
1762 if (quant->mc_size > 256)
1763 quant->mc_size = 256;
1764 if (quant->mc_count > quant->mc_size)
1765 quant->mc_count = quant->mc_size;
1767 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1769 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
1773 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1774 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1776 glob_want_trans = 0;
1777 for (imgn = 0; imgn < count; ++imgn) {
1779 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1780 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1781 if (imgs[imgn]->xsize + posx > scrw)
1782 scrw = imgs[imgn]->xsize + posx;
1783 if (imgs[imgn]->ysize + posy > scrh)
1784 scrh = imgs[imgn]->ysize + posy;
1785 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1786 localmaps[imgn] = 0;
1787 if (localmaps[imgn])
1790 if (imgs[imgn]->channels == 4) {
1791 glob_want_trans = 1;
1793 glob_imgs[glob_img_count++] = imgs[imgn];
1796 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1798 orig_count = quant->mc_count;
1799 orig_size = quant->mc_size;
1801 if (glob_img_count) {
1803 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1804 quant->mc_colors = glob_colors;
1805 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1806 /* we have some images that want to use the global map */
1807 if (glob_want_trans && quant->mc_count == 256) {
1808 mm_log((2, " disabling transparency for global map - no space\n"));
1809 glob_want_trans = 0;
1811 if (glob_want_trans && quant->mc_size == 256) {
1812 mm_log((2, " reserving color for transparency\n"));
1815 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1820 i_quant_makemap(quant, glob_imgs, glob_img_count);
1822 glob_color_count = quant->mc_count;
1823 quant->mc_colors = orig_colors;
1826 /* use the global map if we have one, otherwise use the local map */
1829 quant->mc_colors = glob_colors;
1830 quant->mc_count = glob_color_count;
1831 want_trans = glob_want_trans && imgs[0]->channels == 4;
1833 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1835 if (gif_background < 0)
1837 if (gif_background >= glob_color_count)
1841 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1842 if (has_common_palette(imgs, 1, quant, want_trans)) {
1843 colors_paletted = 1;
1846 colors_paletted = 0;
1847 i_quant_makemap(quant, imgs, 1);
1850 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1851 i_mempool_destroy(&mp);
1852 quant->mc_colors = orig_colors;
1854 mm_log((1, "Error in MakeMapObject"));
1859 /* since we don't know how big some the local palettes could be
1860 we need to base the bits on the maximum number of colors */
1861 while (orig_size > (1 << color_bits))
1865 int count = quant->mc_count;
1868 while (count > (1 << color_bits))
1872 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1873 gif_background, map) == GIF_ERROR) {
1874 i_mempool_destroy(&mp);
1875 quant->mc_colors = orig_colors;
1877 i_push_error(0, "Could not save screen descriptor");
1881 mm_log((1, "Error in EGifPutScreenDesc."));
1886 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1888 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1891 if (!localmaps[0]) {
1893 colors_paletted = glob_paletted;
1896 /* if this image has a global map the colors in quant don't
1897 belong to this image, so build a palette */
1899 /* generate the local map for this image */
1900 quant->mc_colors = orig_colors;
1901 quant->mc_size = orig_size;
1902 quant->mc_count = orig_count;
1903 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1905 /* if the caller gives us too many colours we can't do transparency */
1906 if (want_trans && quant->mc_count == 256)
1908 /* if they want transparency but give us a big size, make it smaller
1909 to give room for a transparency colour */
1910 if (want_trans && quant->mc_size == 256)
1912 if (has_common_palette(imgs, 1, quant, want_trans)) {
1913 colors_paletted = 1;
1916 colors_paletted = 0;
1917 i_quant_makemap(quant, imgs, 1);
1919 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1920 i_mempool_destroy(&mp);
1922 quant->mc_colors = orig_colors;
1923 mm_log((1, "Error in MakeMapObject"));
1928 /* the map we wrote was the map for this image - don't set the local
1934 if (colors_paletted)
1935 result = quant_paletted(quant, imgs[0]);
1937 result = i_quant_translate(quant, imgs[0]);
1939 i_mempool_destroy(&mp);
1940 quant->mc_colors = orig_colors;
1945 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1946 trans_index = quant->mc_count;
1949 if (!do_ns_loop(gf, imgs[0])) {
1950 i_mempool_destroy(&mp);
1951 quant->mc_colors = orig_colors;
1955 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1956 i_mempool_destroy(&mp);
1957 quant->mc_colors = orig_colors;
1963 if (!do_comments(gf, imgs[0])) {
1964 i_mempool_destroy(&mp);
1965 quant->mc_colors = orig_colors;
1971 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1973 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1974 interlace, map) == GIF_ERROR) {
1975 i_mempool_destroy(&mp);
1976 quant->mc_colors = orig_colors;
1978 i_push_error(0, "Could not save image descriptor");
1980 mm_log((1, "Error in EGifPutImageDesc."));
1986 if (!do_write(gf, interlace, imgs[0], result)) {
1987 i_mempool_destroy(&mp);
1988 quant->mc_colors = orig_colors;
1995 /* that first awful image is out of the way, do the rest */
1996 for (imgn = 1; imgn < count; ++imgn) {
1997 if (localmaps[imgn]) {
1998 quant->mc_colors = orig_colors;
1999 quant->mc_count = orig_count;
2000 quant->mc_size = orig_size;
2002 want_trans = quant->transp != tr_none
2003 && imgs[imgn]->channels == 4;
2004 /* if the caller gives us too many colours we can't do transparency */
2005 if (want_trans && quant->mc_count == 256)
2007 /* if they want transparency but give us a big size, make it smaller
2008 to give room for a transparency colour */
2009 if (want_trans && quant->mc_size == 256)
2012 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
2013 result = quant_paletted(quant, imgs[imgn]);
2016 i_quant_makemap(quant, imgs+imgn, 1);
2017 result = i_quant_translate(quant, imgs[imgn]);
2020 i_mempool_destroy(&mp);
2021 quant->mc_colors = orig_colors;
2023 mm_log((1, "error in i_quant_translate()"));
2027 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2028 trans_index = quant->mc_count;
2031 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
2032 i_mempool_destroy(&mp);
2033 quant->mc_colors = orig_colors;
2036 mm_log((1, "Error in MakeMapObject."));
2041 quant->mc_colors = glob_colors;
2042 quant->mc_count = glob_color_count;
2044 result = quant_paletted(quant, imgs[imgn]);
2046 result = i_quant_translate(quant, imgs[imgn]);
2047 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
2049 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2050 trans_index = quant->mc_count;
2055 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
2056 i_mempool_destroy(&mp);
2057 quant->mc_colors = orig_colors;
2063 if (!do_comments(gf, imgs[imgn])) {
2064 i_mempool_destroy(&mp);
2065 quant->mc_colors = orig_colors;
2071 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2073 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2076 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2078 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
2079 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2080 i_mempool_destroy(&mp);
2081 quant->mc_colors = orig_colors;
2083 i_push_error(0, "Could not save image descriptor");
2088 mm_log((1, "Error in EGifPutImageDesc."));
2094 if (!do_write(gf, interlace, imgs[imgn], result)) {
2095 i_mempool_destroy(&mp);
2096 quant->mc_colors = orig_colors;
2104 if (EGifCloseFile(gf) == GIF_ERROR) {
2105 i_mempool_destroy(&mp);
2107 i_push_error(0, "Could not close GIF file");
2108 mm_log((1, "Error in EGifCloseFile\n"));
2113 for (i = 0; i < glob_color_count; ++i)
2114 orig_colors[i] = glob_colors[i];
2117 i_mempool_destroy(&mp);
2118 quant->mc_colors = orig_colors;
2124 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2126 General high-level function to write a GIF to a file.
2128 Writes the GIF images to the specified file handle using the options
2129 in quant and opts. See L<image.h/i_quantize> and
2130 L<image.h/i_gif_opts>.
2132 Returns non-zero on success.
2138 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2142 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2143 quant, fd, imgs, count));
2145 gif_set_version(quant, imgs, count);
2147 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2149 i_push_error(0, "Cannot create GIF file object");
2150 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2154 return i_writegif_low(quant, gf, imgs, count);
2157 #if IM_GIFMAJOR >= 4
2160 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2162 Internal. Wrapper for the user write callback function.
2167 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2169 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2171 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2177 =item i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata, int maxlength, i_img **imgs, int count, i_gif_opts *opts)
2179 General high-level function to write a GIF using callbacks to send
2182 Returns non-zero on success.
2188 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2189 int maxlength, i_img **imgs, int count)
2191 #if IM_GIFMAJOR >= 4
2193 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2198 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2199 quant, cb, userdata, maxlength, imgs, count));
2201 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2203 i_push_error(0, "Cannot create GIF file object");
2204 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2205 i_free_gen_write_data(gwd, 0);
2209 result = i_writegif_low(quant, gf, imgs, count);
2210 return i_free_gen_write_data(gwd, result);
2213 i_push_error(0, "callbacks not supported with giflib3");
2219 #if IM_GIFMAJOR >= 4
2222 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2223 io_glue *ig = (io_glue *)gft->UserData;
2225 return ig->writecb(ig, data, length);
2231 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2236 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2238 io_glue_commit_types(ig);
2240 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2241 int fd = dup(ig->source.fdseek.fd);
2243 i_push_error(errno, "dup() failed");
2246 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2247 is called - dup it so the caller's fd isn't closed */
2248 return i_writegif_gen(quant, fd, imgs, count);
2251 #if IM_GIFMAJOR >= 4
2252 GifFileType *GifFile;
2257 gif_set_version(quant, imgs, count);
2259 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2261 i_push_error(0, "Cannot create giflib callback object");
2262 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2266 result = i_writegif_low(quant, GifFile, imgs, count);
2273 i_push_error(0, "callbacks not supported with giflib3");
2281 =item gif_error_msg(int code)
2283 Grabs the most recent giflib error code from GifLastError() and
2284 returns a string that describes that error.
2286 The returned pointer points to a static buffer, either from a literal
2287 C string or a static buffer.
2292 static char const *gif_error_msg(int code) {
2293 static char msg[80];
2296 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2297 return "Failed to open given file";
2299 case E_GIF_ERR_WRITE_FAILED:
2300 return "Write failed";
2302 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2303 return "Screen descriptor already passed to giflib";
2305 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2306 return "Image descriptor already passed to giflib";
2308 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2309 return "Neither global nor local color map set";
2311 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2312 return "Too much pixel data passed to giflib";
2314 case E_GIF_ERR_NOT_ENOUGH_MEM:
2315 return "Out of memory";
2317 case E_GIF_ERR_DISK_IS_FULL:
2318 return "Disk is full";
2320 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2321 return "File close failed";
2323 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2324 return "File not writable";
2326 case D_GIF_ERR_OPEN_FAILED:
2327 return "Failed to open file";
2329 case D_GIF_ERR_READ_FAILED:
2330 return "Failed to read from file";
2332 case D_GIF_ERR_NOT_GIF_FILE:
2333 return "File is not a GIF file";
2335 case D_GIF_ERR_NO_SCRN_DSCR:
2336 return "No screen descriptor detected - invalid file";
2338 case D_GIF_ERR_NO_IMAG_DSCR:
2339 return "No image descriptor detected - invalid file";
2341 case D_GIF_ERR_NO_COLOR_MAP:
2342 return "No global or local color map found";
2344 case D_GIF_ERR_WRONG_RECORD:
2345 return "Wrong record type detected - invalid file?";
2347 case D_GIF_ERR_DATA_TOO_BIG:
2348 return "Data in file too big for image";
2350 case D_GIF_ERR_NOT_ENOUGH_MEM:
2351 return "Out of memory";
2353 case D_GIF_ERR_CLOSE_FAILED:
2354 return "Close failed";
2356 case D_GIF_ERR_NOT_READABLE:
2357 return "File not opened for read";
2359 case D_GIF_ERR_IMAGE_DEFECT:
2360 return "Defective image";
2362 case D_GIF_ERR_EOF_TOO_SOON:
2363 return "Unexpected EOF - invalid file";
2366 sprintf(msg, "Unknown giflib error code %d", code);
2372 =item gif_push_error()
2374 Utility function that takes the current GIF error code, converts it to
2375 an error message and pushes it on the error stack.
2380 static void gif_push_error(void) {
2381 int code = GifLastError(); /* clears saved error */
2383 i_push_error(code, gif_error_msg(code));
2389 The Netscape loop extension isn't implemented. Giflib's extension
2390 writing code doesn't seem to support writing named extensions in this
2393 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2394 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2397 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2398 supplied interface in giflib 4.1.0 causes a SEGV in
2399 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2403 Arnar M. Hrafnkelsson, addi@umich.edu