16 imgif.c - read and write gif files for Imager
25 int max_colours; // number of bits per colour
26 int pixdev; // how much noise to add
27 i_color fixed[N]; // fixed palette entries
28 int fixedlen; // number of fixed colours
29 int success; // non-zero on success
30 char *data; // a GIF file in memory
31 int length; // how big data is
32 int reader(char *, char *, int, int);
33 int writer(char *, char *, int);
34 char *userdata; // user's data, whatever it is
38 img = i_readgif(fd, &colour_table, &colours);
39 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
40 success = i_writegifmc(img, fd, max_colours);
41 img = i_readgif_scalar(data, length, &colour_table, &colours);
42 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
43 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
44 success = i_writegif_callback(&quant, writer, userdata, maxlength,
49 This source file provides the C level interface to reading and writing
52 This has been tested with giflib 3 and 4, though you lose the callback
53 functionality with giflib3.
62 static char const *gif_error_msg(int code);
63 static void gif_push_error(void);
65 /* Make some variables global, so we could access them faster: */
68 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
69 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
75 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
76 GifColorType *mapentry;
78 int colourmapsize = colourmap->ColorCount;
80 if(colours) *colours = colourmapsize;
81 if(!colour_table) return;
83 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
84 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
86 for(q=0; q<colourmapsize; q++) {
87 mapentry = &colourmap->Colors[q];
88 (*colour_table)[q*3 + 0] = mapentry->Red;
89 (*colour_table)[q*3 + 1] = mapentry->Green;
90 (*colour_table)[q*3 + 2] = mapentry->Blue;
94 #ifdef GIF_LIB_VERSION
97 char gif_version_str[] = GIF_LIB_VERSION;
100 i_giflib_version(void) {
101 const char *p = gif_version_str;
103 while (*p && (*p < '0' || *p > '9'))
109 return strtod(p, NULL);
115 i_giflib_version(void) {
116 return GIFLIB_MAJOR + GIFLIB_MINOR * 0.1;
122 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
124 Internal. Low-level function for reading a GIF file. The caller must
125 create the appropriate GifFileType object and pass it in.
131 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
133 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
134 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
135 ColorMapObject *ColorMap;
137 GifRecordType RecordType;
138 GifByteType *Extension;
141 static GifColorType *ColorMapEntry;
144 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
146 /* it's possible that the caller has called us with *colour_table being
147 non-NULL, but we check that to see if we need to free an allocated
148 colour table on error.
150 if (colour_table) *colour_table = NULL;
152 BackGround = GifFile->SBackGroundColor;
153 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
156 ColorMapSize = ColorMap->ColorCount;
157 i_colortable_copy(colour_table, colours, ColorMap);
161 if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
162 if (colour_table && *colour_table) {
163 myfree(*colour_table);
164 *colour_table = NULL;
166 DGifCloseFile(GifFile);
167 mm_log((1, "i_readgif: image size exceeds limits\n"));
171 im = i_img_8_new(GifFile->SWidth, GifFile->SHeight, 3);
173 if (colour_table && *colour_table) {
174 myfree(*colour_table);
175 *colour_table = NULL;
177 DGifCloseFile(GifFile);
181 Size = GifFile->SWidth * sizeof(GifPixelType);
183 GifRow = mymalloc(Size);
185 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
187 /* Scan the content of the GIF file and load the image(s) in: */
189 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
191 i_push_error(0, "Unable to get record type");
192 if (colour_table && *colour_table) {
193 myfree(*colour_table);
194 *colour_table = NULL;
198 DGifCloseFile(GifFile);
202 switch (RecordType) {
203 case IMAGE_DESC_RECORD_TYPE:
204 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
206 i_push_error(0, "Unable to get image descriptor");
207 if (colour_table && *colour_table) {
208 myfree(*colour_table);
209 *colour_table = NULL;
213 DGifCloseFile(GifFile);
217 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
218 mm_log((1, "Adding local colormap\n"));
219 ColorMapSize = ColorMap->ColorCount;
221 i_colortable_copy(colour_table, colours, ColorMap);
225 /* No colormap and we are about to read in the image - abandon for now */
226 mm_log((1, "Going in with no colormap\n"));
227 i_push_error(0, "Image does not have a local or a global color map");
228 /* we can't have allocated a colour table here */
231 DGifCloseFile(GifFile);
235 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
236 Col = GifFile->Image.Left;
237 Width = GifFile->Image.Width;
238 Height = GifFile->Image.Height;
240 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
242 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
243 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
244 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
245 if (colour_table && *colour_table) {
246 myfree(*colour_table);
247 *colour_table = NULL;
251 DGifCloseFile(GifFile);
254 if (GifFile->Image.Interlace) {
256 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
258 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
260 i_push_error(0, "Reading GIF line");
261 if (colour_table && *colour_table) {
262 myfree(*colour_table);
263 *colour_table = NULL;
267 DGifCloseFile(GifFile);
271 for (x = 0; x < Width; x++) {
272 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
273 col.rgb.r = ColorMapEntry->Red;
274 col.rgb.g = ColorMapEntry->Green;
275 col.rgb.b = ColorMapEntry->Blue;
276 i_ppix(im,Col+x,j,&col);
282 for (i = 0; i < Height; i++) {
283 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
285 i_push_error(0, "Reading GIF line");
286 if (colour_table && *colour_table) {
287 myfree(*colour_table);
288 *colour_table = NULL;
292 DGifCloseFile(GifFile);
296 for (x = 0; x < Width; x++) {
297 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
298 col.rgb.r = ColorMapEntry->Red;
299 col.rgb.g = ColorMapEntry->Green;
300 col.rgb.b = ColorMapEntry->Blue;
301 i_ppix(im, Col+x, Row, &col);
307 case EXTENSION_RECORD_TYPE:
308 /* Skip any extension blocks in file: */
309 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
311 i_push_error(0, "Reading extension record");
312 if (colour_table && *colour_table) {
313 myfree(*colour_table);
314 *colour_table = NULL;
318 DGifCloseFile(GifFile);
321 while (Extension != NULL) {
322 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
324 i_push_error(0, "reading next block of extension");
325 if (colour_table && *colour_table) {
326 myfree(*colour_table);
327 *colour_table = NULL;
331 DGifCloseFile(GifFile);
336 case TERMINATE_RECORD_TYPE:
338 default: /* Should be traps by DGifGetRecordType. */
341 } while (RecordType != TERMINATE_RECORD_TYPE);
345 if (DGifCloseFile(GifFile) == GIF_ERROR) {
347 i_push_error(0, "Closing GIF file object");
348 if (colour_table && *colour_table) {
349 myfree(*colour_table);
350 *colour_table = NULL;
356 i_tags_set(&im->tags, "i_format", "gif", -1);
363 Internal function called by i_readgif_multi_low() in error handling
366 static void free_images(i_img **imgs, int count) {
370 for (i = 0; i < count; ++i)
371 i_img_destroy(imgs[i]);
377 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
379 Reads one of more gif images from the given GIF file.
381 Returns a pointer to an array of i_img *, and puts the count into
384 If page is not -1 then the given image _only_ is returned from the
385 file, where the first image is 0, the second 1 and so on.
387 Unlike the normal i_readgif*() functions the images are paletted
388 images rather than a combined RGB image.
390 This functions sets tags on the images returned:
396 the offset of the image from the left of the "screen" ("Image Left
401 the offset of the image from the top of the "screen" ("Image Top Position")
405 non-zero if the image was interlaced ("Interlace Flag")
407 =item gif_screen_width
409 =item gif_screen_height
411 the size of the logical screen ("Logical Screen Width",
412 "Logical Screen Height")
416 Non-zero if this image had a local color map.
420 The index in the global colormap of the logical screen's background
421 color. This is only set if the current image uses the global
424 =item gif_trans_index
426 The index of the color in the colormap used for transparency. If the
427 image has a transparency then it is returned as a 4 channel image with
428 the alpha set to zero in this palette entry. ("Transparent Color Index")
432 The delay until the next frame is displayed, in 1/100 of a second.
437 whether or not a user input is expected before continuing (view dependent)
442 how the next frame is displayed ("Disposal Method")
446 the number of loops from the Netscape Loop extension. This may be zero.
450 the first block of the first gif comment before each image.
454 Where applicable, the ("name") is the name of that field from the GIF89
460 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
462 int i, j, Size, Width, Height, ExtCode, Count;
463 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
464 ColorMapObject *ColorMap;
466 GifRecordType RecordType;
467 GifByteType *Extension;
471 int trans_index = 0; /* transparent index if we see a GCE */
472 int gif_delay = 0; /* delay from a GCE */
473 int user_input = 0; /* user input flag from a GCE */
474 int disposal = 0; /* disposal method from a GCE */
477 char *comment = NULL; /* a comment */
478 i_img **results = NULL;
479 int result_alloc = 0;
481 int image_colors = 0;
482 i_color black; /* used to expand the palette if needed */
484 for (i = 0; i < MAXCHANNELS; ++i)
485 black.channel[i] = 0;
489 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
491 BackGround = GifFile->SBackGroundColor;
493 Size = GifFile->SWidth * sizeof(GifPixelType);
495 GifRow = (GifRowType) mymalloc(Size);
497 /* Scan the content of the GIF file and load the image(s) in: */
499 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
501 i_push_error(0, "Unable to get record type");
502 free_images(results, *count);
503 DGifCloseFile(GifFile);
510 switch (RecordType) {
511 case IMAGE_DESC_RECORD_TYPE:
512 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
514 i_push_error(0, "Unable to get image descriptor");
515 free_images(results, *count);
516 DGifCloseFile(GifFile);
523 Width = GifFile->Image.Width;
524 Height = GifFile->Image.Height;
525 if (page == -1 || page == ImageNum) {
526 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
527 mm_log((1, "Adding local colormap\n"));
528 ColorMapSize = ColorMap->ColorCount;
530 /* No colormap and we are about to read in the image -
532 mm_log((1, "Going in with no colormap\n"));
533 i_push_error(0, "Image does not have a local or a global color map");
534 free_images(results, *count);
535 DGifCloseFile(GifFile);
543 if (got_gce && trans_index >= 0)
545 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
546 free_images(results, *count);
547 mm_log((1, "i_readgif: image size exceeds limits\n"));
548 DGifCloseFile(GifFile);
554 img = i_img_pal_new(Width, Height, channels, 256);
556 free_images(results, *count);
557 DGifCloseFile(GifFile);
563 /* populate the palette of the new image */
564 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
565 for (i = 0; i < ColorMapSize; ++i) {
567 col.rgba.r = ColorMap->Colors[i].Red;
568 col.rgba.g = ColorMap->Colors[i].Green;
569 col.rgba.b = ColorMap->Colors[i].Blue;
570 if (channels == 4 && trans_index == i)
575 i_addcolors(img, &col, 1);
577 image_colors = ColorMapSize;
579 if (*count > result_alloc) {
580 if (result_alloc == 0) {
582 results = mymalloc(result_alloc * sizeof(i_img *));
585 /* myrealloc never fails (it just dies if it can't allocate) */
587 results = myrealloc(results, result_alloc * sizeof(i_img *));
590 results[*count-1] = img;
591 i_tags_set(&img->tags, "i_format", "gif", -1);
592 i_tags_setn(&img->tags, "gif_left", GifFile->Image.Left);
594 i_tags_setn(&img->tags, "gif_top", GifFile->Image.Top);
595 i_tags_setn(&img->tags, "gif_interlace", GifFile->Image.Interlace);
596 i_tags_setn(&img->tags, "gif_screen_width", GifFile->SWidth);
597 i_tags_setn(&img->tags, "gif_screen_height", GifFile->SHeight);
598 i_tags_setn(&img->tags, "gif_colormap_size", ColorMapSize);
599 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
600 i_tags_setn(&img->tags, "gif_background",
601 GifFile->SBackGroundColor);
603 if (GifFile->Image.ColorMap) {
604 i_tags_setn(&img->tags, "gif_localmap", 1);
607 if (trans_index >= 0) {
609 i_tags_setn(&img->tags, "gif_trans_index", trans_index);
610 i_getcolors(img, trans_index, &trans, 1);
611 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
613 i_tags_setn(&img->tags, "gif_delay", gif_delay);
614 i_tags_setn(&img->tags, "gif_user_input", user_input);
615 i_tags_setn(&img->tags, "gif_disposal", disposal);
619 i_tags_setn(&img->tags, "gif_loop", ns_loop);
621 i_tags_set(&img->tags, "gif_comment", comment, strlen(comment));
626 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
627 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
629 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
630 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
631 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
632 free_images(results, *count);
633 DGifCloseFile(GifFile);
640 if (GifFile->Image.Interlace) {
641 for (Count = i = 0; i < 4; i++) {
642 for (j = InterlacedOffset[i]; j < Height;
643 j += InterlacedJumps[i]) {
645 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
647 i_push_error(0, "Reading GIF line");
648 free_images(results, *count);
649 DGifCloseFile(GifFile);
656 /* range check the scanline if needed */
657 if (image_colors != 256) {
659 for (x = 0; x < Width; ++x) {
660 while (GifRow[x] >= image_colors) {
661 /* expand the palette since a palette index is too big */
662 i_addcolors(img, &black, 1);
668 i_ppal(img, 0, Width, j, GifRow);
673 for (i = 0; i < Height; i++) {
674 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
676 i_push_error(0, "Reading GIF line");
677 free_images(results, *count);
678 DGifCloseFile(GifFile);
685 /* range check the scanline if needed */
686 if (image_colors != 256) {
688 for (x = 0; x < Width; ++x) {
689 while (GifRow[x] >= image_colors) {
690 /* expand the palette since a palette index is too big */
691 i_addcolors(img, &black, 1);
697 i_ppal(img, 0, Width, i, GifRow);
701 /* must be only one image wanted and that was it */
704 DGifCloseFile(GifFile);
712 /* whether interlaced or not, it has the same number of lines */
713 /* giflib does't have an interface to skip the image data */
714 for (i = 0; i < Height; i++) {
715 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
717 i_push_error(0, "Reading GIF line");
718 free_images(results, *count);
720 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);
742 DGifCloseFile(GifFile);
747 /* possibly this should be an error, but "be liberal in what you accept" */
750 if (ExtCode == 0xF9) {
752 if (Extension[1] & 1)
753 trans_index = Extension[4];
756 gif_delay = Extension[2] + 256 * Extension[3];
757 user_input = (Extension[1] & 2) != 0;
758 disposal = (Extension[1] >> 2) & 7;
760 if (ExtCode == 0xFF && *Extension == 11) {
761 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
762 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
764 i_push_error(0, "reading loop extension");
765 free_images(results, *count);
767 DGifCloseFile(GifFile);
772 if (Extension && *Extension == 3) {
774 ns_loop = Extension[2] + 256 * Extension[3];
778 else if (ExtCode == 0xFE) {
779 /* while it's possible for a GIF file to contain more than one
780 comment, I'm only implementing a single comment per image,
781 with the comment saved into the following image.
782 If someone wants more than that they can implement it.
783 I also don't handle comments that take more than one block.
786 comment = mymalloc(*Extension+1);
787 memcpy(comment, Extension+1, *Extension);
788 comment[*Extension] = '\0';
791 while (Extension != NULL) {
792 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
794 i_push_error(0, "reading next block of extension");
795 free_images(results, *count);
797 DGifCloseFile(GifFile);
804 case TERMINATE_RECORD_TYPE:
806 default: /* Should be trapped by DGifGetRecordType. */
809 } while (RecordType != TERMINATE_RECORD_TYPE);
813 i_tags_set(&(results[*count-1]->tags), "gif_comment", comment,
821 if (DGifCloseFile(GifFile) == GIF_ERROR) {
823 i_push_error(0, "Closing GIF file object");
824 free_images(results, *count);
828 if (ImageNum && page != -1) {
829 /* there were images, but the page selected wasn't found */
830 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
831 free_images(results, *count);
838 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
841 =item i_readgif_multi_wiol(ig, int *count)
847 i_readgif_multi_wiol(io_glue *ig, int *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);
863 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
864 io_glue *ig = (io_glue *)gft->UserData;
866 return i_io_read(ig, buf, length);
870 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
871 GifFileType *GifFile;
875 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
877 i_push_error(0, "Cannot create giflib callback object");
878 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
882 return i_readgif_low(GifFile, color_table, colors);
886 =item i_readgif_single_low(GifFile, page)
888 Lower level function to read a single image from a GIF.
890 page must be non-negative.
895 i_readgif_single_low(GifFileType *GifFile, int page) {
899 imgs = i_readgif_multi_low(GifFile, &count, page);
902 i_img *result = imgs[0];
908 /* i_readgif_multi_low() handles the errors appropriately */
914 =item i_readgif_single_wiol(ig, page)
916 Read a single page from a GIF image file, where the page is indexed
919 Returns NULL if the page isn't found.
925 i_readgif_single_wiol(io_glue *ig, int page) {
926 GifFileType *GifFile;
930 i_push_error(0, "page must be non-negative");
934 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
936 i_push_error(0, "Cannot create giflib callback object");
937 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
941 return i_readgif_single_low(GifFile, page);
945 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
947 Internal. Low level image write function. Writes in interlace if
948 that was requested in the GIF options.
950 Returns non-zero on success.
955 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
958 for (i = 0; i < 4; ++i) {
959 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
960 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
962 i_push_error(0, "Could not save image data:");
963 mm_log((1, "Error in EGifPutLine\n"));
972 for (y = 0; y < img->ysize; ++y) {
973 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
975 i_push_error(0, "Could not save image data:");
976 mm_log((1, "Error in EGifPutLine\n"));
988 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
990 Internal. Writes the GIF graphics control extension, if necessary.
992 Returns non-zero on success.
996 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
998 unsigned char gce[4] = {0};
1002 int disposal_method;
1006 gce[3] = trans_index;
1009 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1010 gce[1] = delay % 256;
1011 gce[2] = delay / 256;
1014 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1019 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1020 gce[0] |= (disposal_method & 3) << 2;
1024 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1026 i_push_error(0, "Could not save GCE");
1033 =item do_comments(gf, img)
1035 Write any comments in the image.
1039 static int do_comments(GifFileType *gf, i_img *img) {
1042 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1043 if (img->tags.tags[pos].data) {
1044 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1050 #ifdef IMAGER_SNPRINTF
1051 snprintf(buf, sizeof(buf), "%d", img->tags.tags[pos].idata);
1053 sprintf(buf, "%d", img->tags.tags[pos].idata);
1055 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1065 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1067 Internal. Add the Netscape2.0 loop extension block, if requested.
1069 Giflib/libungif prior to 4.1.1 didn't support writing application
1070 extension blocks, so we don't attempt to write them for older versions.
1072 Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1073 writing extension blocks so that they could only be written to files.
1077 static int do_ns_loop(GifFileType *gf, i_img *img)
1079 /* EGifPutExtension() doesn't appear to handle application
1080 extension blocks in any way
1081 Since giflib wraps the fd with a FILE * (and puts that in its
1082 private data), we can't do an end-run and write the data
1084 There's no open interface that takes a FILE * either, so we
1085 can't workaround it that way either.
1086 If giflib's callback interface wasn't broken by default, I'd
1087 force file writes to use callbacks, but it is broken by default.
1089 /* yes this was another attempt at supporting the loop extension */
1091 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1092 unsigned char nsle[12] = "NETSCAPE2.0";
1093 unsigned char subblock[3];
1094 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
1096 i_push_error(0, "writing loop extension");
1100 subblock[1] = loop_count % 256;
1101 subblock[2] = loop_count / 256;
1102 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
1104 i_push_error(0, "writing loop extension sub-block");
1113 =item make_gif_map(i_quantize *quant, int want_trans)
1115 Create a giflib color map object from an Imager color map.
1120 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1122 GifColorType colors[256];
1124 int size = quant->mc_count;
1126 ColorMapObject *map;
1129 for (i = 0; i < quant->mc_count; ++i) {
1130 colors[i].Red = quant->mc_colors[i].rgb.r;
1131 colors[i].Green = quant->mc_colors[i].rgb.g;
1132 colors[i].Blue = quant->mc_colors[i].rgb.b;
1135 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1136 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1137 colors[size].Red = trans.rgb.r;
1138 colors[size].Green = trans.rgb.g;
1139 colors[size].Blue = trans.rgb.b;
1143 while (map_size < size)
1145 /* giflib spews for 1 colour maps, reasonable, I suppose */
1148 while (i < map_size) {
1149 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1153 map = MakeMapObject(map_size, colors);
1154 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1157 i_push_error(0, "Could not create color map object");
1164 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1166 We need to call EGifSetGifVersion() before opening the file - put that
1169 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1170 giflib 4.1.0 has code:
1172 static char *GifVersionPrefix = GIF87_STAMP;
1174 and the code that sets the version internally does:
1176 strncpy(&GifVersionPrefix[3], Version, 3);
1178 which is very broken.
1180 Failing to set the correct GIF version doesn't seem to cause a problem
1183 Modern versions (4.1.4 anyway) of giflib/libungif handle
1184 EGifSetGifVersion correctly.
1186 If t/t105gif.t crashes here then run Makefile.PL with
1187 --nogifsetversion, eg.:
1189 perl Makefile.PL --nogifsetversion
1191 or install a less buggy giflib.
1196 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1201 if (quant->transp != tr_none)
1204 for (i = 0; i < count; ++i) {
1205 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1209 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1213 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1217 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1224 EGifSetGifVersion("89a");
1226 EGifSetGifVersion("87a");
1230 in_palette(i_color *c, i_quantize *quant, int size) {
1233 for (i = 0; i < size; ++i) {
1234 if (c->channel[0] == quant->mc_colors[i].channel[0]
1235 && c->channel[1] == quant->mc_colors[i].channel[1]
1236 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1245 =item has_common_palette(imgs, count, quant)
1247 Tests if all the given images are paletted and their colors are in the
1250 Previously this would build a consolidated palette from the source,
1251 but that meant that if the caller supplied a static palette (or
1252 specified a fixed palette like "webmap") then we wouldn't be
1253 quantizing to the caller specified palette.
1259 has_common_palette(i_img **imgs, int count, i_quantize *quant) {
1265 /* we try to build a common palette here, if we can manage that, then
1266 that's the palette we use */
1267 for (imgn = 0; imgn < count; ++imgn) {
1268 int eliminate_unused;
1269 if (imgs[imgn]->type != i_palette_type)
1272 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1273 &eliminate_unused)) {
1274 eliminate_unused = 1;
1277 if (eliminate_unused) {
1278 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1280 memset(used, 0, sizeof(used));
1282 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1283 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1284 for (x = 0; x < imgs[imgn]->xsize; ++x)
1291 /* assume all are in use */
1292 memset(used, 1, sizeof(used));
1295 col_count = i_colorcount(imgs[imgn]);
1296 for (i = 0; i < col_count; ++i) {
1299 i_getcolors(imgs[imgn], i, &c, 1);
1301 if (in_palette(&c, quant, quant->mc_count) < 0) {
1302 mm_log((1, " color not found in palette, no palette shortcut\n"));
1310 mm_log((1, " all colors found in palette, palette shortcut\n"));
1316 quant_paletted(i_quantize *quant, i_img *img) {
1317 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1319 i_palidx trans[256];
1323 /* build a translation table */
1324 for (i = 0; i < i_colorcount(img); ++i) {
1326 i_getcolors(img, i, &c, 1);
1327 trans[i] = in_palette(&c, quant, quant->mc_count);
1330 for (y = 0; y < img->ysize; ++y) {
1331 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1332 for (x = 0; x < img->xsize; ++x) {
1342 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1344 Internal. Low-level function that does the high-level GIF processing
1347 Returns non-zero on success.
1353 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1354 unsigned char *result = NULL;
1356 ColorMapObject *map;
1357 int scrw = 0, scrh = 0;
1358 int imgn, orig_count, orig_size;
1360 int trans_index = -1;
1363 i_img **glob_imgs; /* images that will use the global color map */
1365 i_color *orig_colors = quant->mc_colors;
1366 i_color *glob_colors = NULL;
1367 int glob_color_count = 0;
1368 int glob_want_trans;
1369 int glob_paletted = 0; /* the global map was made from the image palettes */
1370 int colors_paletted = 0;
1375 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1376 quant, gf, imgs, count));
1378 /* *((char *)0) = 1; */ /* used to break into the debugger */
1381 i_push_error(0, "No images provided to write");
1382 return 0; /* what are you smoking? */
1385 /* sanity is nice */
1386 if (quant->mc_size > 256)
1387 quant->mc_size = 256;
1388 if (quant->mc_count > quant->mc_size)
1389 quant->mc_count = quant->mc_size;
1391 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1393 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
1397 localmaps = mymalloc(sizeof(int) * count);
1398 glob_imgs = mymalloc(sizeof(i_img *) * count);
1400 glob_want_trans = 0;
1401 for (imgn = 0; imgn < count; ++imgn) {
1402 i_img *im = imgs[imgn];
1403 if (im->xsize > 0xFFFF || im->ysize > 0xFFFF) {
1404 i_push_error(0, "image too large for GIF");
1409 i_tags_get_int(&im->tags, "gif_left", 0, &posx);
1410 if (posx < 0) posx = 0;
1411 i_tags_get_int(&im->tags, "gif_top", 0, &posy);
1412 if (posy < 0) posy = 0;
1413 if (im->xsize + posx > scrw)
1414 scrw = im->xsize + posx;
1415 if (im->ysize + posy > scrh)
1416 scrh = im->ysize + posy;
1417 if (!i_tags_get_int(&im->tags, "gif_local_map", 0, localmaps+imgn))
1418 localmaps[imgn] = 0;
1419 if (localmaps[imgn])
1422 if (im->channels == 4) {
1423 glob_want_trans = 1;
1425 glob_imgs[glob_img_count++] = im;
1428 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1430 if (scrw > 0xFFFF || scrh > 0xFFFF) {
1431 i_push_error(0, "screen size too large for GIF");
1435 orig_count = quant->mc_count;
1436 orig_size = quant->mc_size;
1438 if (glob_img_count) {
1440 glob_colors = mymalloc(sizeof(i_color) * quant->mc_size);
1441 quant->mc_colors = glob_colors;
1442 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1443 /* we have some images that want to use the global map */
1444 if (glob_want_trans && quant->mc_count == 256) {
1445 mm_log((2, " disabling transparency for global map - no space\n"));
1446 glob_want_trans = 0;
1448 if (glob_want_trans && quant->mc_size == 256) {
1449 mm_log((2, " reserving color for transparency\n"));
1453 i_quant_makemap(quant, glob_imgs, glob_img_count);
1454 glob_paletted = has_common_palette(glob_imgs, glob_img_count, quant);
1455 glob_color_count = quant->mc_count;
1456 quant->mc_colors = orig_colors;
1459 /* use the global map if we have one, otherwise use the local map */
1462 quant->mc_colors = glob_colors;
1463 quant->mc_count = glob_color_count;
1464 want_trans = glob_want_trans && imgs[0]->channels == 4;
1466 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1468 if (gif_background < 0)
1470 if (gif_background >= glob_color_count)
1474 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1475 i_quant_makemap(quant, imgs, 1);
1476 colors_paletted = has_common_palette(imgs, 1, quant);
1479 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1480 myfree(glob_colors);
1483 quant->mc_colors = orig_colors;
1485 mm_log((1, "Error in MakeMapObject"));
1490 /* since we don't know how big some the local palettes could be
1491 we need to base the bits on the maximum number of colors */
1492 while (orig_size > (1 << color_bits))
1496 int count = quant->mc_count;
1499 while (count > (1 << color_bits))
1503 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1504 gif_background, map) == GIF_ERROR) {
1505 myfree(glob_colors);
1508 quant->mc_colors = orig_colors;
1510 i_push_error(0, "Could not save screen descriptor");
1514 mm_log((1, "Error in EGifPutScreenDesc."));
1519 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1521 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1524 if (!localmaps[0]) {
1526 colors_paletted = glob_paletted;
1529 /* if this image has a global map the colors in quant don't
1530 belong to this image, so build a palette */
1532 /* generate the local map for this image */
1533 quant->mc_colors = orig_colors;
1534 quant->mc_size = orig_size;
1535 quant->mc_count = orig_count;
1536 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1538 /* if the caller gives us too many colours we can't do transparency */
1539 if (want_trans && quant->mc_count == 256)
1541 /* if they want transparency but give us a big size, make it smaller
1542 to give room for a transparency colour */
1543 if (want_trans && quant->mc_size == 256)
1545 i_quant_makemap(quant, imgs, 1);
1546 colors_paletted = has_common_palette(imgs, 1, quant);
1547 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1548 myfree(glob_colors);
1552 quant->mc_colors = orig_colors;
1553 mm_log((1, "Error in MakeMapObject"));
1558 /* the map we wrote was the map for this image - don't set the local
1564 if (colors_paletted)
1565 result = quant_paletted(quant, imgs[0]);
1567 result = i_quant_translate(quant, imgs[0]);
1569 myfree(glob_colors);
1572 quant->mc_colors = orig_colors;
1577 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
1578 trans_index = quant->mc_count;
1581 if (!do_ns_loop(gf, imgs[0])) {
1582 myfree(glob_colors);
1585 quant->mc_colors = orig_colors;
1589 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1590 myfree(glob_colors);
1593 quant->mc_colors = orig_colors;
1599 if (!do_comments(gf, imgs[0])) {
1600 myfree(glob_colors);
1603 quant->mc_colors = orig_colors;
1609 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1611 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1612 interlace, map) == GIF_ERROR) {
1613 myfree(glob_colors);
1616 quant->mc_colors = orig_colors;
1618 i_push_error(0, "Could not save image descriptor");
1620 mm_log((1, "Error in EGifPutImageDesc."));
1626 if (!do_write(gf, interlace, imgs[0], result)) {
1627 myfree(glob_colors);
1630 quant->mc_colors = orig_colors;
1637 /* that first awful image is out of the way, do the rest */
1638 for (imgn = 1; imgn < count; ++imgn) {
1639 if (localmaps[imgn]) {
1640 quant->mc_colors = orig_colors;
1641 quant->mc_count = orig_count;
1642 quant->mc_size = orig_size;
1644 want_trans = quant->transp != tr_none
1645 && imgs[imgn]->channels == 4;
1646 /* if the caller gives us too many colours we can't do transparency */
1647 if (want_trans && quant->mc_count == 256)
1649 /* if they want transparency but give us a big size, make it smaller
1650 to give room for a transparency colour */
1651 if (want_trans && quant->mc_size == 256)
1654 if (has_common_palette(imgs+imgn, 1, quant)) {
1655 result = quant_paletted(quant, imgs[imgn]);
1658 i_quant_makemap(quant, imgs+imgn, 1);
1659 result = i_quant_translate(quant, imgs[imgn]);
1662 myfree(glob_colors);
1665 quant->mc_colors = orig_colors;
1667 mm_log((1, "error in i_quant_translate()"));
1671 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1672 trans_index = quant->mc_count;
1675 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1676 myfree(glob_colors);
1679 quant->mc_colors = orig_colors;
1682 mm_log((1, "Error in MakeMapObject."));
1687 quant->mc_colors = glob_colors;
1688 quant->mc_count = glob_color_count;
1690 result = quant_paletted(quant, imgs[imgn]);
1692 result = i_quant_translate(quant, imgs[imgn]);
1693 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1695 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1696 trans_index = quant->mc_count;
1701 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1702 myfree(glob_colors);
1705 quant->mc_colors = orig_colors;
1711 if (!do_comments(gf, imgs[imgn])) {
1712 myfree(glob_colors);
1715 quant->mc_colors = orig_colors;
1721 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1723 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1726 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1728 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1729 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1730 myfree(glob_colors);
1733 quant->mc_colors = orig_colors;
1735 i_push_error(0, "Could not save image descriptor");
1740 mm_log((1, "Error in EGifPutImageDesc."));
1746 if (!do_write(gf, interlace, imgs[imgn], result)) {
1747 myfree(glob_colors);
1750 quant->mc_colors = orig_colors;
1758 if (EGifCloseFile(gf) == GIF_ERROR) {
1759 myfree(glob_colors);
1763 i_push_error(0, "Could not close GIF file");
1764 mm_log((1, "Error in EGifCloseFile\n"));
1769 for (i = 0; i < glob_color_count; ++i)
1770 orig_colors[i] = glob_colors[i];
1773 myfree(glob_colors);
1776 quant->mc_colors = orig_colors;
1782 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1783 io_glue *ig = (io_glue *)gft->UserData;
1785 return i_io_write(ig, data, length);
1790 =item i_writegif_wiol(ig, quant, opts, imgs, count)
1795 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
1797 GifFileType *GifFile;
1802 gif_set_version(quant, imgs, count);
1804 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
1806 i_push_error(0, "Cannot create giflib callback object");
1807 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
1811 result = i_writegif_low(quant, GifFile, imgs, count);
1820 =item gif_error_msg(int code)
1822 Grabs the most recent giflib error code from GifLastError() and
1823 returns a string that describes that error.
1825 The returned pointer points to a static buffer, either from a literal
1826 C string or a static buffer.
1831 static char const *gif_error_msg(int code) {
1832 static char msg[80];
1835 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1836 return "Failed to open given file";
1838 case E_GIF_ERR_WRITE_FAILED:
1839 return "Write failed";
1841 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1842 return "Screen descriptor already passed to giflib";
1844 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1845 return "Image descriptor already passed to giflib";
1847 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1848 return "Neither global nor local color map set";
1850 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1851 return "Too much pixel data passed to giflib";
1853 case E_GIF_ERR_NOT_ENOUGH_MEM:
1854 return "Out of memory";
1856 case E_GIF_ERR_DISK_IS_FULL:
1857 return "Disk is full";
1859 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1860 return "File close failed";
1862 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1863 return "File not writable";
1865 case D_GIF_ERR_OPEN_FAILED:
1866 return "Failed to open file";
1868 case D_GIF_ERR_READ_FAILED:
1869 return "Failed to read from file";
1871 case D_GIF_ERR_NOT_GIF_FILE:
1872 return "File is not a GIF file";
1874 case D_GIF_ERR_NO_SCRN_DSCR:
1875 return "No screen descriptor detected - invalid file";
1877 case D_GIF_ERR_NO_IMAG_DSCR:
1878 return "No image descriptor detected - invalid file";
1880 case D_GIF_ERR_NO_COLOR_MAP:
1881 return "No global or local color map found";
1883 case D_GIF_ERR_WRONG_RECORD:
1884 return "Wrong record type detected - invalid file?";
1886 case D_GIF_ERR_DATA_TOO_BIG:
1887 return "Data in file too big for image";
1889 case D_GIF_ERR_NOT_ENOUGH_MEM:
1890 return "Out of memory";
1892 case D_GIF_ERR_CLOSE_FAILED:
1893 return "Close failed";
1895 case D_GIF_ERR_NOT_READABLE:
1896 return "File not opened for read";
1898 case D_GIF_ERR_IMAGE_DEFECT:
1899 return "Defective image";
1901 case D_GIF_ERR_EOF_TOO_SOON:
1902 return "Unexpected EOF - invalid file";
1905 #ifdef IMAGER_SNPRINTF
1906 snprintf(msg, sizeof(msg), "Unknown giflib error code %d", code);
1908 sprintf(msg, "Unknown giflib error code %d", code);
1915 =item gif_push_error()
1917 Utility function that takes the current GIF error code, converts it to
1918 an error message and pushes it on the error stack.
1923 static void gif_push_error(void) {
1924 int code = GifLastError(); /* clears saved error */
1926 i_push_error(code, gif_error_msg(code));
1932 The Netscape loop extension isn't implemented. Giflib's extension
1933 writing code doesn't seem to support writing named extensions in this
1936 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1937 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1940 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1941 supplied interface in giflib 4.1.0 causes a SEGV in
1942 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1946 Arnar M. Hrafnkelsson, addi@umich.edu