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 i_tags_addn(&img->tags, "gif_colormap_size", 0, ColorMapSize);
640 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
641 i_tags_addn(&img->tags, "gif_background", 0,
642 GifFile->SBackGroundColor);
644 if (GifFile->Image.ColorMap) {
645 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
648 if (trans_index >= 0) {
650 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
651 i_getcolors(img, trans_index, &trans, 1);
652 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
654 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
655 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
656 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
660 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
662 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
667 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
668 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
670 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
671 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
672 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
673 free_images(results, *count);
674 DGifCloseFile(GifFile);
679 if (GifFile->Image.Interlace) {
680 for (Count = i = 0; i < 4; i++) {
681 for (j = InterlacedOffset[i]; j < Height;
682 j += InterlacedJumps[i]) {
684 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
686 i_push_error(0, "Reading GIF line");
687 free_images(results, *count);
688 DGifCloseFile(GifFile);
693 /* range check the scanline if needed */
694 if (image_colors != 256) {
696 for (x = 0; x < Width; ++x) {
697 while (GifRow[x] >= image_colors) {
698 /* expand the palette since a palette index is too big */
699 i_addcolors(img, &black, 1);
705 i_ppal(img, 0, Width, j, GifRow);
710 for (i = 0; i < Height; i++) {
711 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
713 i_push_error(0, "Reading GIF line");
714 free_images(results, *count);
715 DGifCloseFile(GifFile);
720 /* range check the scanline if needed */
721 if (image_colors != 256) {
723 for (x = 0; x < Width; ++x) {
724 while (GifRow[x] >= image_colors) {
725 /* expand the palette since a palette index is too big */
726 i_addcolors(img, &black, 1);
732 i_ppal(img, 0, Width, i, GifRow);
736 /* must be only one image wanted and that was it */
739 DGifCloseFile(GifFile);
745 /* whether interlaced or not, it has the same number of lines */
746 /* giflib does't have an interface to skip the image data */
747 for (i = 0; i < Height; i++) {
748 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
750 i_push_error(0, "Reading GIF line");
751 free_images(results, *count);
753 DGifCloseFile(GifFile);
758 /* kill the comment so we get the right comment for the page */
766 case EXTENSION_RECORD_TYPE:
767 /* Skip any extension blocks in file: */
768 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
770 i_push_error(0, "Reading extension record");
771 free_images(results, *count);
772 DGifCloseFile(GifFile);
775 if (ExtCode == 0xF9) {
777 if (Extension[1] & 1)
778 trans_index = Extension[4];
781 gif_delay = Extension[2] + 256 * Extension[3];
782 user_input = (Extension[1] & 2) != 0;
783 disposal = (Extension[1] >> 2) & 7;
785 if (ExtCode == 0xFF && *Extension == 11) {
786 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
787 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
789 i_push_error(0, "reading loop extension");
790 free_images(results, *count);
791 DGifCloseFile(GifFile);
794 if (Extension && *Extension == 3) {
796 ns_loop = Extension[2] + 256 * Extension[3];
800 else if (ExtCode == 0xFE) {
801 /* while it's possible for a GIF file to contain more than one
802 comment, I'm only implementing a single comment per image,
803 with the comment saved into the following image.
804 If someone wants more than that they can implement it.
805 I also don't handle comments that take more than one block.
808 comment = mymalloc(*Extension+1);
809 memcpy(comment, Extension+1, *Extension);
810 comment[*Extension] = '\0';
813 while (Extension != NULL) {
814 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
816 i_push_error(0, "reading next block of extension");
817 free_images(results, *count);
818 DGifCloseFile(GifFile);
823 case TERMINATE_RECORD_TYPE:
825 default: /* Should be trapped by DGifGetRecordType. */
828 } while (RecordType != TERMINATE_RECORD_TYPE);
832 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
840 if (DGifCloseFile(GifFile) == GIF_ERROR) {
842 i_push_error(0, "Closing GIF file object");
843 free_images(results, *count);
847 if (ImageNum && page != -1) {
848 /* there were images, but the page selected wasn't found */
849 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
850 free_images(results, *count);
858 /* giflib declares this incorrectly as EgifOpen */
859 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
861 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
865 =item i_readgif_multi_wiol(ig, int *count)
871 i_readgif_multi_wiol(io_glue *ig, int *count) {
872 io_glue_commit_types(ig);
874 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
875 return i_readgif_multi(ig->source.fdseek.fd, count);
879 GifFileType *GifFile;
883 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
885 i_push_error(0, "Cannot create giflib callback object");
886 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
890 return i_readgif_multi_low(GifFile, count, -1);
893 i_push_error(0, "callbacks not supported with giflib3");
901 =item i_readgif_multi(int fd, int *count)
906 i_readgif_multi(int fd, int *count) {
907 GifFileType *GifFile;
911 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
913 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
915 i_push_error(0, "Cannot create giflib file object");
916 mm_log((1,"i_readgif: Unable to open file\n"));
920 return i_readgif_multi_low(GifFile, count, -1);
924 =item i_readgif_multi_scalar(char *data, int length, int *count)
929 i_readgif_multi_scalar(char *data, int length, int *count) {
931 GifFileType *GifFile;
932 struct gif_scalar_info gsi;
940 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
941 data, length, count));
943 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
945 i_push_error(0, "Cannot create giflib callback object");
946 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
950 return i_readgif_multi_low(GifFile, count, -1);
957 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
959 Read a GIF file into an Imager RGB file, the data of the GIF file is
960 retreived by callin the user supplied callback function.
962 This function is only used with giflib 4 and higher.
968 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
970 GifFileType *GifFile;
973 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
977 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
978 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
980 i_push_error(0, "Cannot create giflib callback object");
981 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
986 result = i_readgif_multi_low(GifFile, count, -1);
987 i_free_gen_read_data(gci);
996 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
998 Write I<img> to the file handle I<fd>. The resulting GIF will use a
999 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
1000 colours taken from I<fixed>.
1002 Returns non-zero on success.
1008 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
1009 i_color colors[256];
1012 memset(&quant, 0, sizeof(quant));
1013 quant.make_colors = mc_addi;
1014 quant.mc_colors = colors;
1015 quant.mc_size = 1<<max_colors;
1016 quant.mc_count = fixedlen;
1017 memcpy(colors, fixed, fixedlen * sizeof(i_color));
1018 quant.translate = pt_perturb;
1019 quant.perturb = pixdev;
1020 return i_writegif_gen(&quant, fd, &im, 1);
1024 =item i_writegifmc(i_img *im, int fd, int max_colors)
1026 Write I<img> to the file handle I<fd>. The resulting GIF will use a
1027 maximum of 1<<I<max_colours> colours.
1029 Returns non-zero on success.
1035 i_writegifmc(i_img *im, int fd, int max_colors) {
1036 i_color colors[256];
1039 /* *(char *)0 = 1; */
1041 memset(&quant, 0, sizeof(quant));
1042 quant.make_colors = mc_none; /* ignored for pt_giflib */
1043 quant.mc_colors = colors;
1044 quant.mc_size = 1 << max_colors;
1046 quant.translate = pt_giflib;
1047 return i_writegif_gen(&quant, fd, &im, 1);
1052 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1054 Reads a GIF file from an in memory copy of the file. This can be used
1055 if you get the 'file' from some source other than an actual file (or
1056 some other file handle).
1058 This function is only available with giflib 4 and higher.
1063 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1064 #if IM_GIFMAJOR >= 4
1065 GifFileType *GifFile;
1066 struct gif_scalar_info gsi;
1074 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1075 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1077 i_push_error(0, "Cannot create giflib callback object");
1078 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1082 return i_readgif_low(GifFile, colour_table, colours);
1088 #if IM_GIFMAJOR >= 4
1091 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1093 Internal. The reader callback wrapper passed to giflib.
1095 This function is only used with giflib 4 and higher.
1101 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1102 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1109 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1111 Read a GIF file into an Imager RGB file, the data of the GIF file is
1112 retreived by callin the user supplied callback function.
1114 This function is only used with giflib 4 and higher.
1120 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1121 #if IM_GIFMAJOR >= 4
1122 GifFileType *GifFile;
1125 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1129 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1130 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1132 i_push_error(0, "Cannot create giflib callback object");
1133 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1138 result = i_readgif_low(GifFile, colour_table, colours);
1139 i_free_gen_read_data(gci);
1144 i_push_error(0, "callbacks not supported with giflib3");
1150 #if IM_GIFMAJOR >= 4
1153 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1154 io_glue *ig = (io_glue *)gft->UserData;
1156 return ig->readcb(ig, buf, length);
1162 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1163 io_glue_commit_types(ig);
1165 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1166 int fd = dup(ig->source.fdseek.fd);
1168 i_push_error(errno, "dup() failed");
1171 return i_readgif(fd, color_table, colors);
1174 #if IM_GIFMAJOR >= 4
1175 GifFileType *GifFile;
1179 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1181 i_push_error(0, "Cannot create giflib callback object");
1182 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1186 return i_readgif_low(GifFile, color_table, colors);
1190 i_push_error(0, "callbacks not supported with giflib3");
1198 =item i_readgif_single_low(GifFile, page)
1200 Lower level function to read a single image from a GIF.
1202 page must be non-negative.
1207 i_readgif_single_low(GifFileType *GifFile, int page) {
1211 imgs = i_readgif_multi_low(GifFile, &count, page);
1213 if (imgs && count) {
1214 i_img *result = imgs[0];
1220 /* i_readgif_multi_low() handles the errors appropriately */
1226 =item i_readgif_single_wiol(ig, page)
1228 Read a single page from a GIF image file, where the page is indexed
1231 Returns NULL if the page isn't found.
1237 i_readgif_single_wiol(io_glue *ig, int page) {
1238 io_glue_commit_types(ig);
1243 i_push_error(0, "page must be non-negative");
1247 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1248 GifFileType *GifFile;
1249 int fd = dup(ig->source.fdseek.fd);
1251 i_push_error(errno, "dup() failed");
1254 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1256 i_push_error(0, "Cannot create giflib file object");
1257 mm_log((1,"i_readgif: Unable to open file\n"));
1260 return i_readgif_single_low(GifFile, page);
1263 #if IM_GIFMAJOR >= 4
1264 GifFileType *GifFile;
1266 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1268 i_push_error(0, "Cannot create giflib callback object");
1269 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1273 return i_readgif_single_low(GifFile, page);
1275 i_push_error(0, "callbacks not supported with giflib3");
1283 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1285 Internal. Low level image write function. Writes in interlace if
1286 that was requested in the GIF options.
1288 Returns non-zero on success.
1293 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1296 for (i = 0; i < 4; ++i) {
1297 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1298 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1300 i_push_error(0, "Could not save image data:");
1301 mm_log((1, "Error in EGifPutLine\n"));
1310 for (y = 0; y < img->ysize; ++y) {
1311 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1313 i_push_error(0, "Could not save image data:");
1314 mm_log((1, "Error in EGifPutLine\n"));
1326 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1328 Internal. Writes the GIF graphics control extension, if necessary.
1330 Returns non-zero on success.
1334 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1336 unsigned char gce[4] = {0};
1340 int disposal_method;
1344 gce[3] = trans_index;
1347 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1348 gce[1] = delay % 256;
1349 gce[2] = delay / 256;
1352 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1357 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1358 gce[0] |= (disposal_method & 3) << 2;
1362 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1364 i_push_error(0, "Could not save GCE");
1371 =item do_comments(gf, img)
1373 Write any comments in the image.
1377 static int do_comments(GifFileType *gf, i_img *img) {
1380 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1381 if (img->tags.tags[pos].data) {
1382 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1388 sprintf(buf, "%d", img->tags.tags[pos].idata);
1389 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1399 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1401 Internal. Add the Netscape2.0 loop extension block, if requested.
1403 Giflib/libungif prior to 4.1.1 didn't support writing application
1404 extension blocks, so we don't attempt to write them for older versions.
1406 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1407 writing extension blocks so that they could only be written to files.
1411 static int do_ns_loop(GifFileType *gf, i_img *img)
1413 /* EGifPutExtension() doesn't appear to handle application
1414 extension blocks in any way
1415 Since giflib wraps the fd with a FILE * (and puts that in its
1416 private data), we can't do an end-run and write the data
1418 There's no open interface that takes a FILE * either, so we
1419 can't workaround it that way either.
1420 If giflib's callback interface wasn't broken by default, I'd
1421 force file writes to use callbacks, but it is broken by default.
1423 /* yes this was another attempt at supporting the loop extension */
1424 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1426 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1427 unsigned char nsle[12] = "NETSCAPE2.0";
1428 unsigned char subblock[3];
1429 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1431 i_push_error(0, "writing loop extension");
1435 subblock[1] = loop_count % 256;
1436 subblock[2] = loop_count / 256;
1437 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1439 i_push_error(0, "writing loop extention sub-block");
1449 =item make_gif_map(i_quantize *quant, int want_trans)
1451 Create a giflib color map object from an Imager color map.
1456 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1458 GifColorType colors[256];
1460 int size = quant->mc_count;
1462 ColorMapObject *map;
1465 for (i = 0; i < quant->mc_count; ++i) {
1466 colors[i].Red = quant->mc_colors[i].rgb.r;
1467 colors[i].Green = quant->mc_colors[i].rgb.g;
1468 colors[i].Blue = quant->mc_colors[i].rgb.b;
1471 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1472 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1473 colors[size].Red = trans.rgb.r;
1474 colors[size].Green = trans.rgb.g;
1475 colors[size].Blue = trans.rgb.b;
1479 while (map_size < size)
1481 /* giflib spews for 1 colour maps, reasonable, I suppose */
1484 while (i < map_size) {
1485 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1489 map = MakeMapObject(map_size, colors);
1490 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1493 i_push_error(0, "Could not create color map object");
1500 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1502 We need to call EGifSetGifVersion() before opening the file - put that
1505 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1506 giflib 4.1.0 has code:
1508 static char *GifVersionPrefix = GIF87_STAMP;
1510 and the code that sets the version internally does:
1512 strncpy(&GifVersionPrefix[3], Version, 3);
1514 which is very broken.
1516 Failing to set the correct GIF version doesn't seem to cause a problem
1519 Modern versions (4.1.4 anyway) of giflib/libungif handle
1520 EGifSetGifVersion correctly.
1522 If t/t105gif.t crashes here then run Makefile.PL with
1523 --nogifsetversion, eg.:
1525 perl Makefile.PL --nogifsetversion
1527 or install a less buggy giflib.
1532 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1533 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1534 && !defined(IM_NO_SET_GIF_VERSION)
1539 if (quant->transp != tr_none)
1542 for (i = 0; i < count; ++i) {
1543 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1547 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1551 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1555 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1562 EGifSetGifVersion("89a");
1564 EGifSetGifVersion("87a");
1569 in_palette(i_color *c, i_quantize *quant, int size) {
1572 for (i = 0; i < size; ++i) {
1573 if (c->channel[0] == quant->mc_colors[i].channel[0]
1574 && c->channel[1] == quant->mc_colors[i].channel[1]
1575 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1584 =item has_common_palette(imgs, count, quant, want_trans)
1586 Tests if all the given images are paletted and have a common palette,
1587 if they do it builds that palette.
1589 A possible improvement might be to eliminate unused colors in the
1595 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1597 int size = quant->mc_count;
1602 /* we try to build a common palette here, if we can manage that, then
1603 that's the palette we use */
1604 for (imgn = 0; imgn < count; ++imgn) {
1605 int eliminate_unused;
1606 if (imgs[imgn]->type != i_palette_type)
1609 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1610 &eliminate_unused)) {
1611 eliminate_unused = 1;
1614 if (eliminate_unused) {
1615 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1617 memset(used, 0, sizeof(used));
1619 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1620 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1621 for (x = 0; x < imgs[imgn]->xsize; ++x)
1628 /* assume all are in use */
1629 memset(used, 1, sizeof(used));
1632 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1635 i_getcolors(imgs[imgn], i, &c, 1);
1637 if (in_palette(&c, quant, size) < 0) {
1638 if (size < quant->mc_size) {
1639 quant->mc_colors[size++] = c;
1642 /* oops, too many colors */
1650 quant->mc_count = size;
1656 quant_paletted(i_quantize *quant, i_img *img) {
1657 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1659 i_palidx trans[256];
1663 /* build a translation table */
1664 for (i = 0; i < i_colorcount(img); ++i) {
1666 i_getcolors(img, i, &c, 1);
1667 trans[i] = in_palette(&c, quant, quant->mc_count);
1670 for (y = 0; y < img->ysize; ++y) {
1671 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1672 for (x = 0; x < img->xsize; ++x) {
1682 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1684 Internal. Low-level function that does the high-level GIF processing
1687 Returns non-zero on success.
1693 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1694 unsigned char *result = NULL;
1696 ColorMapObject *map;
1697 int scrw = 0, scrh = 0;
1698 int imgn, orig_count, orig_size;
1700 int trans_index = -1;
1704 i_img **glob_imgs; /* images that will use the global color map */
1706 i_color *orig_colors = quant->mc_colors;
1707 i_color *glob_colors = NULL;
1708 int glob_color_count = 0;
1709 int glob_want_trans;
1710 int glob_paletted = 0; /* the global map was made from the image palettes */
1711 int colors_paletted = 0;
1716 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1717 quant, gf, imgs, count));
1719 /* *((char *)0) = 1; */ /* used to break into the debugger */
1722 i_push_error(0, "No images provided to write");
1723 return 0; /* what are you smoking? */
1726 i_mempool_init(&mp);
1728 /* sanity is nice */
1729 if (quant->mc_size > 256)
1730 quant->mc_size = 256;
1731 if (quant->mc_count > quant->mc_size)
1732 quant->mc_count = quant->mc_size;
1734 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1736 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1740 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1741 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1743 glob_want_trans = 0;
1744 for (imgn = 0; imgn < count; ++imgn) {
1746 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1747 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1748 if (imgs[imgn]->xsize + posx > scrw)
1749 scrw = imgs[imgn]->xsize + posx;
1750 if (imgs[imgn]->ysize + posy > scrh)
1751 scrh = imgs[imgn]->ysize + posy;
1752 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1753 localmaps[imgn] = 0;
1754 if (localmaps[imgn])
1757 if (imgs[imgn]->channels == 4) {
1758 glob_want_trans = 1;
1760 glob_imgs[glob_img_count++] = imgs[imgn];
1763 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1765 orig_count = quant->mc_count;
1766 orig_size = quant->mc_size;
1768 if (glob_img_count) {
1770 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1771 quant->mc_colors = glob_colors;
1772 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1773 /* we have some images that want to use the global map */
1774 if (glob_want_trans && quant->mc_count == 256) {
1775 mm_log((2, " disabling transparency for global map - no space\n"));
1776 glob_want_trans = 0;
1778 if (glob_want_trans && quant->mc_size == 256) {
1779 mm_log((2, " reserving color for transparency\n"));
1782 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1787 i_quant_makemap(quant, glob_imgs, glob_img_count);
1789 glob_color_count = quant->mc_count;
1790 quant->mc_colors = orig_colors;
1793 /* use the global map if we have one, otherwise use the local map */
1796 quant->mc_colors = glob_colors;
1797 quant->mc_count = glob_color_count;
1798 want_trans = glob_want_trans && imgs[0]->channels == 4;
1800 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1802 if (gif_background < 0)
1804 if (gif_background >= glob_color_count)
1808 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1809 if (has_common_palette(imgs, 1, quant, want_trans)) {
1810 colors_paletted = 1;
1813 colors_paletted = 0;
1814 i_quant_makemap(quant, imgs, 1);
1817 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1818 i_mempool_destroy(&mp);
1819 quant->mc_colors = orig_colors;
1821 mm_log((1, "Error in MakeMapObject"));
1826 /* since we don't know how big some the local palettes could be
1827 we need to base the bits on the maximum number of colors */
1828 while (orig_size > (1 << color_bits))
1832 int count = quant->mc_count;
1835 while (count > (1 << color_bits))
1839 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1840 gif_background, map) == GIF_ERROR) {
1841 i_mempool_destroy(&mp);
1842 quant->mc_colors = orig_colors;
1844 i_push_error(0, "Could not save screen descriptor");
1848 mm_log((1, "Error in EGifPutScreenDesc."));
1853 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1855 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1858 if (!localmaps[0]) {
1860 colors_paletted = glob_paletted;
1863 /* if this image has a global map the colors in quant don't
1864 belong to this image, so build a palette */
1866 /* generate the local map for this image */
1867 quant->mc_colors = orig_colors;
1868 quant->mc_size = orig_size;
1869 quant->mc_count = orig_count;
1870 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1872 /* if the caller gives us too many colours we can't do transparency */
1873 if (want_trans && quant->mc_count == 256)
1875 /* if they want transparency but give us a big size, make it smaller
1876 to give room for a transparency colour */
1877 if (want_trans && quant->mc_size == 256)
1879 if (has_common_palette(imgs, 1, quant, want_trans)) {
1880 colors_paletted = 1;
1883 colors_paletted = 0;
1884 i_quant_makemap(quant, imgs, 1);
1886 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1887 i_mempool_destroy(&mp);
1889 quant->mc_colors = orig_colors;
1890 mm_log((1, "Error in MakeMapObject"));
1895 /* the map we wrote was the map for this image - don't set the local
1901 if (colors_paletted)
1902 result = quant_paletted(quant, imgs[0]);
1904 result = i_quant_translate(quant, imgs[0]);
1906 i_mempool_destroy(&mp);
1907 quant->mc_colors = orig_colors;
1912 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1913 trans_index = quant->mc_count;
1916 if (!do_ns_loop(gf, imgs[0])) {
1917 i_mempool_destroy(&mp);
1918 quant->mc_colors = orig_colors;
1922 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1923 i_mempool_destroy(&mp);
1924 quant->mc_colors = orig_colors;
1930 if (!do_comments(gf, imgs[0])) {
1931 i_mempool_destroy(&mp);
1932 quant->mc_colors = orig_colors;
1938 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1940 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1941 interlace, map) == GIF_ERROR) {
1942 i_mempool_destroy(&mp);
1943 quant->mc_colors = orig_colors;
1945 i_push_error(0, "Could not save image descriptor");
1947 mm_log((1, "Error in EGifPutImageDesc."));
1953 if (!do_write(gf, interlace, imgs[0], result)) {
1954 i_mempool_destroy(&mp);
1955 quant->mc_colors = orig_colors;
1962 /* that first awful image is out of the way, do the rest */
1963 for (imgn = 1; imgn < count; ++imgn) {
1964 if (localmaps[imgn]) {
1965 quant->mc_colors = orig_colors;
1966 quant->mc_count = orig_count;
1967 quant->mc_size = orig_size;
1969 want_trans = quant->transp != tr_none
1970 && imgs[imgn]->channels == 4;
1971 /* if the caller gives us too many colours we can't do transparency */
1972 if (want_trans && quant->mc_count == 256)
1974 /* if they want transparency but give us a big size, make it smaller
1975 to give room for a transparency colour */
1976 if (want_trans && quant->mc_size == 256)
1979 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1980 result = quant_paletted(quant, imgs[imgn]);
1983 i_quant_makemap(quant, imgs+imgn, 1);
1984 result = i_quant_translate(quant, imgs[imgn]);
1987 i_mempool_destroy(&mp);
1988 quant->mc_colors = orig_colors;
1990 mm_log((1, "error in i_quant_translate()"));
1994 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1995 trans_index = quant->mc_count;
1998 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1999 i_mempool_destroy(&mp);
2000 quant->mc_colors = orig_colors;
2003 mm_log((1, "Error in MakeMapObject."));
2008 quant->mc_colors = glob_colors;
2009 quant->mc_count = glob_color_count;
2011 result = quant_paletted(quant, imgs[imgn]);
2013 result = i_quant_translate(quant, imgs[imgn]);
2014 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
2016 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2017 trans_index = quant->mc_count;
2022 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
2023 i_mempool_destroy(&mp);
2024 quant->mc_colors = orig_colors;
2030 if (!do_comments(gf, imgs[imgn])) {
2031 i_mempool_destroy(&mp);
2032 quant->mc_colors = orig_colors;
2038 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2040 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2043 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2045 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
2046 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2047 i_mempool_destroy(&mp);
2048 quant->mc_colors = orig_colors;
2050 i_push_error(0, "Could not save image descriptor");
2055 mm_log((1, "Error in EGifPutImageDesc."));
2061 if (!do_write(gf, interlace, imgs[imgn], result)) {
2062 i_mempool_destroy(&mp);
2063 quant->mc_colors = orig_colors;
2071 if (EGifCloseFile(gf) == GIF_ERROR) {
2072 i_mempool_destroy(&mp);
2074 i_push_error(0, "Could not close GIF file");
2075 mm_log((1, "Error in EGifCloseFile\n"));
2080 for (i = 0; i < glob_color_count; ++i)
2081 orig_colors[i] = glob_colors[i];
2084 i_mempool_destroy(&mp);
2085 quant->mc_colors = orig_colors;
2091 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2093 General high-level function to write a GIF to a file.
2095 Writes the GIF images to the specified file handle using the options
2096 in quant and opts. See L<image.h/i_quantize> and
2097 L<image.h/i_gif_opts>.
2099 Returns non-zero on success.
2105 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2109 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2110 quant, fd, imgs, count));
2112 gif_set_version(quant, imgs, count);
2114 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2116 i_push_error(0, "Cannot create GIF file object");
2117 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2121 return i_writegif_low(quant, gf, imgs, count);
2124 #if IM_GIFMAJOR >= 4
2127 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2129 Internal. Wrapper for the user write callback function.
2134 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2136 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2138 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2144 =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)
2146 General high-level function to write a GIF using callbacks to send
2149 Returns non-zero on success.
2155 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2156 int maxlength, i_img **imgs, int count)
2158 #if IM_GIFMAJOR >= 4
2160 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2165 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2166 quant, cb, userdata, maxlength, imgs, count));
2168 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2170 i_push_error(0, "Cannot create GIF file object");
2171 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2172 i_free_gen_write_data(gwd, 0);
2176 result = i_writegif_low(quant, gf, imgs, count);
2177 return i_free_gen_write_data(gwd, result);
2180 i_push_error(0, "callbacks not supported with giflib3");
2186 #if IM_GIFMAJOR >= 4
2189 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2190 io_glue *ig = (io_glue *)gft->UserData;
2192 return ig->writecb(ig, data, length);
2198 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2203 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2205 io_glue_commit_types(ig);
2207 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2208 int fd = dup(ig->source.fdseek.fd);
2210 i_push_error(errno, "dup() failed");
2213 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2214 is called - dup it so the caller's fd isn't closed */
2215 return i_writegif_gen(quant, fd, imgs, count);
2218 #if IM_GIFMAJOR >= 4
2219 GifFileType *GifFile;
2224 gif_set_version(quant, imgs, count);
2226 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2228 i_push_error(0, "Cannot create giflib callback object");
2229 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2233 result = i_writegif_low(quant, GifFile, imgs, count);
2240 i_push_error(0, "callbacks not supported with giflib3");
2248 =item gif_error_msg(int code)
2250 Grabs the most recent giflib error code from GifLastError() and
2251 returns a string that describes that error.
2253 The returned pointer points to a static buffer, either from a literal
2254 C string or a static buffer.
2259 static char const *gif_error_msg(int code) {
2260 static char msg[80];
2263 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2264 return "Failed to open given file";
2266 case E_GIF_ERR_WRITE_FAILED:
2267 return "Write failed";
2269 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2270 return "Screen descriptor already passed to giflib";
2272 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2273 return "Image descriptor already passed to giflib";
2275 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2276 return "Neither global nor local color map set";
2278 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2279 return "Too much pixel data passed to giflib";
2281 case E_GIF_ERR_NOT_ENOUGH_MEM:
2282 return "Out of memory";
2284 case E_GIF_ERR_DISK_IS_FULL:
2285 return "Disk is full";
2287 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2288 return "File close failed";
2290 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2291 return "File not writable";
2293 case D_GIF_ERR_OPEN_FAILED:
2294 return "Failed to open file";
2296 case D_GIF_ERR_READ_FAILED:
2297 return "Failed to read from file";
2299 case D_GIF_ERR_NOT_GIF_FILE:
2300 return "File is not a GIF file";
2302 case D_GIF_ERR_NO_SCRN_DSCR:
2303 return "No screen descriptor detected - invalid file";
2305 case D_GIF_ERR_NO_IMAG_DSCR:
2306 return "No image descriptor detected - invalid file";
2308 case D_GIF_ERR_NO_COLOR_MAP:
2309 return "No global or local color map found";
2311 case D_GIF_ERR_WRONG_RECORD:
2312 return "Wrong record type detected - invalid file?";
2314 case D_GIF_ERR_DATA_TOO_BIG:
2315 return "Data in file too big for image";
2317 case D_GIF_ERR_NOT_ENOUGH_MEM:
2318 return "Out of memory";
2320 case D_GIF_ERR_CLOSE_FAILED:
2321 return "Close failed";
2323 case D_GIF_ERR_NOT_READABLE:
2324 return "File not opened for read";
2326 case D_GIF_ERR_IMAGE_DEFECT:
2327 return "Defective image";
2329 case D_GIF_ERR_EOF_TOO_SOON:
2330 return "Unexpected EOF - invalid file";
2333 sprintf(msg, "Unknown giflib error code %d", code);
2339 =item gif_push_error()
2341 Utility function that takes the current GIF error code, converts it to
2342 an error message and pushes it on the error stack.
2347 static void gif_push_error(void) {
2348 int code = GifLastError(); /* clears saved error */
2350 i_push_error(code, gif_error_msg(code));
2356 The Netscape loop extension isn't implemented. Giflib's extension
2357 writing code doesn't seem to support writing named extensions in this
2360 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2361 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2364 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2365 supplied interface in giflib 4.1.0 causes a SEGV in
2366 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2370 Arnar M. Hrafnkelsson, addi@umich.edu