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) {
419 for (i = 0; i < count; ++i)
420 i_img_destroy(imgs[i]);
425 =item i_readgif_multi_low(GifFileType *gf, int *count, int page)
427 Reads one of more gif images from the given GIF file.
429 Returns a pointer to an array of i_img *, and puts the count into
432 If page is not -1 then the given image _only_ is returned from the
433 file, where the first image is 0, the second 1 and so on.
435 Unlike the normal i_readgif*() functions the images are paletted
436 images rather than a combined RGB image.
438 This functions sets tags on the images returned:
444 the offset of the image from the left of the "screen" ("Image Left
449 the offset of the image from the top of the "screen" ("Image Top Position")
453 non-zero if the image was interlaced ("Interlace Flag")
455 =item gif_screen_width
457 =item gif_screen_height
459 the size of the logical screen ("Logical Screen Width",
460 "Logical Screen Height")
464 Non-zero if this image had a local color map.
468 The index in the global colormap of the logical screen's background
469 color. This is only set if the current image uses the global
472 =item gif_trans_index
474 The index of the color in the colormap used for transparency. If the
475 image has a transparency then it is returned as a 4 channel image with
476 the alpha set to zero in this palette entry. ("Transparent Color Index")
480 The delay until the next frame is displayed, in 1/100 of a second.
485 whether or not a user input is expected before continuing (view dependent)
490 how the next frame is displayed ("Disposal Method")
494 the number of loops from the Netscape Loop extension. This may be zero.
498 the first block of the first gif comment before each image.
502 Where applicable, the ("name") is the name of that field from the GIF89
508 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
510 int i, j, Size, Width, Height, ExtCode, Count;
511 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
512 ColorMapObject *ColorMap;
514 GifRecordType RecordType;
515 GifByteType *Extension;
519 int trans_index; /* transparent index if we see a GCE */
520 int gif_delay; /* delay from a GCE */
521 int user_input; /* user input flag from a GCE */
522 int disposal; /* disposal method from a GCE */
525 char *comment = NULL; /* a comment */
526 i_img **results = NULL;
527 int result_alloc = 0;
532 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
534 BackGround = GifFile->SBackGroundColor;
536 Size = GifFile->SWidth * sizeof(GifPixelType);
538 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
539 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
541 /* Scan the content of the GIF file and load the image(s) in: */
543 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
545 i_push_error(0, "Unable to get record type");
546 free_images(results, *count);
547 DGifCloseFile(GifFile);
552 switch (RecordType) {
553 case IMAGE_DESC_RECORD_TYPE:
554 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
556 i_push_error(0, "Unable to get image descriptor");
557 free_images(results, *count);
558 DGifCloseFile(GifFile);
563 Width = GifFile->Image.Width;
564 Height = GifFile->Image.Height;
565 if (page == -1 || page == ImageNum) {
566 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
567 mm_log((1, "Adding local colormap\n"));
568 ColorMapSize = ColorMap->ColorCount;
570 /* No colormap and we are about to read in the image -
572 mm_log((1, "Going in with no colormap\n"));
573 i_push_error(0, "Image does not have a local or a global color map");
574 free_images(results, *count);
575 DGifCloseFile(GifFile);
581 if (got_gce && trans_index >= 0)
583 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
584 free_images(results, *count);
585 mm_log((1, "i_readgif: image size exceeds limits\n"));
589 img = i_img_pal_new(Width, Height, channels, 256);
591 free_images(results, *count);
594 /* populate the palette of the new image */
595 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
596 for (i = 0; i < ColorMapSize; ++i) {
598 col.rgba.r = ColorMap->Colors[i].Red;
599 col.rgba.g = ColorMap->Colors[i].Green;
600 col.rgba.b = ColorMap->Colors[i].Blue;
601 if (channels == 4 && trans_index == i)
606 i_addcolors(img, &col, 1);
609 if (*count > result_alloc) {
610 if (result_alloc == 0) {
612 results = mymalloc(result_alloc * sizeof(i_img *));
615 /* myrealloc never fails (it just dies if it can't allocate) */
617 results = myrealloc(results, result_alloc * sizeof(i_img *));
620 results[*count-1] = img;
621 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
622 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
624 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
625 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
626 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
627 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
628 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
629 i_tags_addn(&img->tags, "gif_background", 0,
630 GifFile->SBackGroundColor);
632 if (GifFile->Image.ColorMap) {
633 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
636 if (trans_index >= 0) {
638 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
639 i_getcolors(img, trans_index, &trans, 1);
640 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
642 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
643 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
644 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
648 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
650 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
655 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
656 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
658 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
659 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
660 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
661 free_images(results, *count);
662 DGifCloseFile(GifFile);
667 if (GifFile->Image.Interlace) {
668 for (Count = i = 0; i < 4; i++) {
669 for (j = InterlacedOffset[i]; j < Height;
670 j += InterlacedJumps[i]) {
672 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
674 i_push_error(0, "Reading GIF line");
675 free_images(results, *count);
676 DGifCloseFile(GifFile);
681 i_ppal(img, 0, Width, j, GifRow);
686 for (i = 0; i < Height; i++) {
687 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
689 i_push_error(0, "Reading GIF line");
690 free_images(results, *count);
691 DGifCloseFile(GifFile);
696 i_ppal(img, 0, Width, i, GifRow);
700 /* must be only one image wanted and that was it */
703 DGifCloseFile(GifFile);
709 /* whether interlaced or not, it has the same number of lines */
710 /* giflib does't have an interface to skip the image data */
711 for (i = 0; i < Height; i++) {
712 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
714 i_push_error(0, "Reading GIF line");
715 free_images(results, *count);
717 DGifCloseFile(GifFile);
722 /* kill the comment so we get the right comment for the page */
730 case EXTENSION_RECORD_TYPE:
731 /* Skip any extension blocks in file: */
732 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
734 i_push_error(0, "Reading extension record");
735 free_images(results, *count);
736 DGifCloseFile(GifFile);
739 if (ExtCode == 0xF9) {
741 if (Extension[1] & 1)
742 trans_index = Extension[4];
745 gif_delay = Extension[2] + 256 * Extension[3];
746 user_input = (Extension[0] & 2) != 0;
747 disposal = (Extension[0] >> 2) & 3;
749 if (ExtCode == 0xFF && *Extension == 11) {
750 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
751 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
753 i_push_error(0, "reading loop extension");
754 free_images(results, *count);
755 DGifCloseFile(GifFile);
758 if (Extension && *Extension == 3) {
760 ns_loop = Extension[2] + 256 * Extension[3];
764 else if (ExtCode == 0xFE) {
765 /* while it's possible for a GIF file to contain more than one
766 comment, I'm only implementing a single comment per image,
767 with the comment saved into the following image.
768 If someone wants more than that they can implement it.
769 I also don't handle comments that take more than one block.
772 comment = mymalloc(*Extension+1);
773 memcpy(comment, Extension+1, *Extension);
774 comment[*Extension] = '\0';
777 while (Extension != NULL) {
778 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
780 i_push_error(0, "reading next block of extension");
781 free_images(results, *count);
782 DGifCloseFile(GifFile);
787 case TERMINATE_RECORD_TYPE:
789 default: /* Should be trapped by DGifGetRecordType. */
792 } while (RecordType != TERMINATE_RECORD_TYPE);
796 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
804 if (DGifCloseFile(GifFile) == GIF_ERROR) {
806 i_push_error(0, "Closing GIF file object");
807 free_images(results, *count);
811 if (ImageNum && page != -1) {
812 /* there were images, but the page selected wasn't found */
813 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
814 free_images(results, *count);
822 /* giflib declares this incorrectly as EgifOpen */
823 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
825 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
829 =item i_readgif_multi_wiol(ig, int *count)
835 i_readgif_multi_wiol(io_glue *ig, int *count) {
836 io_glue_commit_types(ig);
838 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
839 return i_readgif_multi(ig->source.fdseek.fd, count);
843 GifFileType *GifFile;
847 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
849 i_push_error(0, "Cannot create giflib callback object");
850 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
854 return i_readgif_multi_low(GifFile, count, -1);
857 i_push_error(0, "callbacks not supported with giflib3");
865 =item i_readgif_multi(int fd, int *count)
870 i_readgif_multi(int fd, int *count) {
871 GifFileType *GifFile;
875 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
877 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
879 i_push_error(0, "Cannot create giflib file object");
880 mm_log((1,"i_readgif: Unable to open file\n"));
884 return i_readgif_multi_low(GifFile, count, -1);
888 =item i_readgif_multi_scalar(char *data, int length, int *count)
893 i_readgif_multi_scalar(char *data, int length, int *count) {
895 GifFileType *GifFile;
896 struct gif_scalar_info gsi;
904 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
905 data, length, count));
907 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
909 i_push_error(0, "Cannot create giflib callback object");
910 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
914 return i_readgif_multi_low(GifFile, count, -1);
921 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
923 Read a GIF file into an Imager RGB file, the data of the GIF file is
924 retreived by callin the user supplied callback function.
926 This function is only used with giflib 4 and higher.
932 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
934 GifFileType *GifFile;
937 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
941 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
942 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
944 i_push_error(0, "Cannot create giflib callback object");
945 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
950 result = i_readgif_multi_low(GifFile, count, -1);
951 i_free_gen_read_data(gci);
960 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
962 Write I<img> to the file handle I<fd>. The resulting GIF will use a
963 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
964 colours taken from I<fixed>.
966 Returns non-zero on success.
972 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
976 memset(&quant, 0, sizeof(quant));
977 quant.make_colors = mc_addi;
978 quant.mc_colors = colors;
979 quant.mc_size = 1<<max_colors;
980 quant.mc_count = fixedlen;
981 memcpy(colors, fixed, fixedlen * sizeof(i_color));
982 quant.translate = pt_perturb;
983 quant.perturb = pixdev;
984 return i_writegif_gen(&quant, fd, &im, 1);
988 =item i_writegifmc(i_img *im, int fd, int max_colors)
990 Write I<img> to the file handle I<fd>. The resulting GIF will use a
991 maximum of 1<<I<max_colours> colours.
993 Returns non-zero on success.
999 i_writegifmc(i_img *im, int fd, int max_colors) {
1000 i_color colors[256];
1003 /* *(char *)0 = 1; */
1005 memset(&quant, 0, sizeof(quant));
1006 quant.make_colors = mc_none; /* ignored for pt_giflib */
1007 quant.mc_colors = colors;
1008 quant.mc_size = 1 << max_colors;
1010 quant.translate = pt_giflib;
1011 return i_writegif_gen(&quant, fd, &im, 1);
1016 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
1018 Reads a GIF file from an in memory copy of the file. This can be used
1019 if you get the 'file' from some source other than an actual file (or
1020 some other file handle).
1022 This function is only available with giflib 4 and higher.
1027 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
1028 #if IM_GIFMAJOR >= 4
1029 GifFileType *GifFile;
1030 struct gif_scalar_info gsi;
1038 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
1039 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
1041 i_push_error(0, "Cannot create giflib callback object");
1042 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
1046 return i_readgif_low(GifFile, colour_table, colours);
1052 #if IM_GIFMAJOR >= 4
1055 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
1057 Internal. The reader callback wrapper passed to giflib.
1059 This function is only used with giflib 4 and higher.
1065 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1066 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1073 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1075 Read a GIF file into an Imager RGB file, the data of the GIF file is
1076 retreived by callin the user supplied callback function.
1078 This function is only used with giflib 4 and higher.
1084 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1085 #if IM_GIFMAJOR >= 4
1086 GifFileType *GifFile;
1089 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1093 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1094 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1096 i_push_error(0, "Cannot create giflib callback object");
1097 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1102 result = i_readgif_low(GifFile, colour_table, colours);
1103 i_free_gen_read_data(gci);
1108 i_push_error(0, "callbacks not supported with giflib3");
1114 #if IM_GIFMAJOR >= 4
1117 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1118 io_glue *ig = (io_glue *)gft->UserData;
1120 return ig->readcb(ig, buf, length);
1126 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1127 io_glue_commit_types(ig);
1129 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1130 int fd = dup(ig->source.fdseek.fd);
1132 i_push_error(errno, "dup() failed");
1135 return i_readgif(fd, color_table, colors);
1138 #if IM_GIFMAJOR >= 4
1139 GifFileType *GifFile;
1143 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1145 i_push_error(0, "Cannot create giflib callback object");
1146 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1150 return i_readgif_low(GifFile, color_table, colors);
1154 i_push_error(0, "callbacks not supported with giflib3");
1162 =item i_readgif_single_low(GifFile, page)
1164 Lower level function to read a single image from a GIF.
1166 page must be non-negative.
1171 i_readgif_single_low(GifFileType *GifFile, int page) {
1175 imgs = i_readgif_multi_low(GifFile, &count, page);
1177 if (imgs && count) {
1178 i_img *result = imgs[0];
1184 /* i_readgif_multi_low() handles the errors appropriately */
1190 =item i_readgif_single_wiol(ig, page)
1192 Read a single page from a GIF image file, where the page is indexed
1195 Returns NULL if the page isn't found.
1201 i_readgif_single_wiol(io_glue *ig, int page) {
1202 io_glue_commit_types(ig);
1207 i_push_error(0, "page must be non-negative");
1211 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1212 GifFileType *GifFile;
1213 int fd = dup(ig->source.fdseek.fd);
1215 i_push_error(errno, "dup() failed");
1218 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
1220 i_push_error(0, "Cannot create giflib file object");
1221 mm_log((1,"i_readgif: Unable to open file\n"));
1224 return i_readgif_single_low(GifFile, page);
1227 #if IM_GIFMAJOR >= 4
1228 GifFileType *GifFile;
1230 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1232 i_push_error(0, "Cannot create giflib callback object");
1233 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1237 return i_readgif_single_low(GifFile, page);
1239 i_push_error(0, "callbacks not supported with giflib3");
1247 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1249 Internal. Low level image write function. Writes in interlace if
1250 that was requested in the GIF options.
1252 Returns non-zero on success.
1257 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1260 for (i = 0; i < 4; ++i) {
1261 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1262 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1264 i_push_error(0, "Could not save image data:");
1265 mm_log((1, "Error in EGifPutLine\n"));
1274 for (y = 0; y < img->ysize; ++y) {
1275 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1277 i_push_error(0, "Could not save image data:");
1278 mm_log((1, "Error in EGifPutLine\n"));
1290 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1292 Internal. Writes the GIF graphics control extension, if necessary.
1294 Returns non-zero on success.
1298 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1300 unsigned char gce[4] = {0};
1304 int disposal_method;
1308 gce[3] = trans_index;
1311 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1312 gce[1] = delay % 256;
1313 gce[2] = delay / 256;
1316 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1321 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1322 gce[0] |= (disposal_method & 3) << 2;
1326 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1328 i_push_error(0, "Could not save GCE");
1335 =item do_comments(gf, img)
1337 Write any comments in the image.
1341 static int do_comments(GifFileType *gf, i_img *img) {
1344 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1345 if (img->tags.tags[pos].data) {
1346 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1352 sprintf(buf, "%d", img->tags.tags[pos].idata);
1353 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1363 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1365 Internal. Add the Netscape2.0 loop extension block, if requested.
1367 The code for this function is currently "#if 0"ed out since the giflib
1368 extension writing code currently doesn't seem to support writing
1369 application extension blocks.
1373 static int do_ns_loop(GifFileType *gf, i_img *img)
1375 /* EGifPutExtension() doesn't appear to handle application
1376 extension blocks in any way
1377 Since giflib wraps the fd with a FILE * (and puts that in its
1378 private data), we can't do an end-run and write the data
1380 There's no open interface that takes a FILE * either, so we
1381 can't workaround it that way either.
1382 If giflib's callback interface wasn't broken by default, I'd
1383 force file writes to use callbacks, but it is broken by default.
1386 /* yes this was another attempt at supporting the loop extension */
1388 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1389 unsigned char nsle[12] = "NETSCAPE2.0";
1390 unsigned char subblock[3];
1391 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1393 i_push_error(0, "writing loop extension");
1397 subblock[1] = loop_count % 256;
1398 subblock[2] = loop_count / 256;
1399 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1401 i_push_error(0, "writing loop extention sub-block");
1404 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1406 i_push_error(0, "writing loop extension terminator");
1415 =item make_gif_map(i_quantize *quant, int want_trans)
1417 Create a giflib color map object from an Imager color map.
1422 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1424 GifColorType colors[256];
1426 int size = quant->mc_count;
1428 ColorMapObject *map;
1431 for (i = 0; i < quant->mc_count; ++i) {
1432 colors[i].Red = quant->mc_colors[i].rgb.r;
1433 colors[i].Green = quant->mc_colors[i].rgb.g;
1434 colors[i].Blue = quant->mc_colors[i].rgb.b;
1437 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1438 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1439 colors[size].Red = trans.rgb.r;
1440 colors[size].Green = trans.rgb.g;
1441 colors[size].Blue = trans.rgb.b;
1445 while (map_size < size)
1447 /* giflib spews for 1 colour maps, reasonable, I suppose */
1450 while (i < map_size) {
1451 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1455 map = MakeMapObject(map_size, colors);
1456 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1459 i_push_error(0, "Could not create color map object");
1466 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1468 We need to call EGifSetGifVersion() before opening the file - put that
1471 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1472 giflib 4.1.0 has code:
1474 static char *GifVersionPrefix = GIF87_STAMP;
1476 and the code that sets the version internally does:
1478 strncpy(&GifVersionPrefix[3], Version, 3);
1480 which is very broken.
1482 Failing to set the correct GIF version doesn't seem to cause a problem
1488 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1489 /* the following crashed giflib
1490 the EGifSetGifVersion() is seriously borked in giflib
1491 it's less borked in the ungiflib beta, but we don't have a mechanism
1493 Needs to be updated to support tags.
1494 if (opts->delay_count
1495 || opts->user_input_count
1496 || opts->disposal_count
1498 || quant->transp != tr_none)
1499 EGifSetGifVersion("89a");
1501 EGifSetGifVersion("87a");
1506 in_palette(i_color *c, i_quantize *quant, int size) {
1509 for (i = 0; i < size; ++i) {
1510 if (c->channel[0] == quant->mc_colors[i].channel[0]
1511 && c->channel[1] == quant->mc_colors[i].channel[1]
1512 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1521 =item has_common_palette(imgs, count, quant, want_trans)
1523 Tests if all the given images are paletted and have a common palette,
1524 if they do it builds that palette.
1526 A possible improvement might be to eliminate unused colors in the
1532 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1534 int size = quant->mc_count;
1539 /* we try to build a common palette here, if we can manage that, then
1540 that's the palette we use */
1541 for (imgn = 0; imgn < count; ++imgn) {
1542 int eliminate_unused;
1543 if (imgs[imgn]->type != i_palette_type)
1546 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1547 &eliminate_unused)) {
1548 eliminate_unused = 1;
1551 if (eliminate_unused) {
1552 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1554 memset(used, 0, sizeof(used));
1556 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1557 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1558 for (x = 0; x < imgs[imgn]->xsize; ++x)
1565 /* assume all are in use */
1566 memset(used, 1, sizeof(used));
1569 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1572 i_getcolors(imgs[imgn], i, &c, 1);
1574 if (in_palette(&c, quant, size) < 0) {
1575 if (size < quant->mc_size) {
1576 quant->mc_colors[size++] = c;
1579 /* oops, too many colors */
1587 quant->mc_count = size;
1593 quant_paletted(i_quantize *quant, i_img *img) {
1594 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1596 i_palidx trans[256];
1600 /* build a translation table */
1601 for (i = 0; i < i_colorcount(img); ++i) {
1603 i_getcolors(img, i, &c, 1);
1604 trans[i] = in_palette(&c, quant, quant->mc_count);
1607 for (y = 0; y < img->ysize; ++y) {
1608 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1609 for (x = 0; x < img->xsize; ++x) {
1619 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1621 Internal. Low-level function that does the high-level GIF processing
1624 Returns non-zero on success.
1630 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1631 unsigned char *result;
1633 ColorMapObject *map;
1634 int scrw = 0, scrh = 0;
1635 int imgn, orig_count, orig_size;
1641 i_img **glob_imgs; /* images that will use the global color map */
1643 i_color *orig_colors = quant->mc_colors;
1644 i_color *glob_colors = NULL;
1645 int glob_color_count;
1646 int glob_want_trans;
1647 int glob_paletted; /* the global map was made from the image palettes */
1648 int colors_paletted;
1653 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1654 quant, gf, imgs, count));
1656 /* *((char *)0) = 1; */ /* used to break into the debugger */
1659 i_push_error(0, "No images provided to write");
1660 return 0; /* what are you smoking? */
1663 i_mempool_init(&mp);
1665 /* sanity is nice */
1666 if (quant->mc_size > 256)
1667 quant->mc_size = 256;
1668 if (quant->mc_count > quant->mc_size)
1669 quant->mc_count = quant->mc_size;
1671 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1673 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1677 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1678 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1680 glob_want_trans = 0;
1681 for (imgn = 0; imgn < count; ++imgn) {
1683 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1684 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1685 if (imgs[imgn]->xsize + posx > scrw)
1686 scrw = imgs[imgn]->xsize + posx;
1687 if (imgs[imgn]->ysize + posy > scrh)
1688 scrh = imgs[imgn]->ysize + posy;
1689 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1690 localmaps[imgn] = 0;
1691 if (localmaps[imgn])
1694 if (imgs[imgn]->channels == 4) {
1695 glob_want_trans = 1;
1697 glob_imgs[glob_img_count++] = imgs[imgn];
1700 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1702 orig_count = quant->mc_count;
1703 orig_size = quant->mc_size;
1705 if (glob_img_count) {
1707 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1708 quant->mc_colors = glob_colors;
1709 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1710 /* we have some images that want to use the global map */
1711 if (glob_want_trans && quant->mc_count == 256) {
1712 mm_log((2, " disabling transparency for global map - no space\n"));
1713 glob_want_trans = 0;
1715 if (glob_want_trans && quant->mc_size == 256) {
1716 mm_log((2, " reserving color for transparency\n"));
1719 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1724 quant_makemap(quant, glob_imgs, glob_img_count);
1726 glob_color_count = quant->mc_count;
1727 quant->mc_colors = orig_colors;
1730 /* use the global map if we have one, otherwise use the local map */
1733 quant->mc_colors = glob_colors;
1734 quant->mc_count = glob_color_count;
1735 want_trans = glob_want_trans && imgs[0]->channels == 4;
1737 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1739 if (gif_background < 0)
1741 if (gif_background >= glob_color_count)
1745 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1746 if (has_common_palette(imgs, 1, quant, want_trans)) {
1747 colors_paletted = 1;
1750 colors_paletted = 0;
1751 quant_makemap(quant, imgs, 1);
1754 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1755 i_mempool_destroy(&mp);
1756 quant->mc_colors = orig_colors;
1758 mm_log((1, "Error in MakeMapObject"));
1763 /* since we don't know how big some the local palettes could be
1764 we need to base the bits on the maximum number of colors */
1765 while (orig_size > (1 << color_bits))
1769 int count = quant->mc_count;
1772 while (count > (1 << color_bits))
1776 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1777 gif_background, map) == GIF_ERROR) {
1778 i_mempool_destroy(&mp);
1779 quant->mc_colors = orig_colors;
1781 i_push_error(0, "Could not save screen descriptor");
1785 mm_log((1, "Error in EGifPutScreenDesc."));
1790 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1792 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1795 if (!localmaps[0]) {
1797 colors_paletted = glob_paletted;
1800 /* if this image has a global map the colors in quant don't
1801 belong to this image, so build a palette */
1803 /* generate the local map for this image */
1804 quant->mc_colors = orig_colors;
1805 quant->mc_size = orig_size;
1806 quant->mc_count = orig_count;
1807 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1809 /* if the caller gives us too many colours we can't do transparency */
1810 if (want_trans && quant->mc_count == 256)
1812 /* if they want transparency but give us a big size, make it smaller
1813 to give room for a transparency colour */
1814 if (want_trans && quant->mc_size == 256)
1816 if (has_common_palette(imgs, 1, quant, want_trans)) {
1817 colors_paletted = 1;
1820 colors_paletted = 0;
1821 quant_makemap(quant, imgs, 1);
1823 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1824 i_mempool_destroy(&mp);
1826 quant->mc_colors = orig_colors;
1827 mm_log((1, "Error in MakeMapObject"));
1832 /* the map we wrote was the map for this image - don't set the local
1838 if (colors_paletted)
1839 result = quant_paletted(quant, imgs[0]);
1841 result = quant_translate(quant, imgs[0]);
1843 i_mempool_destroy(&mp);
1844 quant->mc_colors = orig_colors;
1849 quant_transparent(quant, result, imgs[0], quant->mc_count);
1850 trans_index = quant->mc_count;
1853 if (!do_ns_loop(gf, imgs[0])) {
1854 i_mempool_destroy(&mp);
1855 quant->mc_colors = orig_colors;
1859 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1860 i_mempool_destroy(&mp);
1861 quant->mc_colors = orig_colors;
1867 if (!do_comments(gf, imgs[0])) {
1868 i_mempool_destroy(&mp);
1869 quant->mc_colors = orig_colors;
1875 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1877 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1878 interlace, map) == GIF_ERROR) {
1879 i_mempool_destroy(&mp);
1880 quant->mc_colors = orig_colors;
1882 i_push_error(0, "Could not save image descriptor");
1884 mm_log((1, "Error in EGifPutImageDesc."));
1890 if (!do_write(gf, interlace, imgs[0], result)) {
1891 i_mempool_destroy(&mp);
1892 quant->mc_colors = orig_colors;
1899 /* that first awful image is out of the way, do the rest */
1900 for (imgn = 1; imgn < count; ++imgn) {
1901 if (localmaps[imgn]) {
1902 quant->mc_colors = orig_colors;
1903 quant->mc_count = orig_count;
1904 quant->mc_size = orig_size;
1906 want_trans = quant->transp != tr_none
1907 && imgs[imgn]->channels == 4;
1908 /* if the caller gives us too many colours we can't do transparency */
1909 if (want_trans && quant->mc_count == 256)
1911 /* if they want transparency but give us a big size, make it smaller
1912 to give room for a transparency colour */
1913 if (want_trans && quant->mc_size == 256)
1916 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1917 result = quant_paletted(quant, imgs[imgn]);
1920 quant_makemap(quant, imgs+imgn, 1);
1921 result = quant_translate(quant, imgs[imgn]);
1924 i_mempool_destroy(&mp);
1925 quant->mc_colors = orig_colors;
1927 mm_log((1, "error in quant_translate()"));
1931 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1932 trans_index = quant->mc_count;
1935 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1936 i_mempool_destroy(&mp);
1937 quant->mc_colors = orig_colors;
1940 mm_log((1, "Error in MakeMapObject."));
1945 quant->mc_colors = glob_colors;
1946 quant->mc_count = glob_color_count;
1948 result = quant_paletted(quant, imgs[imgn]);
1950 result = quant_translate(quant, imgs[imgn]);
1951 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1953 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1954 trans_index = quant->mc_count;
1959 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1960 i_mempool_destroy(&mp);
1961 quant->mc_colors = orig_colors;
1967 if (!do_comments(gf, imgs[imgn])) {
1968 i_mempool_destroy(&mp);
1969 quant->mc_colors = orig_colors;
1975 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1977 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1980 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1982 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1983 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1984 i_mempool_destroy(&mp);
1985 quant->mc_colors = orig_colors;
1987 i_push_error(0, "Could not save image descriptor");
1992 mm_log((1, "Error in EGifPutImageDesc."));
1998 if (!do_write(gf, interlace, imgs[imgn], result)) {
1999 i_mempool_destroy(&mp);
2000 quant->mc_colors = orig_colors;
2008 if (EGifCloseFile(gf) == GIF_ERROR) {
2009 i_mempool_destroy(&mp);
2011 i_push_error(0, "Could not close GIF file");
2012 mm_log((1, "Error in EGifCloseFile\n"));
2017 for (i = 0; i < glob_color_count; ++i)
2018 orig_colors[i] = glob_colors[i];
2021 i_mempool_destroy(&mp);
2022 quant->mc_colors = orig_colors;
2028 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
2030 General high-level function to write a GIF to a file.
2032 Writes the GIF images to the specified file handle using the options
2033 in quant and opts. See L<image.h/i_quantize> and
2034 L<image.h/i_gif_opts>.
2036 Returns non-zero on success.
2042 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
2046 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
2047 quant, fd, imgs, count));
2049 gif_set_version(quant, imgs, count);
2051 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
2053 i_push_error(0, "Cannot create GIF file object");
2054 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2058 return i_writegif_low(quant, gf, imgs, count);
2061 #if IM_GIFMAJOR >= 4
2064 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2066 Internal. Wrapper for the user write callback function.
2071 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
2073 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
2075 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
2081 =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)
2083 General high-level function to write a GIF using callbacks to send
2086 Returns non-zero on success.
2092 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
2093 int maxlength, i_img **imgs, int count)
2095 #if IM_GIFMAJOR >= 4
2097 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
2102 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
2103 quant, cb, userdata, maxlength, imgs, count));
2105 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
2107 i_push_error(0, "Cannot create GIF file object");
2108 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
2109 i_free_gen_write_data(gwd, 0);
2113 result = i_writegif_low(quant, gf, imgs, count);
2114 return i_free_gen_write_data(gwd, result);
2117 i_push_error(0, "callbacks not supported with giflib3");
2123 #if IM_GIFMAJOR >= 4
2126 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
2127 io_glue *ig = (io_glue *)gft->UserData;
2129 return ig->writecb(ig, data, length);
2135 =item i_writegif_wiol(ig, quant, opts, imgs, count)
2140 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
2142 io_glue_commit_types(ig);
2144 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2145 int fd = dup(ig->source.fdseek.fd);
2147 i_push_error(errno, "dup() failed");
2150 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2151 is called - dup it so the caller's fd isn't closed */
2152 return i_writegif_gen(quant, fd, imgs, count);
2155 #if IM_GIFMAJOR >= 4
2156 GifFileType *GifFile;
2161 gif_set_version(quant, imgs, count);
2163 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2165 i_push_error(0, "Cannot create giflib callback object");
2166 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2170 result = i_writegif_low(quant, GifFile, imgs, count);
2177 i_push_error(0, "callbacks not supported with giflib3");
2185 =item gif_error_msg(int code)
2187 Grabs the most recent giflib error code from GifLastError() and
2188 returns a string that describes that error.
2190 The returned pointer points to a static buffer, either from a literal
2191 C string or a static buffer.
2196 static char const *gif_error_msg(int code) {
2197 static char msg[80];
2200 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2201 return "Failed to open given file";
2203 case E_GIF_ERR_WRITE_FAILED:
2204 return "Write failed";
2206 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2207 return "Screen descriptor already passed to giflib";
2209 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2210 return "Image descriptor already passed to giflib";
2212 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2213 return "Neither global nor local color map set";
2215 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2216 return "Too much pixel data passed to giflib";
2218 case E_GIF_ERR_NOT_ENOUGH_MEM:
2219 return "Out of memory";
2221 case E_GIF_ERR_DISK_IS_FULL:
2222 return "Disk is full";
2224 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2225 return "File close failed";
2227 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2228 return "File not writable";
2230 case D_GIF_ERR_OPEN_FAILED:
2231 return "Failed to open file";
2233 case D_GIF_ERR_READ_FAILED:
2234 return "Failed to read from file";
2236 case D_GIF_ERR_NOT_GIF_FILE:
2237 return "File is not a GIF file";
2239 case D_GIF_ERR_NO_SCRN_DSCR:
2240 return "No screen descriptor detected - invalid file";
2242 case D_GIF_ERR_NO_IMAG_DSCR:
2243 return "No image descriptor detected - invalid file";
2245 case D_GIF_ERR_NO_COLOR_MAP:
2246 return "No global or local color map found";
2248 case D_GIF_ERR_WRONG_RECORD:
2249 return "Wrong record type detected - invalid file?";
2251 case D_GIF_ERR_DATA_TOO_BIG:
2252 return "Data in file too big for image";
2254 case D_GIF_ERR_NOT_ENOUGH_MEM:
2255 return "Out of memory";
2257 case D_GIF_ERR_CLOSE_FAILED:
2258 return "Close failed";
2260 case D_GIF_ERR_NOT_READABLE:
2261 return "File not opened for read";
2263 case D_GIF_ERR_IMAGE_DEFECT:
2264 return "Defective image";
2266 case D_GIF_ERR_EOF_TOO_SOON:
2267 return "Unexpected EOF - invalid file";
2270 sprintf(msg, "Unknown giflib error code %d", code);
2276 =item gif_push_error()
2278 Utility function that takes the current GIF error code, converts it to
2279 an error message and pushes it on the error stack.
2284 static void gif_push_error(void) {
2285 int code = GifLastError(); /* clears saved error */
2287 i_push_error(code, gif_error_msg(code));
2293 The Netscape loop extension isn't implemented. Giflib's extension
2294 writing code doesn't seem to support writing named extensions in this
2297 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2298 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2301 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2302 supplied interface in giflib 4.1.0 causes a SEGV in
2303 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2307 Arnar M. Hrafnkelsson, addi@umich.edu