9 /* XXX: Reading still needs to support reading all those gif properties */
14 gif.c - read and write gif files for Imager
23 int max_colours; // number of bits per colour
24 int pixdev; // how much noise to add
25 i_color fixed[N]; // fixed palette entries
26 int fixedlen; // number of fixed colours
27 int success; // non-zero on success
28 char *data; // a GIF file in memory
29 int length; // how big data is
30 int reader(char *, char *, int, int);
31 int writer(char *, char *, int);
32 char *userdata; // user's data, whatever it is
36 img = i_readgif(fd, &colour_table, &colours);
37 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
38 success = i_writegifmc(img, fd, max_colours);
39 img = i_readgif_scalar(data, length, &colour_table, &colours);
40 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
41 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
42 success = i_writegif_callback(&quant, writer, userdata, maxlength,
47 This source file provides the C level interface to reading and writing
50 This has been tested with giflib 3 and 4, though you lose the callback
51 functionality with giflib3.
60 static char const *gif_error_msg(int code);
61 static void gif_push_error(void);
65 static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
70 Internal. A structure passed to the reader function used for reading
73 Used with giflib 4 and later.
78 struct gif_scalar_info {
85 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
87 Internal. The reader callback passed to giflib.
89 Used with giflib 4 and later.
95 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
96 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
97 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
99 if (gsi->cpos == gsi->length) return 0;
100 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
101 memcpy(buf,gsi->data+gsi->cpos,length);
110 /* Make some variables global, so we could access them faster: */
113 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
114 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
120 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
121 GifColorType *mapentry;
123 int colourmapsize = colourmap->ColorCount;
125 if(colours) *colours = colourmapsize;
126 if(!colour_table) return;
128 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
129 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
131 for(q=0; q<colourmapsize; q++) {
132 mapentry = &colourmap->Colors[q];
133 (*colour_table)[q*3 + 0] = mapentry->Red;
134 (*colour_table)[q*3 + 1] = mapentry->Green;
135 (*colour_table)[q*3 + 2] = mapentry->Blue;
141 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
143 Internal. Low-level function for reading a GIF file. The caller must
144 create the appropriate GifFileType object and pass it in.
150 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
152 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
153 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
154 ColorMapObject *ColorMap;
156 GifRecordType RecordType;
157 GifByteType *Extension;
160 static GifColorType *ColorMapEntry;
163 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
165 /* it's possible that the caller has called us with *colour_table being
166 non-NULL, but we check that to see if we need to free an allocated
167 colour table on error.
169 if (colour_table) *colour_table = NULL;
171 BackGround = GifFile->SBackGroundColor;
172 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
175 ColorMapSize = ColorMap->ColorCount;
176 i_colortable_copy(colour_table, colours, ColorMap);
180 if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
181 if (colour_table && *colour_table) {
182 myfree(*colour_table);
183 *colour_table = NULL;
185 DGifCloseFile(GifFile);
186 mm_log((1, "i_readgif: image size exceeds limits\n"));
190 im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
192 if (colour_table && *colour_table) {
193 myfree(*colour_table);
194 *colour_table = NULL;
196 DGifCloseFile(GifFile);
200 Size = GifFile->SWidth * sizeof(GifPixelType);
202 GifRow = mymalloc(Size);
204 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
206 /* Scan the content of the GIF file and load the image(s) in: */
208 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
210 i_push_error(0, "Unable to get record type");
211 if (colour_table && *colour_table) {
212 myfree(*colour_table);
213 *colour_table = NULL;
217 DGifCloseFile(GifFile);
221 switch (RecordType) {
222 case IMAGE_DESC_RECORD_TYPE:
223 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
225 i_push_error(0, "Unable to get image descriptor");
226 if (colour_table && *colour_table) {
227 myfree(*colour_table);
228 *colour_table = NULL;
232 DGifCloseFile(GifFile);
236 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
237 mm_log((1, "Adding local colormap\n"));
238 ColorMapSize = ColorMap->ColorCount;
240 i_colortable_copy(colour_table, colours, ColorMap);
244 /* No colormap and we are about to read in the image - abandon for now */
245 mm_log((1, "Going in with no colormap\n"));
246 i_push_error(0, "Image does not have a local or a global color map");
247 /* we can't have allocated a colour table here */
250 DGifCloseFile(GifFile);
254 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
255 Col = GifFile->Image.Left;
256 Width = GifFile->Image.Width;
257 Height = GifFile->Image.Height;
259 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
261 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
262 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
263 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
264 if (colour_table && *colour_table) {
265 myfree(*colour_table);
266 *colour_table = NULL;
270 DGifCloseFile(GifFile);
273 if (GifFile->Image.Interlace) {
275 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
277 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
279 i_push_error(0, "Reading GIF line");
280 if (colour_table && *colour_table) {
281 myfree(*colour_table);
282 *colour_table = NULL;
286 DGifCloseFile(GifFile);
290 for (x = 0; x < Width; x++) {
291 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
292 col.rgb.r = ColorMapEntry->Red;
293 col.rgb.g = ColorMapEntry->Green;
294 col.rgb.b = ColorMapEntry->Blue;
295 i_ppix(im,Col+x,j,&col);
301 for (i = 0; i < Height; i++) {
302 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
304 i_push_error(0, "Reading GIF line");
305 if (colour_table && *colour_table) {
306 myfree(*colour_table);
307 *colour_table = NULL;
311 DGifCloseFile(GifFile);
315 for (x = 0; x < Width; x++) {
316 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
317 col.rgb.r = ColorMapEntry->Red;
318 col.rgb.g = ColorMapEntry->Green;
319 col.rgb.b = ColorMapEntry->Blue;
320 i_ppix(im, Col+x, Row, &col);
326 case EXTENSION_RECORD_TYPE:
327 /* Skip any extension blocks in file: */
328 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
330 i_push_error(0, "Reading extension record");
331 if (colour_table && *colour_table) {
332 myfree(*colour_table);
333 *colour_table = NULL;
337 DGifCloseFile(GifFile);
340 while (Extension != NULL) {
341 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
343 i_push_error(0, "reading next block of extension");
344 if (colour_table && *colour_table) {
345 myfree(*colour_table);
346 *colour_table = NULL;
350 DGifCloseFile(GifFile);
355 case TERMINATE_RECORD_TYPE:
357 default: /* Should be traps by DGifGetRecordType. */
360 } while (RecordType != TERMINATE_RECORD_TYPE);
364 if (DGifCloseFile(GifFile) == GIF_ERROR) {
366 i_push_error(0, "Closing GIF file object");
367 if (colour_table && *colour_table) {
368 myfree(*colour_table);
369 *colour_table = NULL;
375 i_tags_add(&im->tags, "i_format", 0, "gif", -1, 0);
381 =item i_readgif(int fd, int **colour_table, int *colours)
383 Reads in a GIF file from a file handle and converts it to an Imager
386 Returns the palette for the object in colour_table for colours
389 Returns NULL on failure.
395 i_readgif(int fd, int **colour_table, int *colours) {
396 GifFileType *GifFile;
400 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
402 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
404 i_push_error(0, "Cannot create giflib file object");
405 mm_log((1,"i_readgif: Unable to open file\n"));
409 return i_readgif_low(GifFile, colour_table, colours);
414 Internal function called by i_readgif_multi_low() in error handling
417 static void free_images(i_img **imgs, int count) {
421 for (i = 0; i < count; ++i)
422 i_img_destroy(imgs[i]);
428 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
430 Reads one of more gif images from the given GIF file.
432 Returns a pointer to an array of i_img *, and puts the count into
435 If page is not -1 then the given image _only_ is returned from the
436 file, where the first image is 0, the second 1 and so on.
438 Unlike the normal i_readgif*() functions the images are paletted
439 images rather than a combined RGB image.
441 This functions sets tags on the images returned:
447 the offset of the image from the left of the "screen" ("Image Left
452 the offset of the image from the top of the "screen" ("Image Top Position")
456 non-zero if the image was interlaced ("Interlace Flag")
458 =item gif_screen_width
460 =item gif_screen_height
462 the size of the logical screen ("Logical Screen Width",
463 "Logical Screen Height")
467 Non-zero if this image had a local color map.
471 The index in the global colormap of the logical screen's background
472 color. This is only set if the current image uses the global
475 =item gif_trans_index
477 The index of the color in the colormap used for transparency. If the
478 image has a transparency then it is returned as a 4 channel image with
479 the alpha set to zero in this palette entry. ("Transparent Color Index")
483 The delay until the next frame is displayed, in 1/100 of a second.
488 whether or not a user input is expected before continuing (view dependent)
493 how the next frame is displayed ("Disposal Method")
497 the number of loops from the Netscape Loop extension. This may be zero.
501 the first block of the first gif comment before each image.
505 Where applicable, the ("name") is the name of that field from the GIF89
511 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
513 int i, j, Size, Width, Height, ExtCode, Count;
514 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
515 ColorMapObject *ColorMap;
517 GifRecordType RecordType;
518 GifByteType *Extension;
522 int trans_index = 0; /* transparent index if we see a GCE */
523 int gif_delay = 0; /* delay from a GCE */
524 int user_input = 0; /* user input flag from a GCE */
525 int disposal = 0; /* disposal method from a GCE */
528 char *comment = NULL; /* a comment */
529 i_img **results = NULL;
530 int result_alloc = 0;
532 int image_colors = 0;
533 i_color black = { 0 }; /* used to expand the palette if needed */
537 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
539 BackGround = GifFile->SBackGroundColor;
541 Size = GifFile->SWidth * sizeof(GifPixelType);
543 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
544 i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
546 /* Scan the content of the GIF file and load the image(s) in: */
548 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
550 i_push_error(0, "Unable to get record type");
551 free_images(results, *count);
552 DGifCloseFile(GifFile);
557 switch (RecordType) {
558 case IMAGE_DESC_RECORD_TYPE:
559 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
561 i_push_error(0, "Unable to get image descriptor");
562 free_images(results, *count);
563 DGifCloseFile(GifFile);
568 Width = GifFile->Image.Width;
569 Height = GifFile->Image.Height;
570 if (page == -1 || page == ImageNum) {
571 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
572 mm_log((1, "Adding local colormap\n"));
573 ColorMapSize = ColorMap->ColorCount;
575 /* No colormap and we are about to read in the image -
577 mm_log((1, "Going in with no colormap\n"));
578 i_push_error(0, "Image does not have a local or a global color map");
579 free_images(results, *count);
580 DGifCloseFile(GifFile);
586 if (got_gce && trans_index >= 0)
588 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
589 free_images(results, *count);
590 mm_log((1, "i_readgif: image size exceeds limits\n"));
591 DGifCloseFile(GifFile);
595 img = i_img_pal_new(Width, Height, channels, 256);
597 free_images(results, *count);
598 DGifCloseFile(GifFile);
601 /* populate the palette of the new image */
602 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
603 for (i = 0; i < ColorMapSize; ++i) {
605 col.rgba.r = ColorMap->Colors[i].Red;
606 col.rgba.g = ColorMap->Colors[i].Green;
607 col.rgba.b = ColorMap->Colors[i].Blue;
608 if (channels == 4 && trans_index == i)
613 i_addcolors(img, &col, 1);
615 image_colors = ColorMapSize;
617 if (*count > result_alloc) {
618 if (result_alloc == 0) {
620 results = mymalloc(result_alloc * sizeof(i_img *));
623 /* myrealloc never fails (it just dies if it can't allocate) */
625 results = myrealloc(results, result_alloc * sizeof(i_img *));
628 results[*count-1] = img;
629 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
630 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
632 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
633 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
634 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
635 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
636 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
637 i_tags_addn(&img->tags, "gif_background", 0,
638 GifFile->SBackGroundColor);
640 if (GifFile->Image.ColorMap) {
641 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
644 if (trans_index >= 0) {
646 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
647 i_getcolors(img, trans_index, &trans, 1);
648 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
650 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
651 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
652 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
656 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
658 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
663 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
664 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
666 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
667 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
668 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
669 free_images(results, *count);
670 DGifCloseFile(GifFile);
675 if (GifFile->Image.Interlace) {
676 for (Count = i = 0; i < 4; i++) {
677 for (j = InterlacedOffset[i]; j < Height;
678 j += InterlacedJumps[i]) {
680 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
682 i_push_error(0, "Reading GIF line");
683 free_images(results, *count);
684 DGifCloseFile(GifFile);
689 /* range check the scanline if needed */
690 if (image_colors != 256) {
692 for (x = 0; x < Width; ++x) {
693 while (GifRow[x] >= image_colors) {
694 /* expand the palette since a palette index is too big */
695 i_addcolors(img, &black, 1);
701 i_ppal(img, 0, Width, j, GifRow);
706 for (i = 0; i < Height; i++) {
707 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
709 i_push_error(0, "Reading GIF line");
710 free_images(results, *count);
711 DGifCloseFile(GifFile);
716 /* range check the scanline if needed */
717 if (image_colors != 256) {
719 for (x = 0; x < Width; ++x) {
720 while (GifRow[x] >= image_colors) {
721 /* expand the palette since a palette index is too big */
722 i_addcolors(img, &black, 1);
728 i_ppal(img, 0, Width, i, GifRow);
732 /* must be only one image wanted and that was it */
735 DGifCloseFile(GifFile);
741 /* whether interlaced or not, it has the same number of lines */
742 /* giflib does't have an interface to skip the image data */
743 for (i = 0; i < Height; i++) {
744 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
746 i_push_error(0, "Reading GIF line");
747 free_images(results, *count);
749 DGifCloseFile(GifFile);
754 /* kill the comment so we get the right comment for the page */
762 case EXTENSION_RECORD_TYPE:
763 /* Skip any extension blocks in file: */
764 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
766 i_push_error(0, "Reading extension record");
767 free_images(results, *count);
768 DGifCloseFile(GifFile);
771 if (ExtCode == 0xF9) {
773 if (Extension[1] & 1)
774 trans_index = Extension[4];
777 gif_delay = Extension[2] + 256 * Extension[3];
778 user_input = (Extension[1] & 2) != 0;
779 disposal = (Extension[1] >> 2) & 7;
781 if (ExtCode == 0xFF && *Extension == 11) {
782 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
783 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
785 i_push_error(0, "reading loop extension");
786 free_images(results, *count);
787 DGifCloseFile(GifFile);
790 if (Extension && *Extension == 3) {
792 ns_loop = Extension[2] + 256 * Extension[3];
796 else if (ExtCode == 0xFE) {
797 /* while it's possible for a GIF file to contain more than one
798 comment, I'm only implementing a single comment per image,
799 with the comment saved into the following image.
800 If someone wants more than that they can implement it.
801 I also don't handle comments that take more than one block.
804 comment = mymalloc(*Extension+1);
805 memcpy(comment, Extension+1, *Extension);
806 comment[*Extension] = '\0';
809 while (Extension != NULL) {
810 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
812 i_push_error(0, "reading next block of extension");
813 free_images(results, *count);
814 DGifCloseFile(GifFile);
819 case TERMINATE_RECORD_TYPE:
821 default: /* Should be trapped by DGifGetRecordType. */
824 } while (RecordType != TERMINATE_RECORD_TYPE);
828 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
836 if (DGifCloseFile(GifFile) == GIF_ERROR) {
838 i_push_error(0, "Closing GIF file object");
839 free_images(results, *count);
843 if (ImageNum && page != -1) {
844 /* there were images, but the page selected wasn't found */
845 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
846 free_images(results, *count);
854 /* giflib declares this incorrectly as EgifOpen */
855 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
857 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
861 =item i_readgif_multi_wiol(ig, int *count)
867 i_readgif_multi_wiol(io_glue *ig, int *count) {
868 io_glue_commit_types(ig);
870 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
871 return i_readgif_multi(ig->source.fdseek.fd, count);
875 GifFileType *GifFile;
879 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
881 i_push_error(0, "Cannot create giflib callback object");
882 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
886 return i_readgif_multi_low(GifFile, count, -1);
889 i_push_error(0, "callbacks not supported with giflib3");
897 =item i_readgif_multi(int fd, int *count)
902 i_readgif_multi(int fd, int *count) {
903 GifFileType *GifFile;
907 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
909 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
911 i_push_error(0, "Cannot create giflib file object");
912 mm_log((1,"i_readgif: Unable to open file\n"));
916 return i_readgif_multi_low(GifFile, count, -1);
920 =item i_readgif_multi_scalar(char *data, int length, int *count)
925 i_readgif_multi_scalar(char *data, int length, int *count) {
927 GifFileType *GifFile;
928 struct gif_scalar_info gsi;
936 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
937 data, length, count));
939 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
941 i_push_error(0, "Cannot create giflib callback object");
942 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
946 return i_readgif_multi_low(GifFile, count, -1);
953 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
955 Read a GIF file into an Imager RGB file, the data of the GIF file is
956 retreived by callin the user supplied callback function.
958 This function is only used with giflib 4 and higher.
964 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
966 GifFileType *GifFile;
969 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
973 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
974 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
976 i_push_error(0, "Cannot create giflib callback object");
977 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
982 result = i_readgif_multi_low(GifFile, count, -1);
983 i_free_gen_read_data(gci);
992 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
994 Write I<img> to the file handle I<fd>. The resulting GIF will use a
995 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
996 colours taken from I<fixed>.
998 Returns non-zero on success.
1004 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
1005 i_color colors[256];
1008 memset(&quant, 0, sizeof(quant));
1009 quant.make_colors = mc_addi;
1010 quant.mc_colors = colors;
1011 quant.mc_size = 1<<max_colors;
1012 quant.mc_count = fixedlen;
1013 memcpy(colors, fixed, fixedlen * sizeof(i_color));
1014 quant.translate = pt_perturb;
1015 quant.perturb = pixdev;
1016 return i_writegif_gen(&quant, fd, &im, 1);
1020 =item i_writegifmc(i_img *im, int fd, int max_colors)
1022 Write I<img> to the file handle I<fd>. The resulting GIF will use a
1023 maximum of 1<<I<max_colours> colours.
1025 Returns non-zero on success.
1031 i_writegifmc(i_img *im, int fd, int max_colors) {
1032 i_color colors[256];
1035 /* *(char *)0 = 1; */
1037 memset(&quant, 0, sizeof(quant));
1038 quant.make_colors = mc_none; /* ignored for pt_giflib */
1039 quant.mc_colors = colors;
1040 quant.mc_size = 1 << max_colors;
1042 quant.translate = pt_giflib;
1043 return i_writegif_gen(&quant, fd, &im, 1);
1048 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1050 Reads a GIF file from an in memory copy of the file. This can be used
1051 if you get the 'file' from some source other than an actual file (or
1052 some other file handle).
1054 This function is only available with giflib 4 and higher.
1059 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1060 #if IM_GIFMAJOR >= 4
1061 GifFileType *GifFile;
1062 struct gif_scalar_info gsi;
1070 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1071 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1073 i_push_error(0, "Cannot create giflib callback object");
1074 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1078 return i_readgif_low(GifFile, colour_table, colours);
1084 #if IM_GIFMAJOR >= 4
1087 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1089 Internal. The reader callback wrapper passed to giflib.
1091 This function is only used with giflib 4 and higher.
1097 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1098 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1105 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1107 Read a GIF file into an Imager RGB file, the data of the GIF file is
1108 retreived by callin the user supplied callback function.
1110 This function is only used with giflib 4 and higher.
1116 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1117 #if IM_GIFMAJOR >= 4
1118 GifFileType *GifFile;
1121 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1125 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1126 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1128 i_push_error(0, "Cannot create giflib callback object");
1129 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1134 result = i_readgif_low(GifFile, colour_table, colours);
1135 i_free_gen_read_data(gci);
1140 i_push_error(0, "callbacks not supported with giflib3");
1146 #if IM_GIFMAJOR >= 4
1149 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1150 io_glue *ig = (io_glue *)gft->UserData;
1152 return ig->readcb(ig, buf, length);
1158 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1159 io_glue_commit_types(ig);
1161 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1162 int fd = dup(ig->source.fdseek.fd);
1164 i_push_error(errno, "dup() failed");
1167 return i_readgif(fd, color_table, colors);
1170 #if IM_GIFMAJOR >= 4
1171 GifFileType *GifFile;
1175 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1177 i_push_error(0, "Cannot create giflib callback object");
1178 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1182 return i_readgif_low(GifFile, color_table, colors);
1186 i_push_error(0, "callbacks not supported with giflib3");
1194 =item i_readgif_single_low(GifFile, page)
1196 Lower level function to read a single image from a GIF.
1198 page must be non-negative.
1203 i_readgif_single_low(GifFileType *GifFile, int page) {
1207 imgs = i_readgif_multi_low(GifFile, &count, page);
1209 if (imgs && count) {
1210 i_img *result = imgs[0];
1216 /* i_readgif_multi_low() handles the errors appropriately */
1222 =item i_readgif_single_wiol(ig, page)
1224 Read a single page from a GIF image file, where the page is indexed
1227 Returns NULL if the page isn't found.
1233 i_readgif_single_wiol(io_glue *ig, int page) {
1234 io_glue_commit_types(ig);
1239 i_push_error(0, "page must be non-negative");
1243 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1244 GifFileType *GifFile;
1245 int fd = dup(ig->source.fdseek.fd);
1247 i_push_error(errno, "dup() failed");
1250 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1252 i_push_error(0, "Cannot create giflib file object");
1253 mm_log((1,"i_readgif: Unable to open file\n"));
1256 return i_readgif_single_low(GifFile, page);
1259 #if IM_GIFMAJOR >= 4
1260 GifFileType *GifFile;
1262 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1264 i_push_error(0, "Cannot create giflib callback object");
1265 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1269 return i_readgif_single_low(GifFile, page);
1271 i_push_error(0, "callbacks not supported with giflib3");
1279 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1281 Internal. Low level image write function. Writes in interlace if
1282 that was requested in the GIF options.
1284 Returns non-zero on success.
1289 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1292 for (i = 0; i < 4; ++i) {
1293 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1294 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1296 i_push_error(0, "Could not save image data:");
1297 mm_log((1, "Error in EGifPutLine\n"));
1306 for (y = 0; y < img->ysize; ++y) {
1307 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1309 i_push_error(0, "Could not save image data:");
1310 mm_log((1, "Error in EGifPutLine\n"));
1322 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1324 Internal. Writes the GIF graphics control extension, if necessary.
1326 Returns non-zero on success.
1330 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1332 unsigned char gce[4] = {0};
1336 int disposal_method;
1340 gce[3] = trans_index;
1343 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1344 gce[1] = delay % 256;
1345 gce[2] = delay / 256;
1348 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1353 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1354 gce[0] |= (disposal_method & 3) << 2;
1358 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1360 i_push_error(0, "Could not save GCE");
1367 =item do_comments(gf, img)
1369 Write any comments in the image.
1373 static int do_comments(GifFileType *gf, i_img *img) {
1376 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1377 if (img->tags.tags[pos].data) {
1378 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1384 sprintf(buf, "%d", img->tags.tags[pos].idata);
1385 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1395 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1397 Internal. Add the Netscape2.0 loop extension block, if requested.
1399 Giflib/libungif prior to 4.1.1 didn't support writing application
1400 extension blocks, so we don't attempt to write them for older versions.
1402 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1403 writing extension blocks so that they could only be written to files.
1407 static int do_ns_loop(GifFileType *gf, i_img *img)
1409 /* EGifPutExtension() doesn't appear to handle application
1410 extension blocks in any way
1411 Since giflib wraps the fd with a FILE * (and puts that in its
1412 private data), we can't do an end-run and write the data
1414 There's no open interface that takes a FILE * either, so we
1415 can't workaround it that way either.
1416 If giflib's callback interface wasn't broken by default, I'd
1417 force file writes to use callbacks, but it is broken by default.
1419 /* yes this was another attempt at supporting the loop extension */
1420 #if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
1422 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1423 unsigned char nsle[12] = "NETSCAPE2.0";
1424 unsigned char subblock[3];
1425 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1427 i_push_error(0, "writing loop extension");
1431 subblock[1] = loop_count % 256;
1432 subblock[2] = loop_count / 256;
1433 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1435 i_push_error(0, "writing loop extention sub-block");
1445 =item make_gif_map(i_quantize *quant, int want_trans)
1447 Create a giflib color map object from an Imager color map.
1452 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1454 GifColorType colors[256];
1456 int size = quant->mc_count;
1458 ColorMapObject *map;
1461 for (i = 0; i < quant->mc_count; ++i) {
1462 colors[i].Red = quant->mc_colors[i].rgb.r;
1463 colors[i].Green = quant->mc_colors[i].rgb.g;
1464 colors[i].Blue = quant->mc_colors[i].rgb.b;
1467 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1468 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1469 colors[size].Red = trans.rgb.r;
1470 colors[size].Green = trans.rgb.g;
1471 colors[size].Blue = trans.rgb.b;
1475 while (map_size < size)
1477 /* giflib spews for 1 colour maps, reasonable, I suppose */
1480 while (i < map_size) {
1481 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1485 map = MakeMapObject(map_size, colors);
1486 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1489 i_push_error(0, "Could not create color map object");
1496 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1498 We need to call EGifSetGifVersion() before opening the file - put that
1501 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1502 giflib 4.1.0 has code:
1504 static char *GifVersionPrefix = GIF87_STAMP;
1506 and the code that sets the version internally does:
1508 strncpy(&GifVersionPrefix[3], Version, 3);
1510 which is very broken.
1512 Failing to set the correct GIF version doesn't seem to cause a problem
1515 Modern versions (4.1.4 anyway) of giflib/libungif handle
1516 EGifSetGifVersion correctly.
1518 If t/t105gif.t crashes here then run Makefile.PL with
1519 --nogifsetversion, eg.:
1521 perl Makefile.PL --nogifsetversion
1523 or install a less buggy giflib.
1528 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1529 #if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
1530 && !defined(IM_NO_SET_GIF_VERSION)
1535 if (quant->transp != tr_none)
1538 for (i = 0; i < count; ++i) {
1539 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1543 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1547 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1551 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1558 EGifSetGifVersion("89a");
1560 EGifSetGifVersion("87a");
1565 in_palette(i_color *c, i_quantize *quant, int size) {
1568 for (i = 0; i < size; ++i) {
1569 if (c->channel[0] == quant->mc_colors[i].channel[0]
1570 && c->channel[1] == quant->mc_colors[i].channel[1]
1571 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1580 =item has_common_palette(imgs, count, quant, want_trans)
1582 Tests if all the given images are paletted and have a common palette,
1583 if they do it builds that palette.
1585 A possible improvement might be to eliminate unused colors in the
1591 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1593 int size = quant->mc_count;
1598 /* we try to build a common palette here, if we can manage that, then
1599 that's the palette we use */
1600 for (imgn = 0; imgn < count; ++imgn) {
1601 int eliminate_unused;
1602 if (imgs[imgn]->type != i_palette_type)
1605 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1606 &eliminate_unused)) {
1607 eliminate_unused = 1;
1610 if (eliminate_unused) {
1611 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1613 memset(used, 0, sizeof(used));
1615 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1616 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1617 for (x = 0; x < imgs[imgn]->xsize; ++x)
1624 /* assume all are in use */
1625 memset(used, 1, sizeof(used));
1628 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1631 i_getcolors(imgs[imgn], i, &c, 1);
1633 if (in_palette(&c, quant, size) < 0) {
1634 if (size < quant->mc_size) {
1635 quant->mc_colors[size++] = c;
1638 /* oops, too many colors */
1646 quant->mc_count = size;
1652 quant_paletted(i_quantize *quant, i_img *img) {
1653 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1655 i_palidx trans[256];
1659 /* build a translation table */
1660 for (i = 0; i < i_colorcount(img); ++i) {
1662 i_getcolors(img, i, &c, 1);
1663 trans[i] = in_palette(&c, quant, quant->mc_count);
1666 for (y = 0; y < img->ysize; ++y) {
1667 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1668 for (x = 0; x < img->xsize; ++x) {
1678 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1680 Internal. Low-level function that does the high-level GIF processing
1683 Returns non-zero on success.
1689 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1690 unsigned char *result = NULL;
1692 ColorMapObject *map;
1693 int scrw = 0, scrh = 0;
1694 int imgn, orig_count, orig_size;
1696 int trans_index = -1;
1700 i_img **glob_imgs; /* images that will use the global color map */
1702 i_color *orig_colors = quant->mc_colors;
1703 i_color *glob_colors = NULL;
1704 int glob_color_count = 0;
1705 int glob_want_trans;
1706 int glob_paletted = 0; /* the global map was made from the image palettes */
1707 int colors_paletted = 0;
1712 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1713 quant, gf, imgs, count));
1715 /* *((char *)0) = 1; */ /* used to break into the debugger */
1718 i_push_error(0, "No images provided to write");
1719 return 0; /* what are you smoking? */
1722 i_mempool_init(&mp);
1724 /* sanity is nice */
1725 if (quant->mc_size > 256)
1726 quant->mc_size = 256;
1727 if (quant->mc_count > quant->mc_size)
1728 quant->mc_count = quant->mc_size;
1730 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1732 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1736 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1737 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1739 glob_want_trans = 0;
1740 for (imgn = 0; imgn < count; ++imgn) {
1742 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1743 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1744 if (imgs[imgn]->xsize + posx > scrw)
1745 scrw = imgs[imgn]->xsize + posx;
1746 if (imgs[imgn]->ysize + posy > scrh)
1747 scrh = imgs[imgn]->ysize + posy;
1748 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1749 localmaps[imgn] = 0;
1750 if (localmaps[imgn])
1753 if (imgs[imgn]->channels == 4) {
1754 glob_want_trans = 1;
1756 glob_imgs[glob_img_count++] = imgs[imgn];
1759 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1761 orig_count = quant->mc_count;
1762 orig_size = quant->mc_size;
1764 if (glob_img_count) {
1766 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1767 quant->mc_colors = glob_colors;
1768 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1769 /* we have some images that want to use the global map */
1770 if (glob_want_trans && quant->mc_count == 256) {
1771 mm_log((2, " disabling transparency for global map - no space\n"));
1772 glob_want_trans = 0;
1774 if (glob_want_trans && quant->mc_size == 256) {
1775 mm_log((2, " reserving color for transparency\n"));
1778 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1783 i_quant_makemap(quant, glob_imgs, glob_img_count);
1785 glob_color_count = quant->mc_count;
1786 quant->mc_colors = orig_colors;
1789 /* use the global map if we have one, otherwise use the local map */
1792 quant->mc_colors = glob_colors;
1793 quant->mc_count = glob_color_count;
1794 want_trans = glob_want_trans && imgs[0]->channels == 4;
1796 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1798 if (gif_background < 0)
1800 if (gif_background >= glob_color_count)
1804 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1805 if (has_common_palette(imgs, 1, quant, want_trans)) {
1806 colors_paletted = 1;
1809 colors_paletted = 0;
1810 i_quant_makemap(quant, imgs, 1);
1813 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1814 i_mempool_destroy(&mp);
1815 quant->mc_colors = orig_colors;
1817 mm_log((1, "Error in MakeMapObject"));
1822 /* since we don't know how big some the local palettes could be
1823 we need to base the bits on the maximum number of colors */
1824 while (orig_size > (1 << color_bits))
1828 int count = quant->mc_count;
1831 while (count > (1 << color_bits))
1835 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1836 gif_background, map) == GIF_ERROR) {
1837 i_mempool_destroy(&mp);
1838 quant->mc_colors = orig_colors;
1840 i_push_error(0, "Could not save screen descriptor");
1844 mm_log((1, "Error in EGifPutScreenDesc."));
1849 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1851 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1854 if (!localmaps[0]) {
1856 colors_paletted = glob_paletted;
1859 /* if this image has a global map the colors in quant don't
1860 belong to this image, so build a palette */
1862 /* generate the local map for this image */
1863 quant->mc_colors = orig_colors;
1864 quant->mc_size = orig_size;
1865 quant->mc_count = orig_count;
1866 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1868 /* if the caller gives us too many colours we can't do transparency */
1869 if (want_trans && quant->mc_count == 256)
1871 /* if they want transparency but give us a big size, make it smaller
1872 to give room for a transparency colour */
1873 if (want_trans && quant->mc_size == 256)
1875 if (has_common_palette(imgs, 1, quant, want_trans)) {
1876 colors_paletted = 1;
1879 colors_paletted = 0;
1880 i_quant_makemap(quant, imgs, 1);
1882 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1883 i_mempool_destroy(&mp);
1885 quant->mc_colors = orig_colors;
1886 mm_log((1, "Error in MakeMapObject"));
1891 /* the map we wrote was the map for this image - don't set the local
1897 if (colors_paletted)
1898 result = quant_paletted(quant, imgs[0]);
1900 result = i_quant_translate(quant, imgs[0]);
1902 i_mempool_destroy(&mp);
1903 quant->mc_colors = orig_colors;
1908 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1909 trans_index = quant->mc_count;
1912 if (!do_ns_loop(gf, imgs[0])) {
1913 i_mempool_destroy(&mp);
1914 quant->mc_colors = orig_colors;
1918 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1919 i_mempool_destroy(&mp);
1920 quant->mc_colors = orig_colors;
1926 if (!do_comments(gf, imgs[0])) {
1927 i_mempool_destroy(&mp);
1928 quant->mc_colors = orig_colors;
1934 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1936 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1937 interlace, map) == GIF_ERROR) {
1938 i_mempool_destroy(&mp);
1939 quant->mc_colors = orig_colors;
1941 i_push_error(0, "Could not save image descriptor");
1943 mm_log((1, "Error in EGifPutImageDesc."));
1949 if (!do_write(gf, interlace, imgs[0], result)) {
1950 i_mempool_destroy(&mp);
1951 quant->mc_colors = orig_colors;
1958 /* that first awful image is out of the way, do the rest */
1959 for (imgn = 1; imgn < count; ++imgn) {
1960 if (localmaps[imgn]) {
1961 quant->mc_colors = orig_colors;
1962 quant->mc_count = orig_count;
1963 quant->mc_size = orig_size;
1965 want_trans = quant->transp != tr_none
1966 && imgs[imgn]->channels == 4;
1967 /* if the caller gives us too many colours we can't do transparency */
1968 if (want_trans && quant->mc_count == 256)
1970 /* if they want transparency but give us a big size, make it smaller
1971 to give room for a transparency colour */
1972 if (want_trans && quant->mc_size == 256)
1975 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1976 result = quant_paletted(quant, imgs[imgn]);
1979 i_quant_makemap(quant, imgs+imgn, 1);
1980 result = i_quant_translate(quant, imgs[imgn]);
1983 i_mempool_destroy(&mp);
1984 quant->mc_colors = orig_colors;
1986 mm_log((1, "error in i_quant_translate()"));
1990 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1991 trans_index = quant->mc_count;
1994 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1995 i_mempool_destroy(&mp);
1996 quant->mc_colors = orig_colors;
1999 mm_log((1, "Error in MakeMapObject."));
2004 quant->mc_colors = glob_colors;
2005 quant->mc_count = glob_color_count;
2007 result = quant_paletted(quant, imgs[imgn]);
2009 result = i_quant_translate(quant, imgs[imgn]);
2010 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
2012 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
2013 trans_index = quant->mc_count;
2018 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
2019 i_mempool_destroy(&mp);
2020 quant->mc_colors = orig_colors;
2026 if (!do_comments(gf, imgs[imgn])) {
2027 i_mempool_destroy(&mp);
2028 quant->mc_colors = orig_colors;
2034 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
2036 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
2039 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
2041 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
2042 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
2043 i_mempool_destroy(&mp);
2044 quant->mc_colors = orig_colors;
2046 i_push_error(0, "Could not save image descriptor");
2051 mm_log((1, "Error in EGifPutImageDesc."));
2057 if (!do_write(gf, interlace, imgs[imgn], result)) {
2058 i_mempool_destroy(&mp);
2059 quant->mc_colors = orig_colors;
2067 if (EGifCloseFile(gf) == GIF_ERROR) {
2068 i_mempool_destroy(&mp);
2070 i_push_error(0, "Could not close GIF file");
2071 mm_log((1, "Error in EGifCloseFile\n"));
2076 for (i = 0; i < glob_color_count; ++i)
2077 orig_colors[i] = glob_colors[i];
2080 i_mempool_destroy(&mp);
2081 quant->mc_colors = orig_colors;
2087 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2089 General high-level function to write a GIF to a file.
2091 Writes the GIF images to the specified file handle using the options
2092 in quant and opts. See L<image.h/i_quantize> and
2093 L<image.h/i_gif_opts>.
2095 Returns non-zero on success.
2101 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2105 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2106 quant, fd, imgs, count));
2108 gif_set_version(quant, imgs, count);
2110 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2112 i_push_error(0, "Cannot create GIF file object");
2113 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2117 return i_writegif_low(quant, gf, imgs, count);
2120 #if IM_GIFMAJOR >= 4
2123 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2125 Internal. Wrapper for the user write callback function.
2130 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2132 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2134 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2140 =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)
2142 General high-level function to write a GIF using callbacks to send
2145 Returns non-zero on success.
2151 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2152 int maxlength, i_img **imgs, int count)
2154 #if IM_GIFMAJOR >= 4
2156 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2161 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2162 quant, cb, userdata, maxlength, imgs, count));
2164 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2166 i_push_error(0, "Cannot create GIF file object");
2167 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2168 i_free_gen_write_data(gwd, 0);
2172 result = i_writegif_low(quant, gf, imgs, count);
2173 return i_free_gen_write_data(gwd, result);
2176 i_push_error(0, "callbacks not supported with giflib3");
2182 #if IM_GIFMAJOR >= 4
2185 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2186 io_glue *ig = (io_glue *)gft->UserData;
2188 return ig->writecb(ig, data, length);
2194 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2199 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2201 io_glue_commit_types(ig);
2203 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2204 int fd = dup(ig->source.fdseek.fd);
2206 i_push_error(errno, "dup() failed");
2209 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2210 is called - dup it so the caller's fd isn't closed */
2211 return i_writegif_gen(quant, fd, imgs, count);
2214 #if IM_GIFMAJOR >= 4
2215 GifFileType *GifFile;
2220 gif_set_version(quant, imgs, count);
2222 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2224 i_push_error(0, "Cannot create giflib callback object");
2225 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2229 result = i_writegif_low(quant, GifFile, imgs, count);
2236 i_push_error(0, "callbacks not supported with giflib3");
2244 =item gif_error_msg(int code)
2246 Grabs the most recent giflib error code from GifLastError() and
2247 returns a string that describes that error.
2249 The returned pointer points to a static buffer, either from a literal
2250 C string or a static buffer.
2255 static char const *gif_error_msg(int code) {
2256 static char msg[80];
2259 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2260 return "Failed to open given file";
2262 case E_GIF_ERR_WRITE_FAILED:
2263 return "Write failed";
2265 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2266 return "Screen descriptor already passed to giflib";
2268 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2269 return "Image descriptor already passed to giflib";
2271 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2272 return "Neither global nor local color map set";
2274 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2275 return "Too much pixel data passed to giflib";
2277 case E_GIF_ERR_NOT_ENOUGH_MEM:
2278 return "Out of memory";
2280 case E_GIF_ERR_DISK_IS_FULL:
2281 return "Disk is full";
2283 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2284 return "File close failed";
2286 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2287 return "File not writable";
2289 case D_GIF_ERR_OPEN_FAILED:
2290 return "Failed to open file";
2292 case D_GIF_ERR_READ_FAILED:
2293 return "Failed to read from file";
2295 case D_GIF_ERR_NOT_GIF_FILE:
2296 return "File is not a GIF file";
2298 case D_GIF_ERR_NO_SCRN_DSCR:
2299 return "No screen descriptor detected - invalid file";
2301 case D_GIF_ERR_NO_IMAG_DSCR:
2302 return "No image descriptor detected - invalid file";
2304 case D_GIF_ERR_NO_COLOR_MAP:
2305 return "No global or local color map found";
2307 case D_GIF_ERR_WRONG_RECORD:
2308 return "Wrong record type detected - invalid file?";
2310 case D_GIF_ERR_DATA_TOO_BIG:
2311 return "Data in file too big for image";
2313 case D_GIF_ERR_NOT_ENOUGH_MEM:
2314 return "Out of memory";
2316 case D_GIF_ERR_CLOSE_FAILED:
2317 return "Close failed";
2319 case D_GIF_ERR_NOT_READABLE:
2320 return "File not opened for read";
2322 case D_GIF_ERR_IMAGE_DEFECT:
2323 return "Defective image";
2325 case D_GIF_ERR_EOF_TOO_SOON:
2326 return "Unexpected EOF - invalid file";
2329 sprintf(msg, "Unknown giflib error code %d", code);
2335 =item gif_push_error()
2337 Utility function that takes the current GIF error code, converts it to
2338 an error message and pushes it on the error stack.
2343 static void gif_push_error(void) {
2344 int code = GifLastError(); /* clears saved error */
2346 i_push_error(code, gif_error_msg(code));
2352 The Netscape loop extension isn't implemented. Giflib's extension
2353 writing code doesn't seem to support writing named extensions in this
2356 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2357 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2360 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2361 supplied interface in giflib 4.1.0 causes a SEGV in
2362 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2366 Arnar M. Hrafnkelsson, addi@umich.edu