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; /* transparent index if we see a GCE */
523 int gif_delay; /* delay from a GCE */
524 int user_input; /* user input flag from a GCE */
525 int disposal; /* 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[0] & 2) != 0;
752 disposal = (Extension[0] >> 2) & 3;
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 The code for this function is currently "#if 0"ed out since the giflib
1373 extension writing code currently doesn't seem to support writing
1374 application extension blocks.
1378 static int do_ns_loop(GifFileType *gf, i_img *img)
1380 /* EGifPutExtension() doesn't appear to handle application
1381 extension blocks in any way
1382 Since giflib wraps the fd with a FILE * (and puts that in its
1383 private data), we can't do an end-run and write the data
1385 There's no open interface that takes a FILE * either, so we
1386 can't workaround it that way either.
1387 If giflib's callback interface wasn't broken by default, I'd
1388 force file writes to use callbacks, but it is broken by default.
1391 /* yes this was another attempt at supporting the loop extension */
1393 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1394 unsigned char nsle[12] = "NETSCAPE2.0";
1395 unsigned char subblock[3];
1396 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1398 i_push_error(0, "writing loop extension");
1402 subblock[1] = loop_count % 256;
1403 subblock[2] = loop_count / 256;
1404 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1406 i_push_error(0, "writing loop extention sub-block");
1409 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1411 i_push_error(0, "writing loop extension terminator");
1420 =item make_gif_map(i_quantize *quant, int want_trans)
1422 Create a giflib color map object from an Imager color map.
1427 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1429 GifColorType colors[256];
1431 int size = quant->mc_count;
1433 ColorMapObject *map;
1436 for (i = 0; i < quant->mc_count; ++i) {
1437 colors[i].Red = quant->mc_colors[i].rgb.r;
1438 colors[i].Green = quant->mc_colors[i].rgb.g;
1439 colors[i].Blue = quant->mc_colors[i].rgb.b;
1442 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1443 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1444 colors[size].Red = trans.rgb.r;
1445 colors[size].Green = trans.rgb.g;
1446 colors[size].Blue = trans.rgb.b;
1450 while (map_size < size)
1452 /* giflib spews for 1 colour maps, reasonable, I suppose */
1455 while (i < map_size) {
1456 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1460 map = MakeMapObject(map_size, colors);
1461 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1464 i_push_error(0, "Could not create color map object");
1471 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1473 We need to call EGifSetGifVersion() before opening the file - put that
1476 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1477 giflib 4.1.0 has code:
1479 static char *GifVersionPrefix = GIF87_STAMP;
1481 and the code that sets the version internally does:
1483 strncpy(&GifVersionPrefix[3], Version, 3);
1485 which is very broken.
1487 Failing to set the correct GIF version doesn't seem to cause a problem
1493 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1494 /* the following crashed giflib
1495 the EGifSetGifVersion() is seriously borked in giflib
1496 it's less borked in the ungiflib beta, but we don't have a mechanism
1498 Needs to be updated to support tags.
1499 if (opts->delay_count
1500 || opts->user_input_count
1501 || opts->disposal_count
1503 || quant->transp != tr_none)
1504 EGifSetGifVersion("89a");
1506 EGifSetGifVersion("87a");
1511 in_palette(i_color *c, i_quantize *quant, int size) {
1514 for (i = 0; i < size; ++i) {
1515 if (c->channel[0] == quant->mc_colors[i].channel[0]
1516 && c->channel[1] == quant->mc_colors[i].channel[1]
1517 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1526 =item has_common_palette(imgs, count, quant, want_trans)
1528 Tests if all the given images are paletted and have a common palette,
1529 if they do it builds that palette.
1531 A possible improvement might be to eliminate unused colors in the
1537 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1539 int size = quant->mc_count;
1544 /* we try to build a common palette here, if we can manage that, then
1545 that's the palette we use */
1546 for (imgn = 0; imgn < count; ++imgn) {
1547 int eliminate_unused;
1548 if (imgs[imgn]->type != i_palette_type)
1551 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1552 &eliminate_unused)) {
1553 eliminate_unused = 1;
1556 if (eliminate_unused) {
1557 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1559 memset(used, 0, sizeof(used));
1561 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1562 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1563 for (x = 0; x < imgs[imgn]->xsize; ++x)
1570 /* assume all are in use */
1571 memset(used, 1, sizeof(used));
1574 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1577 i_getcolors(imgs[imgn], i, &c, 1);
1579 if (in_palette(&c, quant, size) < 0) {
1580 if (size < quant->mc_size) {
1581 quant->mc_colors[size++] = c;
1584 /* oops, too many colors */
1592 quant->mc_count = size;
1598 quant_paletted(i_quantize *quant, i_img *img) {
1599 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1601 i_palidx trans[256];
1605 /* build a translation table */
1606 for (i = 0; i < i_colorcount(img); ++i) {
1608 i_getcolors(img, i, &c, 1);
1609 trans[i] = in_palette(&c, quant, quant->mc_count);
1612 for (y = 0; y < img->ysize; ++y) {
1613 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1614 for (x = 0; x < img->xsize; ++x) {
1624 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1626 Internal. Low-level function that does the high-level GIF processing
1629 Returns non-zero on success.
1635 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1636 unsigned char *result;
1638 ColorMapObject *map;
1639 int scrw = 0, scrh = 0;
1640 int imgn, orig_count, orig_size;
1646 i_img **glob_imgs; /* images that will use the global color map */
1648 i_color *orig_colors = quant->mc_colors;
1649 i_color *glob_colors = NULL;
1650 int glob_color_count;
1651 int glob_want_trans;
1652 int glob_paletted; /* the global map was made from the image palettes */
1653 int colors_paletted;
1658 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1659 quant, gf, imgs, count));
1661 /* *((char *)0) = 1; */ /* used to break into the debugger */
1664 i_push_error(0, "No images provided to write");
1665 return 0; /* what are you smoking? */
1668 i_mempool_init(&mp);
1670 /* sanity is nice */
1671 if (quant->mc_size > 256)
1672 quant->mc_size = 256;
1673 if (quant->mc_count > quant->mc_size)
1674 quant->mc_count = quant->mc_size;
1676 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1678 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1682 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1683 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1685 glob_want_trans = 0;
1686 for (imgn = 0; imgn < count; ++imgn) {
1688 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1689 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1690 if (imgs[imgn]->xsize + posx > scrw)
1691 scrw = imgs[imgn]->xsize + posx;
1692 if (imgs[imgn]->ysize + posy > scrh)
1693 scrh = imgs[imgn]->ysize + posy;
1694 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1695 localmaps[imgn] = 0;
1696 if (localmaps[imgn])
1699 if (imgs[imgn]->channels == 4) {
1700 glob_want_trans = 1;
1702 glob_imgs[glob_img_count++] = imgs[imgn];
1705 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1707 orig_count = quant->mc_count;
1708 orig_size = quant->mc_size;
1710 if (glob_img_count) {
1712 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1713 quant->mc_colors = glob_colors;
1714 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1715 /* we have some images that want to use the global map */
1716 if (glob_want_trans && quant->mc_count == 256) {
1717 mm_log((2, " disabling transparency for global map - no space\n"));
1718 glob_want_trans = 0;
1720 if (glob_want_trans && quant->mc_size == 256) {
1721 mm_log((2, " reserving color for transparency\n"));
1724 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1729 i_quant_makemap(quant, glob_imgs, glob_img_count);
1731 glob_color_count = quant->mc_count;
1732 quant->mc_colors = orig_colors;
1735 /* use the global map if we have one, otherwise use the local map */
1738 quant->mc_colors = glob_colors;
1739 quant->mc_count = glob_color_count;
1740 want_trans = glob_want_trans && imgs[0]->channels == 4;
1742 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1744 if (gif_background < 0)
1746 if (gif_background >= glob_color_count)
1750 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1751 if (has_common_palette(imgs, 1, quant, want_trans)) {
1752 colors_paletted = 1;
1755 colors_paletted = 0;
1756 i_quant_makemap(quant, imgs, 1);
1759 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1760 i_mempool_destroy(&mp);
1761 quant->mc_colors = orig_colors;
1763 mm_log((1, "Error in MakeMapObject"));
1768 /* since we don't know how big some the local palettes could be
1769 we need to base the bits on the maximum number of colors */
1770 while (orig_size > (1 << color_bits))
1774 int count = quant->mc_count;
1777 while (count > (1 << color_bits))
1781 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1782 gif_background, map) == GIF_ERROR) {
1783 i_mempool_destroy(&mp);
1784 quant->mc_colors = orig_colors;
1786 i_push_error(0, "Could not save screen descriptor");
1790 mm_log((1, "Error in EGifPutScreenDesc."));
1795 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1797 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1800 if (!localmaps[0]) {
1802 colors_paletted = glob_paletted;
1805 /* if this image has a global map the colors in quant don't
1806 belong to this image, so build a palette */
1808 /* generate the local map for this image */
1809 quant->mc_colors = orig_colors;
1810 quant->mc_size = orig_size;
1811 quant->mc_count = orig_count;
1812 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1814 /* if the caller gives us too many colours we can't do transparency */
1815 if (want_trans && quant->mc_count == 256)
1817 /* if they want transparency but give us a big size, make it smaller
1818 to give room for a transparency colour */
1819 if (want_trans && quant->mc_size == 256)
1821 if (has_common_palette(imgs, 1, quant, want_trans)) {
1822 colors_paletted = 1;
1825 colors_paletted = 0;
1826 i_quant_makemap(quant, imgs, 1);
1828 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1829 i_mempool_destroy(&mp);
1831 quant->mc_colors = orig_colors;
1832 mm_log((1, "Error in MakeMapObject"));
1837 /* the map we wrote was the map for this image - don't set the local
1843 if (colors_paletted)
1844 result = quant_paletted(quant, imgs[0]);
1846 result = i_quant_translate(quant, imgs[0]);
1848 i_mempool_destroy(&mp);
1849 quant->mc_colors = orig_colors;
1854 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1855 trans_index = quant->mc_count;
1858 if (!do_ns_loop(gf, imgs[0])) {
1859 i_mempool_destroy(&mp);
1860 quant->mc_colors = orig_colors;
1864 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1865 i_mempool_destroy(&mp);
1866 quant->mc_colors = orig_colors;
1872 if (!do_comments(gf, imgs[0])) {
1873 i_mempool_destroy(&mp);
1874 quant->mc_colors = orig_colors;
1880 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1882 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1883 interlace, map) == GIF_ERROR) {
1884 i_mempool_destroy(&mp);
1885 quant->mc_colors = orig_colors;
1887 i_push_error(0, "Could not save image descriptor");
1889 mm_log((1, "Error in EGifPutImageDesc."));
1895 if (!do_write(gf, interlace, imgs[0], result)) {
1896 i_mempool_destroy(&mp);
1897 quant->mc_colors = orig_colors;
1904 /* that first awful image is out of the way, do the rest */
1905 for (imgn = 1; imgn < count; ++imgn) {
1906 if (localmaps[imgn]) {
1907 quant->mc_colors = orig_colors;
1908 quant->mc_count = orig_count;
1909 quant->mc_size = orig_size;
1911 want_trans = quant->transp != tr_none
1912 && imgs[imgn]->channels == 4;
1913 /* if the caller gives us too many colours we can't do transparency */
1914 if (want_trans && quant->mc_count == 256)
1916 /* if they want transparency but give us a big size, make it smaller
1917 to give room for a transparency colour */
1918 if (want_trans && quant->mc_size == 256)
1921 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1922 result = quant_paletted(quant, imgs[imgn]);
1925 i_quant_makemap(quant, imgs+imgn, 1);
1926 result = i_quant_translate(quant, imgs[imgn]);
1929 i_mempool_destroy(&mp);
1930 quant->mc_colors = orig_colors;
1932 mm_log((1, "error in i_quant_translate()"));
1936 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1937 trans_index = quant->mc_count;
1940 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1941 i_mempool_destroy(&mp);
1942 quant->mc_colors = orig_colors;
1945 mm_log((1, "Error in MakeMapObject."));
1950 quant->mc_colors = glob_colors;
1951 quant->mc_count = glob_color_count;
1953 result = quant_paletted(quant, imgs[imgn]);
1955 result = i_quant_translate(quant, imgs[imgn]);
1956 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1958 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1959 trans_index = quant->mc_count;
1964 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1965 i_mempool_destroy(&mp);
1966 quant->mc_colors = orig_colors;
1972 if (!do_comments(gf, imgs[imgn])) {
1973 i_mempool_destroy(&mp);
1974 quant->mc_colors = orig_colors;
1980 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1982 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1985 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1987 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1988 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1989 i_mempool_destroy(&mp);
1990 quant->mc_colors = orig_colors;
1992 i_push_error(0, "Could not save image descriptor");
1997 mm_log((1, "Error in EGifPutImageDesc."));
2003 if (!do_write(gf, interlace, imgs[imgn], result)) {
2004 i_mempool_destroy(&mp);
2005 quant->mc_colors = orig_colors;
2013 if (EGifCloseFile(gf) == GIF_ERROR) {
2014 i_mempool_destroy(&mp);
2016 i_push_error(0, "Could not close GIF file");
2017 mm_log((1, "Error in EGifCloseFile\n"));
2022 for (i = 0; i < glob_color_count; ++i)
2023 orig_colors[i] = glob_colors[i];
2026 i_mempool_destroy(&mp);
2027 quant->mc_colors = orig_colors;
2033 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2035 General high-level function to write a GIF to a file.
2037 Writes the GIF images to the specified file handle using the options
2038 in quant and opts. See L<image.h/i_quantize> and
2039 L<image.h/i_gif_opts>.
2041 Returns non-zero on success.
2047 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2051 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2052 quant, fd, imgs, count));
2054 gif_set_version(quant, imgs, count);
2056 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2058 i_push_error(0, "Cannot create GIF file object");
2059 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2063 return i_writegif_low(quant, gf, imgs, count);
2066 #if IM_GIFMAJOR >= 4
2069 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2071 Internal. Wrapper for the user write callback function.
2076 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2078 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2080 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2086 =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)
2088 General high-level function to write a GIF using callbacks to send
2091 Returns non-zero on success.
2097 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2098 int maxlength, i_img **imgs, int count)
2100 #if IM_GIFMAJOR >= 4
2102 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2107 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2108 quant, cb, userdata, maxlength, imgs, count));
2110 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2112 i_push_error(0, "Cannot create GIF file object");
2113 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2114 i_free_gen_write_data(gwd, 0);
2118 result = i_writegif_low(quant, gf, imgs, count);
2119 return i_free_gen_write_data(gwd, result);
2122 i_push_error(0, "callbacks not supported with giflib3");
2128 #if IM_GIFMAJOR >= 4
2131 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2132 io_glue *ig = (io_glue *)gft->UserData;
2134 return ig->writecb(ig, data, length);
2140 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2145 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2147 io_glue_commit_types(ig);
2149 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2150 int fd = dup(ig->source.fdseek.fd);
2152 i_push_error(errno, "dup() failed");
2155 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2156 is called - dup it so the caller's fd isn't closed */
2157 return i_writegif_gen(quant, fd, imgs, count);
2160 #if IM_GIFMAJOR >= 4
2161 GifFileType *GifFile;
2166 gif_set_version(quant, imgs, count);
2168 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2170 i_push_error(0, "Cannot create giflib callback object");
2171 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2175 result = i_writegif_low(quant, GifFile, imgs, count);
2182 i_push_error(0, "callbacks not supported with giflib3");
2190 =item gif_error_msg(int code)
2192 Grabs the most recent giflib error code from GifLastError() and
2193 returns a string that describes that error.
2195 The returned pointer points to a static buffer, either from a literal
2196 C string or a static buffer.
2201 static char const *gif_error_msg(int code) {
2202 static char msg[80];
2205 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2206 return "Failed to open given file";
2208 case E_GIF_ERR_WRITE_FAILED:
2209 return "Write failed";
2211 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2212 return "Screen descriptor already passed to giflib";
2214 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2215 return "Image descriptor already passed to giflib";
2217 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2218 return "Neither global nor local color map set";
2220 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2221 return "Too much pixel data passed to giflib";
2223 case E_GIF_ERR_NOT_ENOUGH_MEM:
2224 return "Out of memory";
2226 case E_GIF_ERR_DISK_IS_FULL:
2227 return "Disk is full";
2229 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2230 return "File close failed";
2232 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2233 return "File not writable";
2235 case D_GIF_ERR_OPEN_FAILED:
2236 return "Failed to open file";
2238 case D_GIF_ERR_READ_FAILED:
2239 return "Failed to read from file";
2241 case D_GIF_ERR_NOT_GIF_FILE:
2242 return "File is not a GIF file";
2244 case D_GIF_ERR_NO_SCRN_DSCR:
2245 return "No screen descriptor detected - invalid file";
2247 case D_GIF_ERR_NO_IMAG_DSCR:
2248 return "No image descriptor detected - invalid file";
2250 case D_GIF_ERR_NO_COLOR_MAP:
2251 return "No global or local color map found";
2253 case D_GIF_ERR_WRONG_RECORD:
2254 return "Wrong record type detected - invalid file?";
2256 case D_GIF_ERR_DATA_TOO_BIG:
2257 return "Data in file too big for image";
2259 case D_GIF_ERR_NOT_ENOUGH_MEM:
2260 return "Out of memory";
2262 case D_GIF_ERR_CLOSE_FAILED:
2263 return "Close failed";
2265 case D_GIF_ERR_NOT_READABLE:
2266 return "File not opened for read";
2268 case D_GIF_ERR_IMAGE_DEFECT:
2269 return "Defective image";
2271 case D_GIF_ERR_EOF_TOO_SOON:
2272 return "Unexpected EOF - invalid file";
2275 sprintf(msg, "Unknown giflib error code %d", code);
2281 =item gif_push_error()
2283 Utility function that takes the current GIF error code, converts it to
2284 an error message and pushes it on the error stack.
2289 static void gif_push_error(void) {
2290 int code = GifLastError(); /* clears saved error */
2292 i_push_error(code, gif_error_msg(code));
2298 The Netscape loop extension isn't implemented. Giflib's extension
2299 writing code doesn't seem to support writing named extensions in this
2302 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2303 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2306 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2307 supplied interface in giflib 4.1.0 causes a SEGV in
2308 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2312 Arnar M. Hrafnkelsson, addi@umich.edu