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);
181 im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
183 Size = GifFile->SWidth * sizeof(GifPixelType);
185 GifRow = mymalloc(Size);
187 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
189 /* Scan the content of the GIF file and load the image(s) in: */
191 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
193 i_push_error(0, "Unable to get record type");
194 if (colour_table && *colour_table) {
195 myfree(*colour_table);
196 *colour_table = NULL;
200 DGifCloseFile(GifFile);
204 switch (RecordType) {
205 case IMAGE_DESC_RECORD_TYPE:
206 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
208 i_push_error(0, "Unable to get image descriptor");
209 if (colour_table && *colour_table) {
210 myfree(*colour_table);
211 *colour_table = NULL;
215 DGifCloseFile(GifFile);
219 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
220 mm_log((1, "Adding local colormap\n"));
221 ColorMapSize = ColorMap->ColorCount;
223 i_colortable_copy(colour_table, colours, ColorMap);
227 /* No colormap and we are about to read in the image - abandon for now */
228 mm_log((1, "Going in with no colormap\n"));
229 i_push_error(0, "Image does not have a local or a global color map");
230 /* we can't have allocated a colour table here */
233 DGifCloseFile(GifFile);
237 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
238 Col = GifFile->Image.Left;
239 Width = GifFile->Image.Width;
240 Height = GifFile->Image.Height;
242 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
244 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
245 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
246 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
247 if (colour_table && *colour_table) {
248 myfree(*colour_table);
249 *colour_table = NULL;
253 DGifCloseFile(GifFile);
256 if (GifFile->Image.Interlace) {
258 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
260 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
262 i_push_error(0, "Reading GIF line");
263 if (colour_table && *colour_table) {
264 myfree(*colour_table);
265 *colour_table = NULL;
269 DGifCloseFile(GifFile);
273 for (x = 0; x < Width; x++) {
274 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
275 col.rgb.r = ColorMapEntry->Red;
276 col.rgb.g = ColorMapEntry->Green;
277 col.rgb.b = ColorMapEntry->Blue;
278 i_ppix(im,Col+x,j,&col);
284 for (i = 0; i < Height; i++) {
285 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
287 i_push_error(0, "Reading GIF line");
288 if (colour_table && *colour_table) {
289 myfree(*colour_table);
290 *colour_table = NULL;
294 DGifCloseFile(GifFile);
298 for (x = 0; x < Width; x++) {
299 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
300 col.rgb.r = ColorMapEntry->Red;
301 col.rgb.g = ColorMapEntry->Green;
302 col.rgb.b = ColorMapEntry->Blue;
303 i_ppix(im, Col+x, Row, &col);
309 case EXTENSION_RECORD_TYPE:
310 /* Skip any extension blocks in file: */
311 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
313 i_push_error(0, "Reading extension record");
314 if (colour_table && *colour_table) {
315 myfree(*colour_table);
316 *colour_table = NULL;
320 DGifCloseFile(GifFile);
323 while (Extension != NULL) {
324 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
326 i_push_error(0, "reading next block of extension");
327 if (colour_table && *colour_table) {
328 myfree(*colour_table);
329 *colour_table = NULL;
333 DGifCloseFile(GifFile);
338 case TERMINATE_RECORD_TYPE:
340 default: /* Should be traps by DGifGetRecordType. */
343 } while (RecordType != TERMINATE_RECORD_TYPE);
347 if (DGifCloseFile(GifFile) == GIF_ERROR) {
349 i_push_error(0, "Closing GIF file object");
350 if (colour_table && *colour_table) {
351 myfree(*colour_table);
352 *colour_table = NULL;
361 =item i_readgif(int fd, int **colour_table, int *colours)
363 Reads in a GIF file from a file handle and converts it to an Imager
366 Returns the palette for the object in colour_table for colours
369 Returns NULL on failure.
375 i_readgif(int fd, int **colour_table, int *colours) {
376 GifFileType *GifFile;
380 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
382 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
384 i_push_error(0, "Cannot create giflib file object");
385 mm_log((1,"i_readgif: Unable to open file\n"));
389 return i_readgif_low(GifFile, colour_table, colours);
394 Internal function called by i_readgif_multi_low() in error handling
397 static void free_images(i_img **imgs, int count) {
399 for (i = 0; i < count; ++i)
400 i_img_destroy(imgs[i]);
405 =item i_readgif_multi_low(GifFileType *gf, int *count)
407 Reads one of more gif images from the given GIF file.
409 Returns a pointer to an array of i_img *, and puts the count into
412 Unlike the normal i_readgif*() functions the images are paletted
413 images rather than a combined RGB image.
415 This functions sets tags on the images returned:
421 the offset of the image from the left of the "screen" ("Image Left
426 the offset of the image from the top of the "screen" ("Image Top Position")
430 non-zero if the image was interlaced ("Interlace Flag")
432 =item gif_screen_width
434 =item gif_screen_height
436 the size of the logical screen ("Logical Screen Width",
437 "Logical Screen Height")
441 Non-zero if this image had a local color map.
445 The index in the global colormap of the logical screen's background
446 color. This is only set if the current image uses the global
449 =item gif_trans_index
451 The index of the color in the colormap used for transparency. If the
452 image has a transparency then it is returned as a 4 channel image with
453 the alpha set to zero in this palette entry. ("Transparent Color Index")
457 The delay until the next frame is displayed, in 1/100 of a second.
462 whether or not a user input is expected before continuing (view dependent)
467 how the next frame is displayed ("Disposal Method")
471 the number of loops from the Netscape Loop extension. This may be zero.
475 the first block of the first gif comment before each image.
479 Where applicable, the ("name") is the name of that field from the GIF89
485 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
487 int i, j, Size, Width, Height, ExtCode, Count, x;
488 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
489 ColorMapObject *ColorMap;
491 GifRecordType RecordType;
492 GifByteType *Extension;
496 int trans_index; /* transparent index if we see a GCE */
497 int gif_delay; /* delay from a GCE */
498 int user_input; /* user input flag from a GCE */
499 int disposal; /* disposal method from a GCE */
502 char *comment = NULL; /* a comment */
503 i_img **results = NULL;
504 int result_alloc = 0;
509 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
511 BackGround = GifFile->SBackGroundColor;
513 Size = GifFile->SWidth * sizeof(GifPixelType);
515 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
516 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
518 /* Scan the content of the GIF file and load the image(s) in: */
520 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
522 i_push_error(0, "Unable to get record type");
523 free_images(results, *count);
524 DGifCloseFile(GifFile);
528 switch (RecordType) {
529 case IMAGE_DESC_RECORD_TYPE:
530 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
532 i_push_error(0, "Unable to get image descriptor");
533 free_images(results, *count);
534 DGifCloseFile(GifFile);
538 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
539 mm_log((1, "Adding local colormap\n"));
540 ColorMapSize = ColorMap->ColorCount;
542 /* No colormap and we are about to read in the image -
544 mm_log((1, "Going in with no colormap\n"));
545 i_push_error(0, "Image does not have a local or a global color map");
546 free_images(results, *count);
547 DGifCloseFile(GifFile);
551 Width = GifFile->Image.Width;
552 Height = GifFile->Image.Height;
554 if (got_gce && trans_index >= 0)
556 img = i_img_pal_new(Width, Height, channels, 256);
557 /* populate the palette of the new image */
558 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
559 for (i = 0; i < ColorMapSize; ++i) {
561 col.rgba.r = ColorMap->Colors[i].Red;
562 col.rgba.g = ColorMap->Colors[i].Green;
563 col.rgba.b = ColorMap->Colors[i].Blue;
564 if (channels == 4 && trans_index == i)
569 i_addcolors(img, &col, 1);
572 if (*count > result_alloc) {
573 if (result_alloc == 0) {
575 results = mymalloc(result_alloc * sizeof(i_img *));
578 /* myrealloc never fails (it just dies if it can't allocate) */
580 results = myrealloc(results, result_alloc * sizeof(i_img *));
583 results[*count-1] = img;
584 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
586 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
587 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
588 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
589 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
590 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
591 i_tags_addn(&img->tags, "gif_background", 0,
592 GifFile->SBackGroundColor);
594 if (GifFile->Image.ColorMap) {
595 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
598 if (trans_index >= 0) {
600 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
601 i_getcolors(img, trans_index, &trans, 1);
602 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
604 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
605 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
606 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
610 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
612 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
618 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
619 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
621 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
622 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
623 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
624 free_images(results, *count);
625 DGifCloseFile(GifFile);
629 if (GifFile->Image.Interlace) {
630 for (Count = i = 0; i < 4; i++) {
631 for (j = InterlacedOffset[i]; j < Height;
632 j += InterlacedJumps[i]) {
634 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
636 i_push_error(0, "Reading GIF line");
637 free_images(results, *count);
638 DGifCloseFile(GifFile);
642 i_ppal(img, 0, Width, j, GifRow);
647 for (i = 0; i < Height; i++) {
648 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
650 i_push_error(0, "Reading GIF line");
651 free_images(results, *count);
652 DGifCloseFile(GifFile);
656 i_ppal(img, 0, Width, i, GifRow);
660 case EXTENSION_RECORD_TYPE:
661 /* Skip any extension blocks in file: */
662 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
664 i_push_error(0, "Reading extension record");
665 free_images(results, *count);
666 DGifCloseFile(GifFile);
669 if (ExtCode == 0xF9) {
671 if (Extension[1] & 1)
672 trans_index = Extension[4];
675 gif_delay = Extension[2] + 256 * Extension[3];
676 user_input = (Extension[0] & 2) != 0;
677 disposal = (Extension[0] >> 2) & 3;
679 if (ExtCode == 0xFF && *Extension == 11) {
680 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
681 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
683 i_push_error(0, "reading loop extension");
684 free_images(results, *count);
685 DGifCloseFile(GifFile);
688 if (Extension && *Extension == 3) {
690 ns_loop = Extension[2] + 256 * Extension[3];
694 else if (ExtCode == 0xFE) {
695 /* while it's possible for a GIF file to contain more than one
696 comment, I'm only implementing a single comment per image,
697 with the comment saved into the following image.
698 If someone wants more than that they can implement it.
699 I also don't handle comments that take more than one block.
702 comment = mymalloc(*Extension+1);
703 memcpy(comment, Extension+1, *Extension);
704 comment[*Extension] = '\0';
707 while (Extension != NULL) {
708 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
710 i_push_error(0, "reading next block of extension");
711 free_images(results, *count);
712 DGifCloseFile(GifFile);
717 case TERMINATE_RECORD_TYPE:
719 default: /* Should be trapped by DGifGetRecordType. */
722 } while (RecordType != TERMINATE_RECORD_TYPE);
726 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
734 if (DGifCloseFile(GifFile) == GIF_ERROR) {
736 i_push_error(0, "Closing GIF file object");
737 free_images(results, *count);
745 /* giflib declares this incorrectly as EgifOpen */
746 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
748 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
752 =item i_readgif_multi_wiol(ig, int *count)
758 i_readgif_multi_wiol(io_glue *ig, int *count) {
759 io_glue_commit_types(ig);
761 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
762 return i_readgif_multi(ig->source.fdseek.fd, count);
766 GifFileType *GifFile;
770 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
772 i_push_error(0, "Cannot create giflib callback object");
773 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
777 return i_readgif_multi_low(GifFile, count);
780 i_push_error(0, "callbacks not supported with giflib3");
788 =item i_readgif_multi(int fd, int *count)
793 i_readgif_multi(int fd, int *count) {
794 GifFileType *GifFile;
798 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
800 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
802 i_push_error(0, "Cannot create giflib file object");
803 mm_log((1,"i_readgif: Unable to open file\n"));
807 return i_readgif_multi_low(GifFile, count);
811 =item i_readgif_multi_scalar(char *data, int length, int *count)
816 i_readgif_multi_scalar(char *data, int length, int *count) {
818 GifFileType *GifFile;
819 struct gif_scalar_info gsi;
827 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
828 data, length, count));
830 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
832 i_push_error(0, "Cannot create giflib callback object");
833 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
837 return i_readgif_multi_low(GifFile, count);
844 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
846 Read a GIF file into an Imager RGB file, the data of the GIF file is
847 retreived by callin the user supplied callback function.
849 This function is only used with giflib 4 and higher.
855 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
857 GifFileType *GifFile;
860 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
864 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
865 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
867 i_push_error(0, "Cannot create giflib callback object");
868 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
873 result = i_readgif_multi_low(GifFile, count);
874 i_free_gen_read_data(gci);
883 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
885 Write I<img> to the file handle I<fd>. The resulting GIF will use a
886 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
887 colours taken from I<fixed>.
889 Returns non-zero on success.
895 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
899 memset(&quant, 0, sizeof(quant));
900 quant.make_colors = mc_addi;
901 quant.mc_colors = colors;
902 quant.mc_size = 1<<max_colors;
903 quant.mc_count = fixedlen;
904 memcpy(colors, fixed, fixedlen * sizeof(i_color));
905 quant.translate = pt_perturb;
906 quant.perturb = pixdev;
907 return i_writegif_gen(&quant, fd, &im, 1);
911 =item i_writegifmc(i_img *im, int fd, int max_colors)
913 Write I<img> to the file handle I<fd>. The resulting GIF will use a
914 maximum of 1<<I<max_colours> colours.
916 Returns non-zero on success.
922 i_writegifmc(i_img *im, int fd, int max_colors) {
926 /* *(char *)0 = 1; */
928 memset(&quant, 0, sizeof(quant));
929 quant.make_colors = mc_none; /* ignored for pt_giflib */
930 quant.mc_colors = colors;
931 quant.mc_size = 1 << max_colors;
933 quant.translate = pt_giflib;
934 return i_writegif_gen(&quant, fd, &im, 1);
939 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
941 Reads a GIF file from an in memory copy of the file. This can be used
942 if you get the 'file' from some source other than an actual file (or
943 some other file handle).
945 This function is only available with giflib 4 and higher.
950 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
952 GifFileType *GifFile;
953 struct gif_scalar_info gsi;
961 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
962 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
964 i_push_error(0, "Cannot create giflib callback object");
965 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
969 return i_readgif_low(GifFile, colour_table, colours);
978 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
980 Internal. The reader callback wrapper passed to giflib.
982 This function is only used with giflib 4 and higher.
988 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
989 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
996 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
998 Read a GIF file into an Imager RGB file, the data of the GIF file is
999 retreived by callin the user supplied callback function.
1001 This function is only used with giflib 4 and higher.
1007 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1008 #if IM_GIFMAJOR >= 4
1009 GifFileType *GifFile;
1012 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1016 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1017 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1019 i_push_error(0, "Cannot create giflib callback object");
1020 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1025 result = i_readgif_low(GifFile, colour_table, colours);
1026 i_free_gen_read_data(gci);
1031 i_push_error(0, "callbacks not supported with giflib3");
1037 #if IM_GIFMAJOR >= 4
1040 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1041 io_glue *ig = (io_glue *)gft->UserData;
1043 return ig->readcb(ig, buf, length);
1049 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1050 io_glue_commit_types(ig);
1052 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1053 int fd = dup(ig->source.fdseek.fd);
1055 i_push_error(errno, "dup() failed");
1058 return i_readgif(fd, color_table, colors);
1061 #if IM_GIFMAJOR >= 4
1062 GifFileType *GifFile;
1066 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1068 i_push_error(0, "Cannot create giflib callback object");
1069 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1073 return i_readgif_low(GifFile, color_table, colors);
1077 i_push_error(0, "callbacks not supported with giflib3");
1085 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1087 Internal. Low level image write function. Writes in interlace if
1088 that was requested in the GIF options.
1090 Returns non-zero on success.
1095 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1098 for (i = 0; i < 4; ++i) {
1099 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1100 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1102 i_push_error(0, "Could not save image data:");
1103 mm_log((1, "Error in EGifPutLine\n"));
1112 for (y = 0; y < img->ysize; ++y) {
1113 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1115 i_push_error(0, "Could not save image data:");
1116 mm_log((1, "Error in EGifPutLine\n"));
1128 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1130 Internal. Writes the GIF graphics control extension, if necessary.
1132 Returns non-zero on success.
1136 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1138 unsigned char gce[4] = {0};
1142 int disposal_method;
1146 gce[3] = trans_index;
1149 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1150 gce[1] = delay % 256;
1151 gce[2] = delay / 256;
1154 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1159 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1160 gce[0] |= (disposal_method & 3) << 2;
1164 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1166 i_push_error(0, "Could not save GCE");
1173 =item do_comments(gf, img)
1175 Write any comments in the image.
1179 static int do_comments(GifFileType *gf, i_img *img) {
1182 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1183 if (img->tags.tags[pos].data) {
1184 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1190 sprintf(buf, "%d", img->tags.tags[pos].idata);
1191 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1201 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1203 Internal. Add the Netscape2.0 loop extension block, if requested.
1205 The code for this function is currently "#if 0"ed out since the giflib
1206 extension writing code currently doesn't seem to support writing
1207 application extension blocks.
1211 static int do_ns_loop(GifFileType *gf, i_img *img)
1213 /* EGifPutExtension() doesn't appear to handle application
1214 extension blocks in any way
1215 Since giflib wraps the fd with a FILE * (and puts that in its
1216 private data), we can't do an end-run and write the data
1218 There's no open interface that takes a FILE * either, so we
1219 can't workaround it that way either.
1220 If giflib's callback interface wasn't broken by default, I'd
1221 force file writes to use callbacks, but it is broken by default.
1224 /* yes this was another attempt at supporting the loop extension */
1226 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1227 unsigned char nsle[12] = "NETSCAPE2.0";
1228 unsigned char subblock[3];
1229 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1231 i_push_error(0, "writing loop extension");
1235 subblock[1] = loop_count % 256;
1236 subblock[2] = loop_count / 256;
1237 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1239 i_push_error(0, "writing loop extention sub-block");
1242 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1244 i_push_error(0, "writing loop extension terminator");
1253 =item make_gif_map(i_quantize *quant, int want_trans)
1255 Create a giflib color map object from an Imager color map.
1260 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1262 GifColorType colors[256];
1264 int size = quant->mc_count;
1266 ColorMapObject *map;
1269 for (i = 0; i < quant->mc_count; ++i) {
1270 colors[i].Red = quant->mc_colors[i].rgb.r;
1271 colors[i].Green = quant->mc_colors[i].rgb.g;
1272 colors[i].Blue = quant->mc_colors[i].rgb.b;
1275 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1276 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1277 colors[size].Red = trans.rgb.r;
1278 colors[size].Green = trans.rgb.g;
1279 colors[size].Blue = trans.rgb.b;
1283 while (map_size < size)
1285 /* giflib spews for 1 colour maps, reasonable, I suppose */
1288 while (i < map_size) {
1289 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1293 map = MakeMapObject(map_size, colors);
1294 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1297 i_push_error(0, "Could not create color map object");
1304 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1306 We need to call EGifSetGifVersion() before opening the file - put that
1309 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1310 giflib 4.1.0 has code:
1312 static char *GifVersionPrefix = GIF87_STAMP;
1314 and the code that sets the version internally does:
1316 strncpy(&GifVersionPrefix[3], Version, 3);
1318 which is very broken.
1320 Failing to set the correct GIF version doesn't seem to cause a problem
1326 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1327 /* the following crashed giflib
1328 the EGifSetGifVersion() is seriously borked in giflib
1329 it's less borked in the ungiflib beta, but we don't have a mechanism
1331 Needs to be updated to support tags.
1332 if (opts->delay_count
1333 || opts->user_input_count
1334 || opts->disposal_count
1336 || quant->transp != tr_none)
1337 EGifSetGifVersion("89a");
1339 EGifSetGifVersion("87a");
1344 in_palette(i_color *c, i_quantize *quant, int size) {
1347 for (i = 0; i < size; ++i) {
1348 if (c->channel[0] == quant->mc_colors[i].channel[0]
1349 && c->channel[1] == quant->mc_colors[i].channel[1]
1350 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1359 =item has_common_palette(imgs, count, quant, want_trans)
1361 Tests if all the given images are paletted and have a common palette,
1362 if they do it builds that palette.
1364 A possible improvement might be to eliminate unused colors in the
1370 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1372 int size = quant->mc_count;
1378 /* we try to build a common palette here, if we can manage that, then
1379 that's the palette we use */
1380 for (imgn = 0; imgn < count; ++imgn) {
1381 int eliminate_unused;
1382 if (imgs[imgn]->type != i_palette_type)
1385 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1386 &eliminate_unused)) {
1387 eliminate_unused = 1;
1390 if (eliminate_unused) {
1391 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1393 memset(used, 0, sizeof(used));
1395 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1396 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1397 for (x = 0; x < imgs[imgn]->xsize; ++x)
1404 /* assume all are in use */
1405 memset(used, 1, sizeof(used));
1408 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1411 i_getcolors(imgs[imgn], i, &c, 1);
1413 if (in_palette(&c, quant, size) < 0) {
1414 if (size < quant->mc_size) {
1415 quant->mc_colors[size++] = c;
1418 /* oops, too many colors */
1426 quant->mc_count = size;
1432 quant_paletted(i_quantize *quant, i_img *img) {
1433 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1435 i_palidx trans[256];
1439 /* build a translation table */
1440 for (i = 0; i < i_colorcount(img); ++i) {
1442 i_getcolors(img, i, &c, 1);
1443 trans[i] = in_palette(&c, quant, quant->mc_count);
1446 for (y = 0; y < img->ysize; ++y) {
1447 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1448 for (x = 0; x < img->xsize; ++x) {
1458 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1460 Internal. Low-level function that does the high-level GIF processing
1463 Returns non-zero on success.
1469 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1470 unsigned char *result;
1472 ColorMapObject *map;
1473 int scrw = 0, scrh = 0;
1474 int imgn, orig_count, orig_size;
1480 i_img **glob_imgs; /* images that will use the global color map */
1482 i_color *orig_colors = quant->mc_colors;
1483 i_color *glob_colors = NULL;
1484 int glob_color_count;
1486 int glob_want_trans;
1487 int glob_paletted; /* the global map was made from the image palettes */
1488 int colors_paletted;
1493 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1494 quant, gf, imgs, count));
1496 /* *((char *)0) = 1; */ /* used to break into the debugger */
1499 i_push_error(0, "No images provided to write");
1500 return 0; /* what are you smoking? */
1503 i_mempool_init(&mp);
1505 /* sanity is nice */
1506 if (quant->mc_size > 256)
1507 quant->mc_size = 256;
1508 if (quant->mc_count > quant->mc_size)
1509 quant->mc_count = quant->mc_size;
1511 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1513 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1517 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1518 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1520 glob_want_trans = 0;
1521 for (imgn = 0; imgn < count; ++imgn) {
1523 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1524 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1525 if (imgs[imgn]->xsize + posx > scrw)
1526 scrw = imgs[imgn]->xsize + posx;
1527 if (imgs[imgn]->ysize + posy > scrh)
1528 scrh = imgs[imgn]->ysize + posy;
1529 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1530 localmaps[imgn] = 0;
1531 if (localmaps[imgn])
1534 if (imgs[imgn]->channels == 4) {
1535 glob_want_trans = 1;
1537 glob_imgs[glob_img_count++] = imgs[imgn];
1540 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1542 orig_count = quant->mc_count;
1543 orig_size = quant->mc_size;
1545 if (glob_img_count) {
1547 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1548 quant->mc_colors = glob_colors;
1549 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1550 /* we have some images that want to use the global map */
1551 if (glob_want_trans && quant->mc_count == 256) {
1552 mm_log((2, " disabling transparency for global map - no space\n"));
1553 glob_want_trans = 0;
1555 if (glob_want_trans && quant->mc_size == 256) {
1556 mm_log((2, " reserving color for transparency\n"));
1559 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1564 quant_makemap(quant, glob_imgs, glob_img_count);
1566 glob_color_count = quant->mc_count;
1567 quant->mc_colors = orig_colors;
1570 /* use the global map if we have one, otherwise use the local map */
1573 quant->mc_colors = glob_colors;
1574 quant->mc_count = glob_color_count;
1575 want_trans = glob_want_trans && imgs[0]->channels == 4;
1577 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1579 if (gif_background < 0)
1581 if (gif_background >= glob_color_count)
1585 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1586 if (has_common_palette(imgs, 1, quant, want_trans)) {
1587 colors_paletted = 1;
1590 colors_paletted = 0;
1591 quant_makemap(quant, imgs, 1);
1594 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1595 i_mempool_destroy(&mp);
1596 quant->mc_colors = orig_colors;
1598 mm_log((1, "Error in MakeMapObject"));
1603 /* since we don't know how big some the local palettes could be
1604 we need to base the bits on the maximum number of colors */
1605 while (orig_size > (1 << color_bits))
1609 int count = quant->mc_count;
1612 while (count > (1 << color_bits))
1616 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1617 gif_background, map) == GIF_ERROR) {
1618 i_mempool_destroy(&mp);
1619 quant->mc_colors = orig_colors;
1621 i_push_error(0, "Could not save screen descriptor");
1625 mm_log((1, "Error in EGifPutScreenDesc."));
1630 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1632 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1635 if (!localmaps[0]) {
1637 colors_paletted = glob_paletted;
1640 /* if this image has a global map the colors in quant don't
1641 belong to this image, so build a palette */
1643 /* generate the local map for this image */
1644 quant->mc_colors = orig_colors;
1645 quant->mc_size = orig_size;
1646 quant->mc_count = orig_count;
1647 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1649 /* if the caller gives us too many colours we can't do transparency */
1650 if (want_trans && quant->mc_count == 256)
1652 /* if they want transparency but give us a big size, make it smaller
1653 to give room for a transparency colour */
1654 if (want_trans && quant->mc_size == 256)
1656 if (has_common_palette(imgs, 1, quant, want_trans)) {
1657 colors_paletted = 1;
1660 colors_paletted = 0;
1661 quant_makemap(quant, imgs, 1);
1663 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1664 i_mempool_destroy(&mp);
1666 mm_log((1, "Error in MakeMapObject"));
1671 /* the map we wrote was the map for this image - don't set the local
1677 if (colors_paletted)
1678 result = quant_paletted(quant, imgs[0]);
1680 result = quant_translate(quant, imgs[0]);
1682 quant_transparent(quant, result, imgs[0], quant->mc_count);
1683 trans_index = quant->mc_count;
1686 if (!do_ns_loop(gf, imgs[0])) {
1687 i_mempool_destroy(&mp);
1688 quant->mc_colors = orig_colors;
1692 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1693 i_mempool_destroy(&mp);
1694 quant->mc_colors = orig_colors;
1700 if (!do_comments(gf, imgs[0])) {
1701 i_mempool_destroy(&mp);
1702 quant->mc_colors = orig_colors;
1708 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1710 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1711 interlace, map) == GIF_ERROR) {
1712 i_mempool_destroy(&mp);
1713 quant->mc_colors = orig_colors;
1715 i_push_error(0, "Could not save image descriptor");
1717 mm_log((1, "Error in EGifPutImageDesc."));
1723 if (!do_write(gf, interlace, imgs[0], result)) {
1724 i_mempool_destroy(&mp);
1725 quant->mc_colors = orig_colors;
1732 /* that first awful image is out of the way, do the rest */
1733 for (imgn = 1; imgn < count; ++imgn) {
1734 if (localmaps[imgn]) {
1735 quant->mc_colors = orig_colors;
1736 quant->mc_count = orig_count;
1737 quant->mc_size = orig_size;
1739 want_trans = quant->transp != tr_none
1740 && imgs[imgn]->channels == 4;
1741 /* if the caller gives us too many colours we can't do transparency */
1742 if (want_trans && quant->mc_count == 256)
1744 /* if they want transparency but give us a big size, make it smaller
1745 to give room for a transparency colour */
1746 if (want_trans && quant->mc_size == 256)
1749 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1750 result = quant_paletted(quant, imgs[imgn]);
1753 quant_makemap(quant, imgs+imgn, 1);
1754 result = quant_translate(quant, imgs[imgn]);
1757 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1758 trans_index = quant->mc_count;
1761 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1762 i_mempool_destroy(&mp);
1763 quant->mc_colors = orig_colors;
1766 mm_log((1, "Error in MakeMapObject."));
1771 quant->mc_colors = glob_colors;
1772 quant->mc_count = glob_color_count;
1774 result = quant_paletted(quant, imgs[imgn]);
1776 result = quant_translate(quant, imgs[imgn]);
1777 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1779 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1780 trans_index = quant->mc_count;
1785 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1786 i_mempool_destroy(&mp);
1787 quant->mc_colors = orig_colors;
1793 if (!do_comments(gf, imgs[imgn])) {
1794 i_mempool_destroy(&mp);
1795 quant->mc_colors = orig_colors;
1801 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1803 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1806 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1808 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1809 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1810 i_mempool_destroy(&mp);
1811 quant->mc_colors = orig_colors;
1813 i_push_error(0, "Could not save image descriptor");
1818 mm_log((1, "Error in EGifPutImageDesc."));
1824 if (!do_write(gf, interlace, imgs[imgn], result)) {
1825 i_mempool_destroy(&mp);
1826 quant->mc_colors = orig_colors;
1834 if (EGifCloseFile(gf) == GIF_ERROR) {
1835 i_mempool_destroy(&mp);
1837 i_push_error(0, "Could not close GIF file");
1838 mm_log((1, "Error in EGifCloseFile\n"));
1841 i_mempool_destroy(&mp);
1842 quant->mc_colors = orig_colors;
1848 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1850 General high-level function to write a GIF to a file.
1852 Writes the GIF images to the specified file handle using the options
1853 in quant and opts. See L<image.h/i_quantize> and
1854 L<image.h/i_gif_opts>.
1856 Returns non-zero on success.
1862 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
1866 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
1867 quant, fd, imgs, count));
1869 gif_set_version(quant, imgs, count);
1871 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1873 i_push_error(0, "Cannot create GIF file object");
1874 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1878 return i_writegif_low(quant, gf, imgs, count);
1881 #if IM_GIFMAJOR >= 4
1884 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1886 Internal. Wrapper for the user write callback function.
1891 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1893 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1895 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
1901 =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)
1903 General high-level function to write a GIF using callbacks to send
1906 Returns non-zero on success.
1912 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1913 int maxlength, i_img **imgs, int count)
1915 #if IM_GIFMAJOR >= 4
1917 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1922 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
1923 quant, cb, userdata, maxlength, imgs, count));
1925 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1927 i_push_error(0, "Cannot create GIF file object");
1928 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1929 i_free_gen_write_data(gwd, 0);
1933 result = i_writegif_low(quant, gf, imgs, count);
1934 return i_free_gen_write_data(gwd, result);
1937 i_push_error(0, "callbacks not supported with giflib3");
1943 #if IM_GIFMAJOR >= 4
1946 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1947 io_glue *ig = (io_glue *)gft->UserData;
1949 return ig->writecb(ig, data, length);
1955 =item i_writegif_wiol(ig, quant, opts, imgs, count)
1960 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
1962 io_glue_commit_types(ig);
1964 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1965 int fd = dup(ig->source.fdseek.fd);
1967 i_push_error(errno, "dup() failed");
1970 /* giflib opens the fd with fdopen(), which is then closed when fclose()
1971 is called - dup it so the caller's fd isn't closed */
1972 return i_writegif_gen(quant, fd, imgs, count);
1975 #if IM_GIFMAJOR >= 4
1976 GifFileType *GifFile;
1981 gif_set_version(quant, imgs, count);
1983 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
1985 i_push_error(0, "Cannot create giflib callback object");
1986 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
1990 result = i_writegif_low(quant, GifFile, imgs, count);
1997 i_push_error(0, "callbacks not supported with giflib3");
2005 =item gif_error_msg(int code)
2007 Grabs the most recent giflib error code from GifLastError() and
2008 returns a string that describes that error.
2010 The returned pointer points to a static buffer, either from a literal
2011 C string or a static buffer.
2016 static char const *gif_error_msg(int code) {
2017 static char msg[80];
2020 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2021 return "Failed to open given file";
2023 case E_GIF_ERR_WRITE_FAILED:
2024 return "Write failed";
2026 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2027 return "Screen descriptor already passed to giflib";
2029 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2030 return "Image descriptor already passed to giflib";
2032 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2033 return "Neither global nor local color map set";
2035 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2036 return "Too much pixel data passed to giflib";
2038 case E_GIF_ERR_NOT_ENOUGH_MEM:
2039 return "Out of memory";
2041 case E_GIF_ERR_DISK_IS_FULL:
2042 return "Disk is full";
2044 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2045 return "File close failed";
2047 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2048 return "File not writable";
2050 case D_GIF_ERR_OPEN_FAILED:
2051 return "Failed to open file";
2053 case D_GIF_ERR_READ_FAILED:
2054 return "Failed to read from file";
2056 case D_GIF_ERR_NOT_GIF_FILE:
2057 return "File is not a GIF file";
2059 case D_GIF_ERR_NO_SCRN_DSCR:
2060 return "No screen descriptor detected - invalid file";
2062 case D_GIF_ERR_NO_IMAG_DSCR:
2063 return "No image descriptor detected - invalid file";
2065 case D_GIF_ERR_NO_COLOR_MAP:
2066 return "No global or local color map found";
2068 case D_GIF_ERR_WRONG_RECORD:
2069 return "Wrong record type detected - invalid file?";
2071 case D_GIF_ERR_DATA_TOO_BIG:
2072 return "Data in file too big for image";
2074 case D_GIF_ERR_NOT_ENOUGH_MEM:
2075 return "Out of memory";
2077 case D_GIF_ERR_CLOSE_FAILED:
2078 return "Close failed";
2080 case D_GIF_ERR_NOT_READABLE:
2081 return "File not opened for read";
2083 case D_GIF_ERR_IMAGE_DEFECT:
2084 return "Defective image";
2086 case D_GIF_ERR_EOF_TOO_SOON:
2087 return "Unexpected EOF - invalid file";
2090 sprintf(msg, "Unknown giflib error code %d", code);
2096 =item gif_push_error()
2098 Utility function that takes the current GIF error code, converts it to
2099 an error message and pushes it on the error stack.
2104 static void gif_push_error(void) {
2105 int code = GifLastError(); /* clears saved error */
2107 i_push_error(code, gif_error_msg(code));
2113 The Netscape loop extension isn't implemented. Giflib's extension
2114 writing code doesn't seem to support writing named extensions in this
2117 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2118 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2121 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2122 supplied interface in giflib 4.1.0 causes a SEGV in
2123 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2127 Arnar M. Hrafnkelsson, addi@umich.edu