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;
535 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
537 BackGround = GifFile->SBackGroundColor;
539 Size = GifFile->SWidth * sizeof(GifPixelType);
541 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
542 i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
544 /* Scan the content of the GIF file and load the image(s) in: */
546 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
548 i_push_error(0, "Unable to get record type");
549 free_images(results, *count);
550 DGifCloseFile(GifFile);
555 switch (RecordType) {
556 case IMAGE_DESC_RECORD_TYPE:
557 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
559 i_push_error(0, "Unable to get image descriptor");
560 free_images(results, *count);
561 DGifCloseFile(GifFile);
566 Width = GifFile->Image.Width;
567 Height = GifFile->Image.Height;
568 if (page == -1 || page == ImageNum) {
569 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
570 mm_log((1, "Adding local colormap\n"));
571 ColorMapSize = ColorMap->ColorCount;
573 /* No colormap and we are about to read in the image -
575 mm_log((1, "Going in with no colormap\n"));
576 i_push_error(0, "Image does not have a local or a global color map");
577 free_images(results, *count);
578 DGifCloseFile(GifFile);
584 if (got_gce && trans_index >= 0)
586 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
587 free_images(results, *count);
588 mm_log((1, "i_readgif: image size exceeds limits\n"));
589 DGifCloseFile(GifFile);
593 img = i_img_pal_new(Width, Height, channels, 256);
595 free_images(results, *count);
596 DGifCloseFile(GifFile);
599 /* populate the palette of the new image */
600 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
601 for (i = 0; i < ColorMapSize; ++i) {
603 col.rgba.r = ColorMap->Colors[i].Red;
604 col.rgba.g = ColorMap->Colors[i].Green;
605 col.rgba.b = ColorMap->Colors[i].Blue;
606 if (channels == 4 && trans_index == i)
611 i_addcolors(img, &col, 1);
614 if (*count > result_alloc) {
615 if (result_alloc == 0) {
617 results = mymalloc(result_alloc * sizeof(i_img *));
620 /* myrealloc never fails (it just dies if it can't allocate) */
622 results = myrealloc(results, result_alloc * sizeof(i_img *));
625 results[*count-1] = img;
626 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
627 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
629 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
630 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
631 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
632 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
633 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
634 i_tags_addn(&img->tags, "gif_background", 0,
635 GifFile->SBackGroundColor);
637 if (GifFile->Image.ColorMap) {
638 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
641 if (trans_index >= 0) {
643 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
644 i_getcolors(img, trans_index, &trans, 1);
645 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
647 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
648 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
649 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
653 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
655 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
660 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
661 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
663 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
664 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
665 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
666 free_images(results, *count);
667 DGifCloseFile(GifFile);
672 if (GifFile->Image.Interlace) {
673 for (Count = i = 0; i < 4; i++) {
674 for (j = InterlacedOffset[i]; j < Height;
675 j += InterlacedJumps[i]) {
677 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
679 i_push_error(0, "Reading GIF line");
680 free_images(results, *count);
681 DGifCloseFile(GifFile);
686 i_ppal(img, 0, Width, j, GifRow);
691 for (i = 0; i < Height; i++) {
692 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
694 i_push_error(0, "Reading GIF line");
695 free_images(results, *count);
696 DGifCloseFile(GifFile);
701 i_ppal(img, 0, Width, i, GifRow);
705 /* must be only one image wanted and that was it */
708 DGifCloseFile(GifFile);
714 /* whether interlaced or not, it has the same number of lines */
715 /* giflib does't have an interface to skip the image data */
716 for (i = 0; i < Height; i++) {
717 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
719 i_push_error(0, "Reading GIF line");
720 free_images(results, *count);
722 DGifCloseFile(GifFile);
727 /* kill the comment so we get the right comment for the page */
735 case EXTENSION_RECORD_TYPE:
736 /* Skip any extension blocks in file: */
737 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
739 i_push_error(0, "Reading extension record");
740 free_images(results, *count);
741 DGifCloseFile(GifFile);
744 if (ExtCode == 0xF9) {
746 if (Extension[1] & 1)
747 trans_index = Extension[4];
750 gif_delay = Extension[2] + 256 * Extension[3];
751 user_input = (Extension[1] & 2) != 0;
752 disposal = (Extension[1] >> 2) & 7;
754 if (ExtCode == 0xFF && *Extension == 11) {
755 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
756 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
758 i_push_error(0, "reading loop extension");
759 free_images(results, *count);
760 DGifCloseFile(GifFile);
763 if (Extension && *Extension == 3) {
765 ns_loop = Extension[2] + 256 * Extension[3];
769 else if (ExtCode == 0xFE) {
770 /* while it's possible for a GIF file to contain more than one
771 comment, I'm only implementing a single comment per image,
772 with the comment saved into the following image.
773 If someone wants more than that they can implement it.
774 I also don't handle comments that take more than one block.
777 comment = mymalloc(*Extension+1);
778 memcpy(comment, Extension+1, *Extension);
779 comment[*Extension] = '\0';
782 while (Extension != NULL) {
783 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
785 i_push_error(0, "reading next block of extension");
786 free_images(results, *count);
787 DGifCloseFile(GifFile);
792 case TERMINATE_RECORD_TYPE:
794 default: /* Should be trapped by DGifGetRecordType. */
797 } while (RecordType != TERMINATE_RECORD_TYPE);
801 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
809 if (DGifCloseFile(GifFile) == GIF_ERROR) {
811 i_push_error(0, "Closing GIF file object");
812 free_images(results, *count);
816 if (ImageNum && page != -1) {
817 /* there were images, but the page selected wasn't found */
818 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
819 free_images(results, *count);
827 /* giflib declares this incorrectly as EgifOpen */
828 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
830 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
834 =item i_readgif_multi_wiol(ig, int *count)
840 i_readgif_multi_wiol(io_glue *ig, int *count) {
841 io_glue_commit_types(ig);
843 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
844 return i_readgif_multi(ig->source.fdseek.fd, count);
848 GifFileType *GifFile;
852 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
854 i_push_error(0, "Cannot create giflib callback object");
855 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
859 return i_readgif_multi_low(GifFile, count, -1);
862 i_push_error(0, "callbacks not supported with giflib3");
870 =item i_readgif_multi(int fd, int *count)
875 i_readgif_multi(int fd, int *count) {
876 GifFileType *GifFile;
880 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
882 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
884 i_push_error(0, "Cannot create giflib file object");
885 mm_log((1,"i_readgif: Unable to open file\n"));
889 return i_readgif_multi_low(GifFile, count, -1);
893 =item i_readgif_multi_scalar(char *data, int length, int *count)
898 i_readgif_multi_scalar(char *data, int length, int *count) {
900 GifFileType *GifFile;
901 struct gif_scalar_info gsi;
909 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
910 data, length, count));
912 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
914 i_push_error(0, "Cannot create giflib callback object");
915 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
919 return i_readgif_multi_low(GifFile, count, -1);
926 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
928 Read a GIF file into an Imager RGB file, the data of the GIF file is
929 retreived by callin the user supplied callback function.
931 This function is only used with giflib 4 and higher.
937 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
939 GifFileType *GifFile;
942 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
946 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
947 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
949 i_push_error(0, "Cannot create giflib callback object");
950 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
955 result = i_readgif_multi_low(GifFile, count, -1);
956 i_free_gen_read_data(gci);
965 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
967 Write I<img> to the file handle I<fd>. The resulting GIF will use a
968 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
969 colours taken from I<fixed>.
971 Returns non-zero on success.
977 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
981 memset(&quant, 0, sizeof(quant));
982 quant.make_colors = mc_addi;
983 quant.mc_colors = colors;
984 quant.mc_size = 1<<max_colors;
985 quant.mc_count = fixedlen;
986 memcpy(colors, fixed, fixedlen * sizeof(i_color));
987 quant.translate = pt_perturb;
988 quant.perturb = pixdev;
989 return i_writegif_gen(&quant, fd, &im, 1);
993 =item i_writegifmc(i_img *im, int fd, int max_colors)
995 Write I<img> to the file handle I<fd>. The resulting GIF will use a
996 maximum of 1<<I<max_colours> colours.
998 Returns non-zero on success.
1004 i_writegifmc(i_img *im, int fd, int max_colors) {
1005 i_color colors[256];
1008 /* *(char *)0 = 1; */
1010 memset(&quant, 0, sizeof(quant));
1011 quant.make_colors = mc_none; /* ignored for pt_giflib */
1012 quant.mc_colors = colors;
1013 quant.mc_size = 1 << max_colors;
1015 quant.translate = pt_giflib;
1016 return i_writegif_gen(&quant, fd, &im, 1);
1021 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1023 Reads a GIF file from an in memory copy of the file. This can be used
1024 if you get the 'file' from some source other than an actual file (or
1025 some other file handle).
1027 This function is only available with giflib 4 and higher.
1032 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1033 #if IM_GIFMAJOR >= 4
1034 GifFileType *GifFile;
1035 struct gif_scalar_info gsi;
1043 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1044 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1046 i_push_error(0, "Cannot create giflib callback object");
1047 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1051 return i_readgif_low(GifFile, colour_table, colours);
1057 #if IM_GIFMAJOR >= 4
1060 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1062 Internal. The reader callback wrapper passed to giflib.
1064 This function is only used with giflib 4 and higher.
1070 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1071 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1078 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1080 Read a GIF file into an Imager RGB file, the data of the GIF file is
1081 retreived by callin the user supplied callback function.
1083 This function is only used with giflib 4 and higher.
1089 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1090 #if IM_GIFMAJOR >= 4
1091 GifFileType *GifFile;
1094 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1098 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1099 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1101 i_push_error(0, "Cannot create giflib callback object");
1102 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1107 result = i_readgif_low(GifFile, colour_table, colours);
1108 i_free_gen_read_data(gci);
1113 i_push_error(0, "callbacks not supported with giflib3");
1119 #if IM_GIFMAJOR >= 4
1122 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1123 io_glue *ig = (io_glue *)gft->UserData;
1125 return ig->readcb(ig, buf, length);
1131 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1132 io_glue_commit_types(ig);
1134 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1135 int fd = dup(ig->source.fdseek.fd);
1137 i_push_error(errno, "dup() failed");
1140 return i_readgif(fd, color_table, colors);
1143 #if IM_GIFMAJOR >= 4
1144 GifFileType *GifFile;
1148 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1150 i_push_error(0, "Cannot create giflib callback object");
1151 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1155 return i_readgif_low(GifFile, color_table, colors);
1159 i_push_error(0, "callbacks not supported with giflib3");
1167 =item i_readgif_single_low(GifFile, page)
1169 Lower level function to read a single image from a GIF.
1171 page must be non-negative.
1176 i_readgif_single_low(GifFileType *GifFile, int page) {
1180 imgs = i_readgif_multi_low(GifFile, &count, page);
1182 if (imgs && count) {
1183 i_img *result = imgs[0];
1189 /* i_readgif_multi_low() handles the errors appropriately */
1195 =item i_readgif_single_wiol(ig, page)
1197 Read a single page from a GIF image file, where the page is indexed
1200 Returns NULL if the page isn't found.
1206 i_readgif_single_wiol(io_glue *ig, int page) {
1207 io_glue_commit_types(ig);
1212 i_push_error(0, "page must be non-negative");
1216 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1217 GifFileType *GifFile;
1218 int fd = dup(ig->source.fdseek.fd);
1220 i_push_error(errno, "dup() failed");
1223 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1225 i_push_error(0, "Cannot create giflib file object");
1226 mm_log((1,"i_readgif: Unable to open file\n"));
1229 return i_readgif_single_low(GifFile, page);
1232 #if IM_GIFMAJOR >= 4
1233 GifFileType *GifFile;
1235 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1237 i_push_error(0, "Cannot create giflib callback object");
1238 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1242 return i_readgif_single_low(GifFile, page);
1244 i_push_error(0, "callbacks not supported with giflib3");
1252 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1254 Internal. Low level image write function. Writes in interlace if
1255 that was requested in the GIF options.
1257 Returns non-zero on success.
1262 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1265 for (i = 0; i < 4; ++i) {
1266 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1267 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1269 i_push_error(0, "Could not save image data:");
1270 mm_log((1, "Error in EGifPutLine\n"));
1279 for (y = 0; y < img->ysize; ++y) {
1280 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1282 i_push_error(0, "Could not save image data:");
1283 mm_log((1, "Error in EGifPutLine\n"));
1295 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1297 Internal. Writes the GIF graphics control extension, if necessary.
1299 Returns non-zero on success.
1303 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1305 unsigned char gce[4] = {0};
1309 int disposal_method;
1313 gce[3] = trans_index;
1316 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1317 gce[1] = delay % 256;
1318 gce[2] = delay / 256;
1321 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1326 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1327 gce[0] |= (disposal_method & 3) << 2;
1331 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1333 i_push_error(0, "Could not save GCE");
1340 =item do_comments(gf, img)
1342 Write any comments in the image.
1346 static int do_comments(GifFileType *gf, i_img *img) {
1349 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1350 if (img->tags.tags[pos].data) {
1351 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1357 sprintf(buf, "%d", img->tags.tags[pos].idata);
1358 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1368 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1370 Internal. Add the Netscape2.0 loop extension block, if requested.
1372 Giflib/libungif prior to 4.1.1 didn't support writing application
1373 extension blocks, so we don't attempt to write them for older versions.
1375 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1376 writing extension blocks so that they could only be written to files.
1380 static int do_ns_loop(GifFileType *gf, i_img *img)
1382 /* EGifPutExtension() doesn't appear to handle application
1383 extension blocks in any way
1384 Since giflib wraps the fd with a FILE * (and puts that in its
1385 private data), we can't do an end-run and write the data
1387 There's no open interface that takes a FILE * either, so we
1388 can't workaround it that way either.
1389 If giflib's callback interface wasn't broken by default, I'd
1390 force file writes to use callbacks, but it is broken by default.
1392 /* yes this was another attempt at supporting the loop extension */
1393 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1395 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1396 unsigned char nsle[12] = "NETSCAPE2.0";
1397 unsigned char subblock[3];
1398 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1400 i_push_error(0, "writing loop extension");
1404 subblock[1] = loop_count % 256;
1405 subblock[2] = loop_count / 256;
1406 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1408 i_push_error(0, "writing loop extention sub-block");
1418 =item make_gif_map(i_quantize *quant, int want_trans)
1420 Create a giflib color map object from an Imager color map.
1425 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1427 GifColorType colors[256];
1429 int size = quant->mc_count;
1431 ColorMapObject *map;
1434 for (i = 0; i < quant->mc_count; ++i) {
1435 colors[i].Red = quant->mc_colors[i].rgb.r;
1436 colors[i].Green = quant->mc_colors[i].rgb.g;
1437 colors[i].Blue = quant->mc_colors[i].rgb.b;
1440 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1441 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1442 colors[size].Red = trans.rgb.r;
1443 colors[size].Green = trans.rgb.g;
1444 colors[size].Blue = trans.rgb.b;
1448 while (map_size < size)
1450 /* giflib spews for 1 colour maps, reasonable, I suppose */
1453 while (i < map_size) {
1454 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1458 map = MakeMapObject(map_size, colors);
1459 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1462 i_push_error(0, "Could not create color map object");
1469 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1471 We need to call EGifSetGifVersion() before opening the file - put that
1474 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1475 giflib 4.1.0 has code:
1477 static char *GifVersionPrefix = GIF87_STAMP;
1479 and the code that sets the version internally does:
1481 strncpy(&GifVersionPrefix[3], Version, 3);
1483 which is very broken.
1485 Failing to set the correct GIF version doesn't seem to cause a problem
1488 Modern versions (4.1.4 anyway) of giflib/libungif handle
1489 EGifSetGifVersion correctly.
1491 If t/t105gif.t crashes here then run Makefile.PL with
1492 --nogifsetversion, eg.:
1494 perl Makefile.PL --nogifsetversion
1496 or install a less buggy giflib.
1501 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1502 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1503 && !defined(IM_NO_SET_GIF_VERSION)
1508 if (quant->transp != tr_none)
1511 for (i = 0; i < count; ++i) {
1512 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1516 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1520 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1524 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1531 EGifSetGifVersion("89a");
1533 EGifSetGifVersion("87a");
1538 in_palette(i_color *c, i_quantize *quant, int size) {
1541 for (i = 0; i < size; ++i) {
1542 if (c->channel[0] == quant->mc_colors[i].channel[0]
1543 && c->channel[1] == quant->mc_colors[i].channel[1]
1544 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1553 =item has_common_palette(imgs, count, quant, want_trans)
1555 Tests if all the given images are paletted and have a common palette,
1556 if they do it builds that palette.
1558 A possible improvement might be to eliminate unused colors in the
1564 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1566 int size = quant->mc_count;
1571 /* we try to build a common palette here, if we can manage that, then
1572 that's the palette we use */
1573 for (imgn = 0; imgn < count; ++imgn) {
1574 int eliminate_unused;
1575 if (imgs[imgn]->type != i_palette_type)
1578 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1579 &eliminate_unused)) {
1580 eliminate_unused = 1;
1583 if (eliminate_unused) {
1584 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1586 memset(used, 0, sizeof(used));
1588 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1589 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1590 for (x = 0; x < imgs[imgn]->xsize; ++x)
1597 /* assume all are in use */
1598 memset(used, 1, sizeof(used));
1601 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1604 i_getcolors(imgs[imgn], i, &c, 1);
1606 if (in_palette(&c, quant, size) < 0) {
1607 if (size < quant->mc_size) {
1608 quant->mc_colors[size++] = c;
1611 /* oops, too many colors */
1619 quant->mc_count = size;
1625 quant_paletted(i_quantize *quant, i_img *img) {
1626 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1628 i_palidx trans[256];
1632 /* build a translation table */
1633 for (i = 0; i < i_colorcount(img); ++i) {
1635 i_getcolors(img, i, &c, 1);
1636 trans[i] = in_palette(&c, quant, quant->mc_count);
1639 for (y = 0; y < img->ysize; ++y) {
1640 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1641 for (x = 0; x < img->xsize; ++x) {
1651 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1653 Internal. Low-level function that does the high-level GIF processing
1656 Returns non-zero on success.
1662 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1663 unsigned char *result = NULL;
1665 ColorMapObject *map;
1666 int scrw = 0, scrh = 0;
1667 int imgn, orig_count, orig_size;
1669 int trans_index = -1;
1673 i_img **glob_imgs; /* images that will use the global color map */
1675 i_color *orig_colors = quant->mc_colors;
1676 i_color *glob_colors = NULL;
1677 int glob_color_count = 0;
1678 int glob_want_trans;
1679 int glob_paletted = 0; /* the global map was made from the image palettes */
1680 int colors_paletted = 0;
1685 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1686 quant, gf, imgs, count));
1688 /* *((char *)0) = 1; */ /* used to break into the debugger */
1691 i_push_error(0, "No images provided to write");
1692 return 0; /* what are you smoking? */
1695 i_mempool_init(&mp);
1697 /* sanity is nice */
1698 if (quant->mc_size > 256)
1699 quant->mc_size = 256;
1700 if (quant->mc_count > quant->mc_size)
1701 quant->mc_count = quant->mc_size;
1703 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1705 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1709 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1710 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1712 glob_want_trans = 0;
1713 for (imgn = 0; imgn < count; ++imgn) {
1715 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1716 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1717 if (imgs[imgn]->xsize + posx > scrw)
1718 scrw = imgs[imgn]->xsize + posx;
1719 if (imgs[imgn]->ysize + posy > scrh)
1720 scrh = imgs[imgn]->ysize + posy;
1721 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1722 localmaps[imgn] = 0;
1723 if (localmaps[imgn])
1726 if (imgs[imgn]->channels == 4) {
1727 glob_want_trans = 1;
1729 glob_imgs[glob_img_count++] = imgs[imgn];
1732 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1734 orig_count = quant->mc_count;
1735 orig_size = quant->mc_size;
1737 if (glob_img_count) {
1739 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1740 quant->mc_colors = glob_colors;
1741 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1742 /* we have some images that want to use the global map */
1743 if (glob_want_trans && quant->mc_count == 256) {
1744 mm_log((2, " disabling transparency for global map - no space\n"));
1745 glob_want_trans = 0;
1747 if (glob_want_trans && quant->mc_size == 256) {
1748 mm_log((2, " reserving color for transparency\n"));
1751 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1756 i_quant_makemap(quant, glob_imgs, glob_img_count);
1758 glob_color_count = quant->mc_count;
1759 quant->mc_colors = orig_colors;
1762 /* use the global map if we have one, otherwise use the local map */
1765 quant->mc_colors = glob_colors;
1766 quant->mc_count = glob_color_count;
1767 want_trans = glob_want_trans && imgs[0]->channels == 4;
1769 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1771 if (gif_background < 0)
1773 if (gif_background >= glob_color_count)
1777 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1778 if (has_common_palette(imgs, 1, quant, want_trans)) {
1779 colors_paletted = 1;
1782 colors_paletted = 0;
1783 i_quant_makemap(quant, imgs, 1);
1786 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1787 i_mempool_destroy(&mp);
1788 quant->mc_colors = orig_colors;
1790 mm_log((1, "Error in MakeMapObject"));
1795 /* since we don't know how big some the local palettes could be
1796 we need to base the bits on the maximum number of colors */
1797 while (orig_size > (1 << color_bits))
1801 int count = quant->mc_count;
1804 while (count > (1 << color_bits))
1808 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1809 gif_background, map) == GIF_ERROR) {
1810 i_mempool_destroy(&mp);
1811 quant->mc_colors = orig_colors;
1813 i_push_error(0, "Could not save screen descriptor");
1817 mm_log((1, "Error in EGifPutScreenDesc."));
1822 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1824 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1827 if (!localmaps[0]) {
1829 colors_paletted = glob_paletted;
1832 /* if this image has a global map the colors in quant don't
1833 belong to this image, so build a palette */
1835 /* generate the local map for this image */
1836 quant->mc_colors = orig_colors;
1837 quant->mc_size = orig_size;
1838 quant->mc_count = orig_count;
1839 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1841 /* if the caller gives us too many colours we can't do transparency */
1842 if (want_trans && quant->mc_count == 256)
1844 /* if they want transparency but give us a big size, make it smaller
1845 to give room for a transparency colour */
1846 if (want_trans && quant->mc_size == 256)
1848 if (has_common_palette(imgs, 1, quant, want_trans)) {
1849 colors_paletted = 1;
1852 colors_paletted = 0;
1853 i_quant_makemap(quant, imgs, 1);
1855 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1856 i_mempool_destroy(&mp);
1858 quant->mc_colors = orig_colors;
1859 mm_log((1, "Error in MakeMapObject"));
1864 /* the map we wrote was the map for this image - don't set the local
1870 if (colors_paletted)
1871 result = quant_paletted(quant, imgs[0]);
1873 result = i_quant_translate(quant, imgs[0]);
1875 i_mempool_destroy(&mp);
1876 quant->mc_colors = orig_colors;
1881 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1882 trans_index = quant->mc_count;
1885 if (!do_ns_loop(gf, imgs[0])) {
1886 i_mempool_destroy(&mp);
1887 quant->mc_colors = orig_colors;
1891 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1892 i_mempool_destroy(&mp);
1893 quant->mc_colors = orig_colors;
1899 if (!do_comments(gf, imgs[0])) {
1900 i_mempool_destroy(&mp);
1901 quant->mc_colors = orig_colors;
1907 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1909 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1910 interlace, map) == GIF_ERROR) {
1911 i_mempool_destroy(&mp);
1912 quant->mc_colors = orig_colors;
1914 i_push_error(0, "Could not save image descriptor");
1916 mm_log((1, "Error in EGifPutImageDesc."));
1922 if (!do_write(gf, interlace, imgs[0], result)) {
1923 i_mempool_destroy(&mp);
1924 quant->mc_colors = orig_colors;
1931 /* that first awful image is out of the way, do the rest */
1932 for (imgn = 1; imgn < count; ++imgn) {
1933 if (localmaps[imgn]) {
1934 quant->mc_colors = orig_colors;
1935 quant->mc_count = orig_count;
1936 quant->mc_size = orig_size;
1938 want_trans = quant->transp != tr_none
1939 && imgs[imgn]->channels == 4;
1940 /* if the caller gives us too many colours we can't do transparency */
1941 if (want_trans && quant->mc_count == 256)
1943 /* if they want transparency but give us a big size, make it smaller
1944 to give room for a transparency colour */
1945 if (want_trans && quant->mc_size == 256)
1948 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1949 result = quant_paletted(quant, imgs[imgn]);
1952 i_quant_makemap(quant, imgs+imgn, 1);
1953 result = i_quant_translate(quant, imgs[imgn]);
1956 i_mempool_destroy(&mp);
1957 quant->mc_colors = orig_colors;
1959 mm_log((1, "error in i_quant_translate()"));
1963 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1964 trans_index = quant->mc_count;
1967 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1968 i_mempool_destroy(&mp);
1969 quant->mc_colors = orig_colors;
1972 mm_log((1, "Error in MakeMapObject."));
1977 quant->mc_colors = glob_colors;
1978 quant->mc_count = glob_color_count;
1980 result = quant_paletted(quant, imgs[imgn]);
1982 result = i_quant_translate(quant, imgs[imgn]);
1983 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1985 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1986 trans_index = quant->mc_count;
1991 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1992 i_mempool_destroy(&mp);
1993 quant->mc_colors = orig_colors;
1999 if (!do_comments(gf, imgs[imgn])) {
2000 i_mempool_destroy(&mp);
2001 quant->mc_colors = orig_colors;
2007 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2009 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2012 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2014 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
2015 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2016 i_mempool_destroy(&mp);
2017 quant->mc_colors = orig_colors;
2019 i_push_error(0, "Could not save image descriptor");
2024 mm_log((1, "Error in EGifPutImageDesc."));
2030 if (!do_write(gf, interlace, imgs[imgn], result)) {
2031 i_mempool_destroy(&mp);
2032 quant->mc_colors = orig_colors;
2040 if (EGifCloseFile(gf) == GIF_ERROR) {
2041 i_mempool_destroy(&mp);
2043 i_push_error(0, "Could not close GIF file");
2044 mm_log((1, "Error in EGifCloseFile\n"));
2049 for (i = 0; i < glob_color_count; ++i)
2050 orig_colors[i] = glob_colors[i];
2053 i_mempool_destroy(&mp);
2054 quant->mc_colors = orig_colors;
2060 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2062 General high-level function to write a GIF to a file.
2064 Writes the GIF images to the specified file handle using the options
2065 in quant and opts. See L<image.h/i_quantize> and
2066 L<image.h/i_gif_opts>.
2068 Returns non-zero on success.
2074 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2078 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2079 quant, fd, imgs, count));
2081 gif_set_version(quant, imgs, count);
2083 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2085 i_push_error(0, "Cannot create GIF file object");
2086 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2090 return i_writegif_low(quant, gf, imgs, count);
2093 #if IM_GIFMAJOR >= 4
2096 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2098 Internal. Wrapper for the user write callback function.
2103 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2105 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2107 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2113 =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)
2115 General high-level function to write a GIF using callbacks to send
2118 Returns non-zero on success.
2124 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2125 int maxlength, i_img **imgs, int count)
2127 #if IM_GIFMAJOR >= 4
2129 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2134 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2135 quant, cb, userdata, maxlength, imgs, count));
2137 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2139 i_push_error(0, "Cannot create GIF file object");
2140 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2141 i_free_gen_write_data(gwd, 0);
2145 result = i_writegif_low(quant, gf, imgs, count);
2146 return i_free_gen_write_data(gwd, result);
2149 i_push_error(0, "callbacks not supported with giflib3");
2155 #if IM_GIFMAJOR >= 4
2158 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2159 io_glue *ig = (io_glue *)gft->UserData;
2161 return ig->writecb(ig, data, length);
2167 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2172 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2174 io_glue_commit_types(ig);
2176 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2177 int fd = dup(ig->source.fdseek.fd);
2179 i_push_error(errno, "dup() failed");
2182 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2183 is called - dup it so the caller's fd isn't closed */
2184 return i_writegif_gen(quant, fd, imgs, count);
2187 #if IM_GIFMAJOR >= 4
2188 GifFileType *GifFile;
2193 gif_set_version(quant, imgs, count);
2195 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2197 i_push_error(0, "Cannot create giflib callback object");
2198 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2202 result = i_writegif_low(quant, GifFile, imgs, count);
2209 i_push_error(0, "callbacks not supported with giflib3");
2217 =item gif_error_msg(int code)
2219 Grabs the most recent giflib error code from GifLastError() and
2220 returns a string that describes that error.
2222 The returned pointer points to a static buffer, either from a literal
2223 C string or a static buffer.
2228 static char const *gif_error_msg(int code) {
2229 static char msg[80];
2232 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2233 return "Failed to open given file";
2235 case E_GIF_ERR_WRITE_FAILED:
2236 return "Write failed";
2238 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2239 return "Screen descriptor already passed to giflib";
2241 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2242 return "Image descriptor already passed to giflib";
2244 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2245 return "Neither global nor local color map set";
2247 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2248 return "Too much pixel data passed to giflib";
2250 case E_GIF_ERR_NOT_ENOUGH_MEM:
2251 return "Out of memory";
2253 case E_GIF_ERR_DISK_IS_FULL:
2254 return "Disk is full";
2256 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2257 return "File close failed";
2259 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2260 return "File not writable";
2262 case D_GIF_ERR_OPEN_FAILED:
2263 return "Failed to open file";
2265 case D_GIF_ERR_READ_FAILED:
2266 return "Failed to read from file";
2268 case D_GIF_ERR_NOT_GIF_FILE:
2269 return "File is not a GIF file";
2271 case D_GIF_ERR_NO_SCRN_DSCR:
2272 return "No screen descriptor detected - invalid file";
2274 case D_GIF_ERR_NO_IMAG_DSCR:
2275 return "No image descriptor detected - invalid file";
2277 case D_GIF_ERR_NO_COLOR_MAP:
2278 return "No global or local color map found";
2280 case D_GIF_ERR_WRONG_RECORD:
2281 return "Wrong record type detected - invalid file?";
2283 case D_GIF_ERR_DATA_TOO_BIG:
2284 return "Data in file too big for image";
2286 case D_GIF_ERR_NOT_ENOUGH_MEM:
2287 return "Out of memory";
2289 case D_GIF_ERR_CLOSE_FAILED:
2290 return "Close failed";
2292 case D_GIF_ERR_NOT_READABLE:
2293 return "File not opened for read";
2295 case D_GIF_ERR_IMAGE_DEFECT:
2296 return "Defective image";
2298 case D_GIF_ERR_EOF_TOO_SOON:
2299 return "Unexpected EOF - invalid file";
2302 sprintf(msg, "Unknown giflib error code %d", code);
2308 =item gif_push_error()
2310 Utility function that takes the current GIF error code, converts it to
2311 an error message and pushes it on the error stack.
2316 static void gif_push_error(void) {
2317 int code = GifLastError(); /* clears saved error */
2319 i_push_error(code, gif_error_msg(code));
2325 The Netscape loop extension isn't implemented. Giflib's extension
2326 writing code doesn't seem to support writing named extensions in this
2329 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2330 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2333 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2334 supplied interface in giflib 4.1.0 causes a SEGV in
2335 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2339 Arnar M. Hrafnkelsson, addi@umich.edu