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);
560 switch (RecordType) {
561 case IMAGE_DESC_RECORD_TYPE:
562 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
564 i_push_error(0, "Unable to get image descriptor");
565 free_images(results, *count);
566 DGifCloseFile(GifFile);
571 Width = GifFile->Image.Width;
572 Height = GifFile->Image.Height;
573 if (page == -1 || page == ImageNum) {
574 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
575 mm_log((1, "Adding local colormap\n"));
576 ColorMapSize = ColorMap->ColorCount;
578 /* No colormap and we are about to read in the image -
580 mm_log((1, "Going in with no colormap\n"));
581 i_push_error(0, "Image does not have a local or a global color map");
582 free_images(results, *count);
583 DGifCloseFile(GifFile);
589 if (got_gce && trans_index >= 0)
591 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
592 free_images(results, *count);
593 mm_log((1, "i_readgif: image size exceeds limits\n"));
594 DGifCloseFile(GifFile);
598 img = i_img_pal_new(Width, Height, channels, 256);
600 free_images(results, *count);
601 DGifCloseFile(GifFile);
604 /* populate the palette of the new image */
605 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
606 for (i = 0; i < ColorMapSize; ++i) {
608 col.rgba.r = ColorMap->Colors[i].Red;
609 col.rgba.g = ColorMap->Colors[i].Green;
610 col.rgba.b = ColorMap->Colors[i].Blue;
611 if (channels == 4 && trans_index == i)
616 i_addcolors(img, &col, 1);
618 image_colors = ColorMapSize;
620 if (*count > result_alloc) {
621 if (result_alloc == 0) {
623 results = mymalloc(result_alloc * sizeof(i_img *));
626 /* myrealloc never fails (it just dies if it can't allocate) */
628 results = myrealloc(results, result_alloc * sizeof(i_img *));
631 results[*count-1] = img;
632 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
633 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
635 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
636 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
637 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
638 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
639 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
640 i_tags_addn(&img->tags, "gif_background", 0,
641 GifFile->SBackGroundColor);
643 if (GifFile->Image.ColorMap) {
644 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
647 if (trans_index >= 0) {
649 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
650 i_getcolors(img, trans_index, &trans, 1);
651 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
653 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
654 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
655 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
659 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
661 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
666 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
667 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
669 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
670 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
671 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
672 free_images(results, *count);
673 DGifCloseFile(GifFile);
678 if (GifFile->Image.Interlace) {
679 for (Count = i = 0; i < 4; i++) {
680 for (j = InterlacedOffset[i]; j < Height;
681 j += InterlacedJumps[i]) {
683 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
685 i_push_error(0, "Reading GIF line");
686 free_images(results, *count);
687 DGifCloseFile(GifFile);
692 /* range check the scanline if needed */
693 if (image_colors != 256) {
695 for (x = 0; x < Width; ++x) {
696 while (GifRow[x] >= image_colors) {
697 /* expand the palette since a palette index is too big */
698 i_addcolors(img, &black, 1);
704 i_ppal(img, 0, Width, j, GifRow);
709 for (i = 0; i < Height; i++) {
710 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
712 i_push_error(0, "Reading GIF line");
713 free_images(results, *count);
714 DGifCloseFile(GifFile);
719 /* range check the scanline if needed */
720 if (image_colors != 256) {
722 for (x = 0; x < Width; ++x) {
723 while (GifRow[x] >= image_colors) {
724 /* expand the palette since a palette index is too big */
725 i_addcolors(img, &black, 1);
731 i_ppal(img, 0, Width, i, GifRow);
735 /* must be only one image wanted and that was it */
738 DGifCloseFile(GifFile);
744 /* whether interlaced or not, it has the same number of lines */
745 /* giflib does't have an interface to skip the image data */
746 for (i = 0; i < Height; i++) {
747 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
749 i_push_error(0, "Reading GIF line");
750 free_images(results, *count);
752 DGifCloseFile(GifFile);
757 /* kill the comment so we get the right comment for the page */
765 case EXTENSION_RECORD_TYPE:
766 /* Skip any extension blocks in file: */
767 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
769 i_push_error(0, "Reading extension record");
770 free_images(results, *count);
771 DGifCloseFile(GifFile);
774 if (ExtCode == 0xF9) {
776 if (Extension[1] & 1)
777 trans_index = Extension[4];
780 gif_delay = Extension[2] + 256 * Extension[3];
781 user_input = (Extension[1] & 2) != 0;
782 disposal = (Extension[1] >> 2) & 7;
784 if (ExtCode == 0xFF && *Extension == 11) {
785 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
786 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
788 i_push_error(0, "reading loop extension");
789 free_images(results, *count);
790 DGifCloseFile(GifFile);
793 if (Extension && *Extension == 3) {
795 ns_loop = Extension[2] + 256 * Extension[3];
799 else if (ExtCode == 0xFE) {
800 /* while it's possible for a GIF file to contain more than one
801 comment, I'm only implementing a single comment per image,
802 with the comment saved into the following image.
803 If someone wants more than that they can implement it.
804 I also don't handle comments that take more than one block.
807 comment = mymalloc(*Extension+1);
808 memcpy(comment, Extension+1, *Extension);
809 comment[*Extension] = '\0';
812 while (Extension != NULL) {
813 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
815 i_push_error(0, "reading next block of extension");
816 free_images(results, *count);
817 DGifCloseFile(GifFile);
822 case TERMINATE_RECORD_TYPE:
824 default: /* Should be trapped by DGifGetRecordType. */
827 } while (RecordType != TERMINATE_RECORD_TYPE);
831 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
839 if (DGifCloseFile(GifFile) == GIF_ERROR) {
841 i_push_error(0, "Closing GIF file object");
842 free_images(results, *count);
846 if (ImageNum && page != -1) {
847 /* there were images, but the page selected wasn't found */
848 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
849 free_images(results, *count);
857 /* giflib declares this incorrectly as EgifOpen */
858 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
860 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
864 =item i_readgif_multi_wiol(ig, int *count)
870 i_readgif_multi_wiol(io_glue *ig, int *count) {
871 io_glue_commit_types(ig);
873 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
874 return i_readgif_multi(ig->source.fdseek.fd, count);
878 GifFileType *GifFile;
882 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
884 i_push_error(0, "Cannot create giflib callback object");
885 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
889 return i_readgif_multi_low(GifFile, count, -1);
892 i_push_error(0, "callbacks not supported with giflib3");
900 =item i_readgif_multi(int fd, int *count)
905 i_readgif_multi(int fd, int *count) {
906 GifFileType *GifFile;
910 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
912 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
914 i_push_error(0, "Cannot create giflib file object");
915 mm_log((1,"i_readgif: Unable to open file\n"));
919 return i_readgif_multi_low(GifFile, count, -1);
923 =item i_readgif_multi_scalar(char *data, int length, int *count)
928 i_readgif_multi_scalar(char *data, int length, int *count) {
930 GifFileType *GifFile;
931 struct gif_scalar_info gsi;
939 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
940 data, length, count));
942 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
944 i_push_error(0, "Cannot create giflib callback object");
945 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
949 return i_readgif_multi_low(GifFile, count, -1);
956 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
958 Read a GIF file into an Imager RGB file, the data of the GIF file is
959 retreived by callin the user supplied callback function.
961 This function is only used with giflib 4 and higher.
967 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
969 GifFileType *GifFile;
972 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
976 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
977 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
979 i_push_error(0, "Cannot create giflib callback object");
980 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
985 result = i_readgif_multi_low(GifFile, count, -1);
986 i_free_gen_read_data(gci);
995 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
997 Write I<img> to the file handle I<fd>. The resulting GIF will use a
998 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
999 colours taken from I<fixed>.
1001 Returns non-zero on success.
1007 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
1008 i_color colors[256];
1011 memset(&quant, 0, sizeof(quant));
1012 quant.make_colors = mc_addi;
1013 quant.mc_colors = colors;
1014 quant.mc_size = 1<<max_colors;
1015 quant.mc_count = fixedlen;
1016 memcpy(colors, fixed, fixedlen * sizeof(i_color));
1017 quant.translate = pt_perturb;
1018 quant.perturb = pixdev;
1019 return i_writegif_gen(&quant, fd, &im, 1);
1023 =item i_writegifmc(i_img *im, int fd, int max_colors)
1025 Write I<img> to the file handle I<fd>. The resulting GIF will use a
1026 maximum of 1<<I<max_colours> colours.
1028 Returns non-zero on success.
1034 i_writegifmc(i_img *im, int fd, int max_colors) {
1035 i_color colors[256];
1038 /* *(char *)0 = 1; */
1040 memset(&quant, 0, sizeof(quant));
1041 quant.make_colors = mc_none; /* ignored for pt_giflib */
1042 quant.mc_colors = colors;
1043 quant.mc_size = 1 << max_colors;
1045 quant.translate = pt_giflib;
1046 return i_writegif_gen(&quant, fd, &im, 1);
1051 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1053 Reads a GIF file from an in memory copy of the file. This can be used
1054 if you get the 'file' from some source other than an actual file (or
1055 some other file handle).
1057 This function is only available with giflib 4 and higher.
1062 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1063 #if IM_GIFMAJOR >= 4
1064 GifFileType *GifFile;
1065 struct gif_scalar_info gsi;
1073 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1074 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1076 i_push_error(0, "Cannot create giflib callback object");
1077 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1081 return i_readgif_low(GifFile, colour_table, colours);
1087 #if IM_GIFMAJOR >= 4
1090 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1092 Internal. The reader callback wrapper passed to giflib.
1094 This function is only used with giflib 4 and higher.
1100 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1101 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1108 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1110 Read a GIF file into an Imager RGB file, the data of the GIF file is
1111 retreived by callin the user supplied callback function.
1113 This function is only used with giflib 4 and higher.
1119 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1120 #if IM_GIFMAJOR >= 4
1121 GifFileType *GifFile;
1124 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1128 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1129 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1131 i_push_error(0, "Cannot create giflib callback object");
1132 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1137 result = i_readgif_low(GifFile, colour_table, colours);
1138 i_free_gen_read_data(gci);
1143 i_push_error(0, "callbacks not supported with giflib3");
1149 #if IM_GIFMAJOR >= 4
1152 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1153 io_glue *ig = (io_glue *)gft->UserData;
1155 return ig->readcb(ig, buf, length);
1161 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1162 io_glue_commit_types(ig);
1164 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1165 int fd = dup(ig->source.fdseek.fd);
1167 i_push_error(errno, "dup() failed");
1170 return i_readgif(fd, color_table, colors);
1173 #if IM_GIFMAJOR >= 4
1174 GifFileType *GifFile;
1178 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1180 i_push_error(0, "Cannot create giflib callback object");
1181 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1185 return i_readgif_low(GifFile, color_table, colors);
1189 i_push_error(0, "callbacks not supported with giflib3");
1197 =item i_readgif_single_low(GifFile, page)
1199 Lower level function to read a single image from a GIF.
1201 page must be non-negative.
1206 i_readgif_single_low(GifFileType *GifFile, int page) {
1210 imgs = i_readgif_multi_low(GifFile, &count, page);
1212 if (imgs && count) {
1213 i_img *result = imgs[0];
1219 /* i_readgif_multi_low() handles the errors appropriately */
1225 =item i_readgif_single_wiol(ig, page)
1227 Read a single page from a GIF image file, where the page is indexed
1230 Returns NULL if the page isn't found.
1236 i_readgif_single_wiol(io_glue *ig, int page) {
1237 io_glue_commit_types(ig);
1242 i_push_error(0, "page must be non-negative");
1246 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1247 GifFileType *GifFile;
1248 int fd = dup(ig->source.fdseek.fd);
1250 i_push_error(errno, "dup() failed");
1253 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1255 i_push_error(0, "Cannot create giflib file object");
1256 mm_log((1,"i_readgif: Unable to open file\n"));
1259 return i_readgif_single_low(GifFile, page);
1262 #if IM_GIFMAJOR >= 4
1263 GifFileType *GifFile;
1265 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1267 i_push_error(0, "Cannot create giflib callback object");
1268 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1272 return i_readgif_single_low(GifFile, page);
1274 i_push_error(0, "callbacks not supported with giflib3");
1282 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1284 Internal. Low level image write function. Writes in interlace if
1285 that was requested in the GIF options.
1287 Returns non-zero on success.
1292 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1295 for (i = 0; i < 4; ++i) {
1296 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1297 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1299 i_push_error(0, "Could not save image data:");
1300 mm_log((1, "Error in EGifPutLine\n"));
1309 for (y = 0; y < img->ysize; ++y) {
1310 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1312 i_push_error(0, "Could not save image data:");
1313 mm_log((1, "Error in EGifPutLine\n"));
1325 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1327 Internal. Writes the GIF graphics control extension, if necessary.
1329 Returns non-zero on success.
1333 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1335 unsigned char gce[4] = {0};
1339 int disposal_method;
1343 gce[3] = trans_index;
1346 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1347 gce[1] = delay % 256;
1348 gce[2] = delay / 256;
1351 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1356 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1357 gce[0] |= (disposal_method & 3) << 2;
1361 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1363 i_push_error(0, "Could not save GCE");
1370 =item do_comments(gf, img)
1372 Write any comments in the image.
1376 static int do_comments(GifFileType *gf, i_img *img) {
1379 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1380 if (img->tags.tags[pos].data) {
1381 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1387 sprintf(buf, "%d", img->tags.tags[pos].idata);
1388 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1398 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1400 Internal. Add the Netscape2.0 loop extension block, if requested.
1402 Giflib/libungif prior to 4.1.1 didn't support writing application
1403 extension blocks, so we don't attempt to write them for older versions.
1405 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1406 writing extension blocks so that they could only be written to files.
1410 static int do_ns_loop(GifFileType *gf, i_img *img)
1412 /* EGifPutExtension() doesn't appear to handle application
1413 extension blocks in any way
1414 Since giflib wraps the fd with a FILE * (and puts that in its
1415 private data), we can't do an end-run and write the data
1417 There's no open interface that takes a FILE * either, so we
1418 can't workaround it that way either.
1419 If giflib's callback interface wasn't broken by default, I'd
1420 force file writes to use callbacks, but it is broken by default.
1422 /* yes this was another attempt at supporting the loop extension */
1423 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1425 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1426 unsigned char nsle[12] = "NETSCAPE2.0";
1427 unsigned char subblock[3];
1428 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1430 i_push_error(0, "writing loop extension");
1434 subblock[1] = loop_count % 256;
1435 subblock[2] = loop_count / 256;
1436 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1438 i_push_error(0, "writing loop extention sub-block");
1448 =item make_gif_map(i_quantize *quant, int want_trans)
1450 Create a giflib color map object from an Imager color map.
1455 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1457 GifColorType colors[256];
1459 int size = quant->mc_count;
1461 ColorMapObject *map;
1464 for (i = 0; i < quant->mc_count; ++i) {
1465 colors[i].Red = quant->mc_colors[i].rgb.r;
1466 colors[i].Green = quant->mc_colors[i].rgb.g;
1467 colors[i].Blue = quant->mc_colors[i].rgb.b;
1470 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1471 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1472 colors[size].Red = trans.rgb.r;
1473 colors[size].Green = trans.rgb.g;
1474 colors[size].Blue = trans.rgb.b;
1478 while (map_size < size)
1480 /* giflib spews for 1 colour maps, reasonable, I suppose */
1483 while (i < map_size) {
1484 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1488 map = MakeMapObject(map_size, colors);
1489 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1492 i_push_error(0, "Could not create color map object");
1499 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1501 We need to call EGifSetGifVersion() before opening the file - put that
1504 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1505 giflib 4.1.0 has code:
1507 static char *GifVersionPrefix = GIF87_STAMP;
1509 and the code that sets the version internally does:
1511 strncpy(&GifVersionPrefix[3], Version, 3);
1513 which is very broken.
1515 Failing to set the correct GIF version doesn't seem to cause a problem
1518 Modern versions (4.1.4 anyway) of giflib/libungif handle
1519 EGifSetGifVersion correctly.
1521 If t/t105gif.t crashes here then run Makefile.PL with
1522 --nogifsetversion, eg.:
1524 perl Makefile.PL --nogifsetversion
1526 or install a less buggy giflib.
1531 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1532 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1533 && !defined(IM_NO_SET_GIF_VERSION)
1538 if (quant->transp != tr_none)
1541 for (i = 0; i < count; ++i) {
1542 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1546 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1550 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1554 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1561 EGifSetGifVersion("89a");
1563 EGifSetGifVersion("87a");
1568 in_palette(i_color *c, i_quantize *quant, int size) {
1571 for (i = 0; i < size; ++i) {
1572 if (c->channel[0] == quant->mc_colors[i].channel[0]
1573 && c->channel[1] == quant->mc_colors[i].channel[1]
1574 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1583 =item has_common_palette(imgs, count, quant, want_trans)
1585 Tests if all the given images are paletted and have a common palette,
1586 if they do it builds that palette.
1588 A possible improvement might be to eliminate unused colors in the
1594 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1596 int size = quant->mc_count;
1601 /* we try to build a common palette here, if we can manage that, then
1602 that's the palette we use */
1603 for (imgn = 0; imgn < count; ++imgn) {
1604 int eliminate_unused;
1605 if (imgs[imgn]->type != i_palette_type)
1608 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1609 &eliminate_unused)) {
1610 eliminate_unused = 1;
1613 if (eliminate_unused) {
1614 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1616 memset(used, 0, sizeof(used));
1618 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1619 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1620 for (x = 0; x < imgs[imgn]->xsize; ++x)
1627 /* assume all are in use */
1628 memset(used, 1, sizeof(used));
1631 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1634 i_getcolors(imgs[imgn], i, &c, 1);
1636 if (in_palette(&c, quant, size) < 0) {
1637 if (size < quant->mc_size) {
1638 quant->mc_colors[size++] = c;
1641 /* oops, too many colors */
1649 quant->mc_count = size;
1655 quant_paletted(i_quantize *quant, i_img *img) {
1656 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1658 i_palidx trans[256];
1662 /* build a translation table */
1663 for (i = 0; i < i_colorcount(img); ++i) {
1665 i_getcolors(img, i, &c, 1);
1666 trans[i] = in_palette(&c, quant, quant->mc_count);
1669 for (y = 0; y < img->ysize; ++y) {
1670 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1671 for (x = 0; x < img->xsize; ++x) {
1681 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1683 Internal. Low-level function that does the high-level GIF processing
1686 Returns non-zero on success.
1692 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1693 unsigned char *result = NULL;
1695 ColorMapObject *map;
1696 int scrw = 0, scrh = 0;
1697 int imgn, orig_count, orig_size;
1699 int trans_index = -1;
1703 i_img **glob_imgs; /* images that will use the global color map */
1705 i_color *orig_colors = quant->mc_colors;
1706 i_color *glob_colors = NULL;
1707 int glob_color_count = 0;
1708 int glob_want_trans;
1709 int glob_paletted = 0; /* the global map was made from the image palettes */
1710 int colors_paletted = 0;
1715 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1716 quant, gf, imgs, count));
1718 /* *((char *)0) = 1; */ /* used to break into the debugger */
1721 i_push_error(0, "No images provided to write");
1722 return 0; /* what are you smoking? */
1725 i_mempool_init(&mp);
1727 /* sanity is nice */
1728 if (quant->mc_size > 256)
1729 quant->mc_size = 256;
1730 if (quant->mc_count > quant->mc_size)
1731 quant->mc_count = quant->mc_size;
1733 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1735 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1739 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1740 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1742 glob_want_trans = 0;
1743 for (imgn = 0; imgn < count; ++imgn) {
1745 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1746 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1747 if (imgs[imgn]->xsize + posx > scrw)
1748 scrw = imgs[imgn]->xsize + posx;
1749 if (imgs[imgn]->ysize + posy > scrh)
1750 scrh = imgs[imgn]->ysize + posy;
1751 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1752 localmaps[imgn] = 0;
1753 if (localmaps[imgn])
1756 if (imgs[imgn]->channels == 4) {
1757 glob_want_trans = 1;
1759 glob_imgs[glob_img_count++] = imgs[imgn];
1762 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1764 orig_count = quant->mc_count;
1765 orig_size = quant->mc_size;
1767 if (glob_img_count) {
1769 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1770 quant->mc_colors = glob_colors;
1771 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1772 /* we have some images that want to use the global map */
1773 if (glob_want_trans && quant->mc_count == 256) {
1774 mm_log((2, " disabling transparency for global map - no space\n"));
1775 glob_want_trans = 0;
1777 if (glob_want_trans && quant->mc_size == 256) {
1778 mm_log((2, " reserving color for transparency\n"));
1781 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1786 i_quant_makemap(quant, glob_imgs, glob_img_count);
1788 glob_color_count = quant->mc_count;
1789 quant->mc_colors = orig_colors;
1792 /* use the global map if we have one, otherwise use the local map */
1795 quant->mc_colors = glob_colors;
1796 quant->mc_count = glob_color_count;
1797 want_trans = glob_want_trans && imgs[0]->channels == 4;
1799 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1801 if (gif_background < 0)
1803 if (gif_background >= glob_color_count)
1807 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1808 if (has_common_palette(imgs, 1, quant, want_trans)) {
1809 colors_paletted = 1;
1812 colors_paletted = 0;
1813 i_quant_makemap(quant, imgs, 1);
1816 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1817 i_mempool_destroy(&mp);
1818 quant->mc_colors = orig_colors;
1820 mm_log((1, "Error in MakeMapObject"));
1825 /* since we don't know how big some the local palettes could be
1826 we need to base the bits on the maximum number of colors */
1827 while (orig_size > (1 << color_bits))
1831 int count = quant->mc_count;
1834 while (count > (1 << color_bits))
1838 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1839 gif_background, map) == GIF_ERROR) {
1840 i_mempool_destroy(&mp);
1841 quant->mc_colors = orig_colors;
1843 i_push_error(0, "Could not save screen descriptor");
1847 mm_log((1, "Error in EGifPutScreenDesc."));
1852 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1854 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1857 if (!localmaps[0]) {
1859 colors_paletted = glob_paletted;
1862 /* if this image has a global map the colors in quant don't
1863 belong to this image, so build a palette */
1865 /* generate the local map for this image */
1866 quant->mc_colors = orig_colors;
1867 quant->mc_size = orig_size;
1868 quant->mc_count = orig_count;
1869 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1871 /* if the caller gives us too many colours we can't do transparency */
1872 if (want_trans && quant->mc_count == 256)
1874 /* if they want transparency but give us a big size, make it smaller
1875 to give room for a transparency colour */
1876 if (want_trans && quant->mc_size == 256)
1878 if (has_common_palette(imgs, 1, quant, want_trans)) {
1879 colors_paletted = 1;
1882 colors_paletted = 0;
1883 i_quant_makemap(quant, imgs, 1);
1885 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1886 i_mempool_destroy(&mp);
1888 quant->mc_colors = orig_colors;
1889 mm_log((1, "Error in MakeMapObject"));
1894 /* the map we wrote was the map for this image - don't set the local
1900 if (colors_paletted)
1901 result = quant_paletted(quant, imgs[0]);
1903 result = i_quant_translate(quant, imgs[0]);
1905 i_mempool_destroy(&mp);
1906 quant->mc_colors = orig_colors;
1911 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1912 trans_index = quant->mc_count;
1915 if (!do_ns_loop(gf, imgs[0])) {
1916 i_mempool_destroy(&mp);
1917 quant->mc_colors = orig_colors;
1921 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1922 i_mempool_destroy(&mp);
1923 quant->mc_colors = orig_colors;
1929 if (!do_comments(gf, imgs[0])) {
1930 i_mempool_destroy(&mp);
1931 quant->mc_colors = orig_colors;
1937 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1939 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1940 interlace, map) == GIF_ERROR) {
1941 i_mempool_destroy(&mp);
1942 quant->mc_colors = orig_colors;
1944 i_push_error(0, "Could not save image descriptor");
1946 mm_log((1, "Error in EGifPutImageDesc."));
1952 if (!do_write(gf, interlace, imgs[0], result)) {
1953 i_mempool_destroy(&mp);
1954 quant->mc_colors = orig_colors;
1961 /* that first awful image is out of the way, do the rest */
1962 for (imgn = 1; imgn < count; ++imgn) {
1963 if (localmaps[imgn]) {
1964 quant->mc_colors = orig_colors;
1965 quant->mc_count = orig_count;
1966 quant->mc_size = orig_size;
1968 want_trans = quant->transp != tr_none
1969 && imgs[imgn]->channels == 4;
1970 /* if the caller gives us too many colours we can't do transparency */
1971 if (want_trans && quant->mc_count == 256)
1973 /* if they want transparency but give us a big size, make it smaller
1974 to give room for a transparency colour */
1975 if (want_trans && quant->mc_size == 256)
1978 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1979 result = quant_paletted(quant, imgs[imgn]);
1982 i_quant_makemap(quant, imgs+imgn, 1);
1983 result = i_quant_translate(quant, imgs[imgn]);
1986 i_mempool_destroy(&mp);
1987 quant->mc_colors = orig_colors;
1989 mm_log((1, "error in i_quant_translate()"));
1993 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1994 trans_index = quant->mc_count;
1997 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1998 i_mempool_destroy(&mp);
1999 quant->mc_colors = orig_colors;
2002 mm_log((1, "Error in MakeMapObject."));
2007 quant->mc_colors = glob_colors;
2008 quant->mc_count = glob_color_count;
2010 result = quant_paletted(quant, imgs[imgn]);
2012 result = i_quant_translate(quant, imgs[imgn]);
2013 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
2015 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2016 trans_index = quant->mc_count;
2021 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
2022 i_mempool_destroy(&mp);
2023 quant->mc_colors = orig_colors;
2029 if (!do_comments(gf, imgs[imgn])) {
2030 i_mempool_destroy(&mp);
2031 quant->mc_colors = orig_colors;
2037 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2039 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2042 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2044 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
2045 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2046 i_mempool_destroy(&mp);
2047 quant->mc_colors = orig_colors;
2049 i_push_error(0, "Could not save image descriptor");
2054 mm_log((1, "Error in EGifPutImageDesc."));
2060 if (!do_write(gf, interlace, imgs[imgn], result)) {
2061 i_mempool_destroy(&mp);
2062 quant->mc_colors = orig_colors;
2070 if (EGifCloseFile(gf) == GIF_ERROR) {
2071 i_mempool_destroy(&mp);
2073 i_push_error(0, "Could not close GIF file");
2074 mm_log((1, "Error in EGifCloseFile\n"));
2079 for (i = 0; i < glob_color_count; ++i)
2080 orig_colors[i] = glob_colors[i];
2083 i_mempool_destroy(&mp);
2084 quant->mc_colors = orig_colors;
2090 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2092 General high-level function to write a GIF to a file.
2094 Writes the GIF images to the specified file handle using the options
2095 in quant and opts. See L<image.h/i_quantize> and
2096 L<image.h/i_gif_opts>.
2098 Returns non-zero on success.
2104 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2108 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2109 quant, fd, imgs, count));
2111 gif_set_version(quant, imgs, count);
2113 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2115 i_push_error(0, "Cannot create GIF file object");
2116 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2120 return i_writegif_low(quant, gf, imgs, count);
2123 #if IM_GIFMAJOR >= 4
2126 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2128 Internal. Wrapper for the user write callback function.
2133 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2135 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2137 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2143 =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)
2145 General high-level function to write a GIF using callbacks to send
2148 Returns non-zero on success.
2154 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2155 int maxlength, i_img **imgs, int count)
2157 #if IM_GIFMAJOR >= 4
2159 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2164 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2165 quant, cb, userdata, maxlength, imgs, count));
2167 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2169 i_push_error(0, "Cannot create GIF file object");
2170 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2171 i_free_gen_write_data(gwd, 0);
2175 result = i_writegif_low(quant, gf, imgs, count);
2176 return i_free_gen_write_data(gwd, result);
2179 i_push_error(0, "callbacks not supported with giflib3");
2185 #if IM_GIFMAJOR >= 4
2188 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2189 io_glue *ig = (io_glue *)gft->UserData;
2191 return ig->writecb(ig, data, length);
2197 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2202 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2204 io_glue_commit_types(ig);
2206 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2207 int fd = dup(ig->source.fdseek.fd);
2209 i_push_error(errno, "dup() failed");
2212 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2213 is called - dup it so the caller's fd isn't closed */
2214 return i_writegif_gen(quant, fd, imgs, count);
2217 #if IM_GIFMAJOR >= 4
2218 GifFileType *GifFile;
2223 gif_set_version(quant, imgs, count);
2225 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2227 i_push_error(0, "Cannot create giflib callback object");
2228 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2232 result = i_writegif_low(quant, GifFile, imgs, count);
2239 i_push_error(0, "callbacks not supported with giflib3");
2247 =item gif_error_msg(int code)
2249 Grabs the most recent giflib error code from GifLastError() and
2250 returns a string that describes that error.
2252 The returned pointer points to a static buffer, either from a literal
2253 C string or a static buffer.
2258 static char const *gif_error_msg(int code) {
2259 static char msg[80];
2262 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2263 return "Failed to open given file";
2265 case E_GIF_ERR_WRITE_FAILED:
2266 return "Write failed";
2268 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2269 return "Screen descriptor already passed to giflib";
2271 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2272 return "Image descriptor already passed to giflib";
2274 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2275 return "Neither global nor local color map set";
2277 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2278 return "Too much pixel data passed to giflib";
2280 case E_GIF_ERR_NOT_ENOUGH_MEM:
2281 return "Out of memory";
2283 case E_GIF_ERR_DISK_IS_FULL:
2284 return "Disk is full";
2286 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2287 return "File close failed";
2289 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2290 return "File not writable";
2292 case D_GIF_ERR_OPEN_FAILED:
2293 return "Failed to open file";
2295 case D_GIF_ERR_READ_FAILED:
2296 return "Failed to read from file";
2298 case D_GIF_ERR_NOT_GIF_FILE:
2299 return "File is not a GIF file";
2301 case D_GIF_ERR_NO_SCRN_DSCR:
2302 return "No screen descriptor detected - invalid file";
2304 case D_GIF_ERR_NO_IMAG_DSCR:
2305 return "No image descriptor detected - invalid file";
2307 case D_GIF_ERR_NO_COLOR_MAP:
2308 return "No global or local color map found";
2310 case D_GIF_ERR_WRONG_RECORD:
2311 return "Wrong record type detected - invalid file?";
2313 case D_GIF_ERR_DATA_TOO_BIG:
2314 return "Data in file too big for image";
2316 case D_GIF_ERR_NOT_ENOUGH_MEM:
2317 return "Out of memory";
2319 case D_GIF_ERR_CLOSE_FAILED:
2320 return "Close failed";
2322 case D_GIF_ERR_NOT_READABLE:
2323 return "File not opened for read";
2325 case D_GIF_ERR_IMAGE_DEFECT:
2326 return "Defective image";
2328 case D_GIF_ERR_EOF_TOO_SOON:
2329 return "Unexpected EOF - invalid file";
2332 sprintf(msg, "Unknown giflib error code %d", code);
2338 =item gif_push_error()
2340 Utility function that takes the current GIF error code, converts it to
2341 an error message and pushes it on the error stack.
2346 static void gif_push_error(void) {
2347 int code = GifLastError(); /* clears saved error */
2349 i_push_error(code, gif_error_msg(code));
2355 The Netscape loop extension isn't implemented. Giflib's extension
2356 writing code doesn't seem to support writing named extensions in this
2359 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2360 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2363 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2364 supplied interface in giflib 4.1.0 causes a SEGV in
2365 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2369 Arnar M. Hrafnkelsson, addi@umich.edu