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 if (colour_table && *colour_table) {
184 myfree(*colour_table);
185 *colour_table = NULL;
187 DGifCloseFile(GifFile);
191 Size = GifFile->SWidth * sizeof(GifPixelType);
193 GifRow = mymalloc(Size);
195 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
197 /* Scan the content of the GIF file and load the image(s) in: */
199 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
201 i_push_error(0, "Unable to get record type");
202 if (colour_table && *colour_table) {
203 myfree(*colour_table);
204 *colour_table = NULL;
208 DGifCloseFile(GifFile);
212 switch (RecordType) {
213 case IMAGE_DESC_RECORD_TYPE:
214 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
216 i_push_error(0, "Unable to get image descriptor");
217 if (colour_table && *colour_table) {
218 myfree(*colour_table);
219 *colour_table = NULL;
223 DGifCloseFile(GifFile);
227 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
228 mm_log((1, "Adding local colormap\n"));
229 ColorMapSize = ColorMap->ColorCount;
231 i_colortable_copy(colour_table, colours, ColorMap);
235 /* No colormap and we are about to read in the image - abandon for now */
236 mm_log((1, "Going in with no colormap\n"));
237 i_push_error(0, "Image does not have a local or a global color map");
238 /* we can't have allocated a colour table here */
241 DGifCloseFile(GifFile);
245 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
246 Col = GifFile->Image.Left;
247 Width = GifFile->Image.Width;
248 Height = GifFile->Image.Height;
250 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
252 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
253 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
254 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
255 if (colour_table && *colour_table) {
256 myfree(*colour_table);
257 *colour_table = NULL;
261 DGifCloseFile(GifFile);
264 if (GifFile->Image.Interlace) {
266 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
268 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
270 i_push_error(0, "Reading GIF line");
271 if (colour_table && *colour_table) {
272 myfree(*colour_table);
273 *colour_table = NULL;
277 DGifCloseFile(GifFile);
281 for (x = 0; x < Width; x++) {
282 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
283 col.rgb.r = ColorMapEntry->Red;
284 col.rgb.g = ColorMapEntry->Green;
285 col.rgb.b = ColorMapEntry->Blue;
286 i_ppix(im,Col+x,j,&col);
292 for (i = 0; i < Height; i++) {
293 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
295 i_push_error(0, "Reading GIF line");
296 if (colour_table && *colour_table) {
297 myfree(*colour_table);
298 *colour_table = NULL;
302 DGifCloseFile(GifFile);
306 for (x = 0; x < Width; x++) {
307 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
308 col.rgb.r = ColorMapEntry->Red;
309 col.rgb.g = ColorMapEntry->Green;
310 col.rgb.b = ColorMapEntry->Blue;
311 i_ppix(im, Col+x, Row, &col);
317 case EXTENSION_RECORD_TYPE:
318 /* Skip any extension blocks in file: */
319 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
321 i_push_error(0, "Reading extension record");
322 if (colour_table && *colour_table) {
323 myfree(*colour_table);
324 *colour_table = NULL;
328 DGifCloseFile(GifFile);
331 while (Extension != NULL) {
332 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
334 i_push_error(0, "reading next block of extension");
335 if (colour_table && *colour_table) {
336 myfree(*colour_table);
337 *colour_table = NULL;
341 DGifCloseFile(GifFile);
346 case TERMINATE_RECORD_TYPE:
348 default: /* Should be traps by DGifGetRecordType. */
351 } while (RecordType != TERMINATE_RECORD_TYPE);
355 if (DGifCloseFile(GifFile) == GIF_ERROR) {
357 i_push_error(0, "Closing GIF file object");
358 if (colour_table && *colour_table) {
359 myfree(*colour_table);
360 *colour_table = NULL;
366 i_tags_add(&im->tags, "i_format", 0, "gif", -1, 0);
372 =item i_readgif(int fd, int **colour_table, int *colours)
374 Reads in a GIF file from a file handle and converts it to an Imager
377 Returns the palette for the object in colour_table for colours
380 Returns NULL on failure.
386 i_readgif(int fd, int **colour_table, int *colours) {
387 GifFileType *GifFile;
391 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
393 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
395 i_push_error(0, "Cannot create giflib file object");
396 mm_log((1,"i_readgif: Unable to open file\n"));
400 return i_readgif_low(GifFile, colour_table, colours);
405 Internal function called by i_readgif_multi_low() in error handling
408 static void free_images(i_img **imgs, int count) {
410 for (i = 0; i < count; ++i)
411 i_img_destroy(imgs[i]);
416 =item i_readgif_multi_low(GifFileType *gf, int *count)
418 Reads one of more gif images from the given GIF file.
420 Returns a pointer to an array of i_img *, and puts the count into
423 Unlike the normal i_readgif*() functions the images are paletted
424 images rather than a combined RGB image.
426 This functions sets tags on the images returned:
432 the offset of the image from the left of the "screen" ("Image Left
437 the offset of the image from the top of the "screen" ("Image Top Position")
441 non-zero if the image was interlaced ("Interlace Flag")
443 =item gif_screen_width
445 =item gif_screen_height
447 the size of the logical screen ("Logical Screen Width",
448 "Logical Screen Height")
452 Non-zero if this image had a local color map.
456 The index in the global colormap of the logical screen's background
457 color. This is only set if the current image uses the global
460 =item gif_trans_index
462 The index of the color in the colormap used for transparency. If the
463 image has a transparency then it is returned as a 4 channel image with
464 the alpha set to zero in this palette entry. ("Transparent Color Index")
468 The delay until the next frame is displayed, in 1/100 of a second.
473 whether or not a user input is expected before continuing (view dependent)
478 how the next frame is displayed ("Disposal Method")
482 the number of loops from the Netscape Loop extension. This may be zero.
486 the first block of the first gif comment before each image.
490 Where applicable, the ("name") is the name of that field from the GIF89
496 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
498 int i, j, Size, Width, Height, ExtCode, Count, x;
499 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
500 ColorMapObject *ColorMap;
502 GifRecordType RecordType;
503 GifByteType *Extension;
507 int trans_index; /* transparent index if we see a GCE */
508 int gif_delay; /* delay from a GCE */
509 int user_input; /* user input flag from a GCE */
510 int disposal; /* disposal method from a GCE */
513 char *comment = NULL; /* a comment */
514 i_img **results = NULL;
515 int result_alloc = 0;
520 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
522 BackGround = GifFile->SBackGroundColor;
524 Size = GifFile->SWidth * sizeof(GifPixelType);
526 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
527 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
529 /* Scan the content of the GIF file and load the image(s) in: */
531 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
533 i_push_error(0, "Unable to get record type");
534 free_images(results, *count);
535 DGifCloseFile(GifFile);
539 switch (RecordType) {
540 case IMAGE_DESC_RECORD_TYPE:
541 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
543 i_push_error(0, "Unable to get image descriptor");
544 free_images(results, *count);
545 DGifCloseFile(GifFile);
549 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
550 mm_log((1, "Adding local colormap\n"));
551 ColorMapSize = ColorMap->ColorCount;
553 /* No colormap and we are about to read in the image -
555 mm_log((1, "Going in with no colormap\n"));
556 i_push_error(0, "Image does not have a local or a global color map");
557 free_images(results, *count);
558 DGifCloseFile(GifFile);
562 Width = GifFile->Image.Width;
563 Height = GifFile->Image.Height;
565 if (got_gce && trans_index >= 0)
567 img = i_img_pal_new(Width, Height, channels, 256);
569 free_images(results, *count);
572 /* populate the palette of the new image */
573 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
574 for (i = 0; i < ColorMapSize; ++i) {
576 col.rgba.r = ColorMap->Colors[i].Red;
577 col.rgba.g = ColorMap->Colors[i].Green;
578 col.rgba.b = ColorMap->Colors[i].Blue;
579 if (channels == 4 && trans_index == i)
584 i_addcolors(img, &col, 1);
587 if (*count > result_alloc) {
588 if (result_alloc == 0) {
590 results = mymalloc(result_alloc * sizeof(i_img *));
593 /* myrealloc never fails (it just dies if it can't allocate) */
595 results = myrealloc(results, result_alloc * sizeof(i_img *));
598 results[*count-1] = img;
599 i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
600 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
602 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
603 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
604 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
605 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
606 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
607 i_tags_addn(&img->tags, "gif_background", 0,
608 GifFile->SBackGroundColor);
610 if (GifFile->Image.ColorMap) {
611 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
614 if (trans_index >= 0) {
616 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
617 i_getcolors(img, trans_index, &trans, 1);
618 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
620 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
621 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
622 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
626 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
628 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
634 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
635 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
637 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
638 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
639 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
640 free_images(results, *count);
641 DGifCloseFile(GifFile);
645 if (GifFile->Image.Interlace) {
646 for (Count = i = 0; i < 4; i++) {
647 for (j = InterlacedOffset[i]; j < Height;
648 j += InterlacedJumps[i]) {
650 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
652 i_push_error(0, "Reading GIF line");
653 free_images(results, *count);
654 DGifCloseFile(GifFile);
658 i_ppal(img, 0, Width, j, GifRow);
663 for (i = 0; i < Height; i++) {
664 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
666 i_push_error(0, "Reading GIF line");
667 free_images(results, *count);
668 DGifCloseFile(GifFile);
672 i_ppal(img, 0, Width, i, GifRow);
676 case EXTENSION_RECORD_TYPE:
677 /* Skip any extension blocks in file: */
678 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
680 i_push_error(0, "Reading extension record");
681 free_images(results, *count);
682 DGifCloseFile(GifFile);
685 if (ExtCode == 0xF9) {
687 if (Extension[1] & 1)
688 trans_index = Extension[4];
691 gif_delay = Extension[2] + 256 * Extension[3];
692 user_input = (Extension[0] & 2) != 0;
693 disposal = (Extension[0] >> 2) & 3;
695 if (ExtCode == 0xFF && *Extension == 11) {
696 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
697 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
699 i_push_error(0, "reading loop extension");
700 free_images(results, *count);
701 DGifCloseFile(GifFile);
704 if (Extension && *Extension == 3) {
706 ns_loop = Extension[2] + 256 * Extension[3];
710 else if (ExtCode == 0xFE) {
711 /* while it's possible for a GIF file to contain more than one
712 comment, I'm only implementing a single comment per image,
713 with the comment saved into the following image.
714 If someone wants more than that they can implement it.
715 I also don't handle comments that take more than one block.
718 comment = mymalloc(*Extension+1);
719 memcpy(comment, Extension+1, *Extension);
720 comment[*Extension] = '\0';
723 while (Extension != NULL) {
724 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
726 i_push_error(0, "reading next block of extension");
727 free_images(results, *count);
728 DGifCloseFile(GifFile);
733 case TERMINATE_RECORD_TYPE:
735 default: /* Should be trapped by DGifGetRecordType. */
738 } while (RecordType != TERMINATE_RECORD_TYPE);
742 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
750 if (DGifCloseFile(GifFile) == GIF_ERROR) {
752 i_push_error(0, "Closing GIF file object");
753 free_images(results, *count);
761 /* giflib declares this incorrectly as EgifOpen */
762 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
764 static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
768 =item i_readgif_multi_wiol(ig, int *count)
774 i_readgif_multi_wiol(io_glue *ig, int *count) {
775 io_glue_commit_types(ig);
777 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
778 return i_readgif_multi(ig->source.fdseek.fd, count);
782 GifFileType *GifFile;
786 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
788 i_push_error(0, "Cannot create giflib callback object");
789 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
793 return i_readgif_multi_low(GifFile, count);
796 i_push_error(0, "callbacks not supported with giflib3");
804 =item i_readgif_multi(int fd, int *count)
809 i_readgif_multi(int fd, int *count) {
810 GifFileType *GifFile;
814 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
816 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
818 i_push_error(0, "Cannot create giflib file object");
819 mm_log((1,"i_readgif: Unable to open file\n"));
823 return i_readgif_multi_low(GifFile, count);
827 =item i_readgif_multi_scalar(char *data, int length, int *count)
832 i_readgif_multi_scalar(char *data, int length, int *count) {
834 GifFileType *GifFile;
835 struct gif_scalar_info gsi;
843 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
844 data, length, count));
846 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
848 i_push_error(0, "Cannot create giflib callback object");
849 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
853 return i_readgif_multi_low(GifFile, count);
860 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
862 Read a GIF file into an Imager RGB file, the data of the GIF file is
863 retreived by callin the user supplied callback function.
865 This function is only used with giflib 4 and higher.
871 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
873 GifFileType *GifFile;
876 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
880 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
881 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
883 i_push_error(0, "Cannot create giflib callback object");
884 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
889 result = i_readgif_multi_low(GifFile, count);
890 i_free_gen_read_data(gci);
899 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
901 Write I<img> to the file handle I<fd>. The resulting GIF will use a
902 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
903 colours taken from I<fixed>.
905 Returns non-zero on success.
911 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
915 memset(&quant, 0, sizeof(quant));
916 quant.make_colors = mc_addi;
917 quant.mc_colors = colors;
918 quant.mc_size = 1<<max_colors;
919 quant.mc_count = fixedlen;
920 memcpy(colors, fixed, fixedlen * sizeof(i_color));
921 quant.translate = pt_perturb;
922 quant.perturb = pixdev;
923 return i_writegif_gen(&quant, fd, &im, 1);
927 =item i_writegifmc(i_img *im, int fd, int max_colors)
929 Write I<img> to the file handle I<fd>. The resulting GIF will use a
930 maximum of 1<<I<max_colours> colours.
932 Returns non-zero on success.
938 i_writegifmc(i_img *im, int fd, int max_colors) {
942 /* *(char *)0 = 1; */
944 memset(&quant, 0, sizeof(quant));
945 quant.make_colors = mc_none; /* ignored for pt_giflib */
946 quant.mc_colors = colors;
947 quant.mc_size = 1 << max_colors;
949 quant.translate = pt_giflib;
950 return i_writegif_gen(&quant, fd, &im, 1);
955 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
957 Reads a GIF file from an in memory copy of the file. This can be used
958 if you get the 'file' from some source other than an actual file (or
959 some other file handle).
961 This function is only available with giflib 4 and higher.
966 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
968 GifFileType *GifFile;
969 struct gif_scalar_info gsi;
977 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
978 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
980 i_push_error(0, "Cannot create giflib callback object");
981 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
985 return i_readgif_low(GifFile, colour_table, colours);
994 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
996 Internal. The reader callback wrapper passed to giflib.
998 This function is only used with giflib 4 and higher.
1004 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
1005 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
1012 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
1014 Read a GIF file into an Imager RGB file, the data of the GIF file is
1015 retreived by callin the user supplied callback function.
1017 This function is only used with giflib 4 and higher.
1023 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
1024 #if IM_GIFMAJOR >= 4
1025 GifFileType *GifFile;
1028 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
1032 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
1033 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
1035 i_push_error(0, "Cannot create giflib callback object");
1036 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
1041 result = i_readgif_low(GifFile, colour_table, colours);
1042 i_free_gen_read_data(gci);
1047 i_push_error(0, "callbacks not supported with giflib3");
1053 #if IM_GIFMAJOR >= 4
1056 io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
1057 io_glue *ig = (io_glue *)gft->UserData;
1059 return ig->readcb(ig, buf, length);
1065 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
1066 io_glue_commit_types(ig);
1068 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1069 int fd = dup(ig->source.fdseek.fd);
1071 i_push_error(errno, "dup() failed");
1074 return i_readgif(fd, color_table, colors);
1077 #if IM_GIFMAJOR >= 4
1078 GifFileType *GifFile;
1082 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
1084 i_push_error(0, "Cannot create giflib callback object");
1085 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
1089 return i_readgif_low(GifFile, color_table, colors);
1093 i_push_error(0, "callbacks not supported with giflib3");
1101 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
1103 Internal. Low level image write function. Writes in interlace if
1104 that was requested in the GIF options.
1106 Returns non-zero on success.
1111 do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
1114 for (i = 0; i < 4; ++i) {
1115 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
1116 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
1118 i_push_error(0, "Could not save image data:");
1119 mm_log((1, "Error in EGifPutLine\n"));
1128 for (y = 0; y < img->ysize; ++y) {
1129 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1131 i_push_error(0, "Could not save image data:");
1132 mm_log((1, "Error in EGifPutLine\n"));
1144 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1146 Internal. Writes the GIF graphics control extension, if necessary.
1148 Returns non-zero on success.
1152 static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
1154 unsigned char gce[4] = {0};
1158 int disposal_method;
1162 gce[3] = trans_index;
1165 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1166 gce[1] = delay % 256;
1167 gce[2] = delay / 256;
1170 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1175 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1176 gce[0] |= (disposal_method & 3) << 2;
1180 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1182 i_push_error(0, "Could not save GCE");
1189 =item do_comments(gf, img)
1191 Write any comments in the image.
1195 static int do_comments(GifFileType *gf, i_img *img) {
1198 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1199 if (img->tags.tags[pos].data) {
1200 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1206 sprintf(buf, "%d", img->tags.tags[pos].idata);
1207 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1217 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1219 Internal. Add the Netscape2.0 loop extension block, if requested.
1221 The code for this function is currently "#if 0"ed out since the giflib
1222 extension writing code currently doesn't seem to support writing
1223 application extension blocks.
1227 static int do_ns_loop(GifFileType *gf, i_img *img)
1229 /* EGifPutExtension() doesn't appear to handle application
1230 extension blocks in any way
1231 Since giflib wraps the fd with a FILE * (and puts that in its
1232 private data), we can't do an end-run and write the data
1234 There's no open interface that takes a FILE * either, so we
1235 can't workaround it that way either.
1236 If giflib's callback interface wasn't broken by default, I'd
1237 force file writes to use callbacks, but it is broken by default.
1240 /* yes this was another attempt at supporting the loop extension */
1242 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
1243 unsigned char nsle[12] = "NETSCAPE2.0";
1244 unsigned char subblock[3];
1245 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1247 i_push_error(0, "writing loop extension");
1251 subblock[1] = loop_count % 256;
1252 subblock[2] = loop_count / 256;
1253 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1255 i_push_error(0, "writing loop extention sub-block");
1258 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1260 i_push_error(0, "writing loop extension terminator");
1269 =item make_gif_map(i_quantize *quant, int want_trans)
1271 Create a giflib color map object from an Imager color map.
1276 static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1278 GifColorType colors[256];
1280 int size = quant->mc_count;
1282 ColorMapObject *map;
1285 for (i = 0; i < quant->mc_count; ++i) {
1286 colors[i].Red = quant->mc_colors[i].rgb.r;
1287 colors[i].Green = quant->mc_colors[i].rgb.g;
1288 colors[i].Blue = quant->mc_colors[i].rgb.b;
1291 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1292 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1293 colors[size].Red = trans.rgb.r;
1294 colors[size].Green = trans.rgb.g;
1295 colors[size].Blue = trans.rgb.b;
1299 while (map_size < size)
1301 /* giflib spews for 1 colour maps, reasonable, I suppose */
1304 while (i < map_size) {
1305 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1309 map = MakeMapObject(map_size, colors);
1310 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1313 i_push_error(0, "Could not create color map object");
1320 =item gif_set_version(i_quantize *quant, i_img *imgs, int count)
1322 We need to call EGifSetGifVersion() before opening the file - put that
1325 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1326 giflib 4.1.0 has code:
1328 static char *GifVersionPrefix = GIF87_STAMP;
1330 and the code that sets the version internally does:
1332 strncpy(&GifVersionPrefix[3], Version, 3);
1334 which is very broken.
1336 Failing to set the correct GIF version doesn't seem to cause a problem
1342 static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1343 /* the following crashed giflib
1344 the EGifSetGifVersion() is seriously borked in giflib
1345 it's less borked in the ungiflib beta, but we don't have a mechanism
1347 Needs to be updated to support tags.
1348 if (opts->delay_count
1349 || opts->user_input_count
1350 || opts->disposal_count
1352 || quant->transp != tr_none)
1353 EGifSetGifVersion("89a");
1355 EGifSetGifVersion("87a");
1360 in_palette(i_color *c, i_quantize *quant, int size) {
1363 for (i = 0; i < size; ++i) {
1364 if (c->channel[0] == quant->mc_colors[i].channel[0]
1365 && c->channel[1] == quant->mc_colors[i].channel[1]
1366 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1375 =item has_common_palette(imgs, count, quant, want_trans)
1377 Tests if all the given images are paletted and have a common palette,
1378 if they do it builds that palette.
1380 A possible improvement might be to eliminate unused colors in the
1386 has_common_palette(i_img **imgs, int count, i_quantize *quant,
1388 int size = quant->mc_count;
1394 /* we try to build a common palette here, if we can manage that, then
1395 that's the palette we use */
1396 for (imgn = 0; imgn < count; ++imgn) {
1397 int eliminate_unused;
1398 if (imgs[imgn]->type != i_palette_type)
1401 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1402 &eliminate_unused)) {
1403 eliminate_unused = 1;
1406 if (eliminate_unused) {
1407 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1409 memset(used, 0, sizeof(used));
1411 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1412 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1413 for (x = 0; x < imgs[imgn]->xsize; ++x)
1420 /* assume all are in use */
1421 memset(used, 1, sizeof(used));
1424 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1427 i_getcolors(imgs[imgn], i, &c, 1);
1429 if (in_palette(&c, quant, size) < 0) {
1430 if (size < quant->mc_size) {
1431 quant->mc_colors[size++] = c;
1434 /* oops, too many colors */
1442 quant->mc_count = size;
1448 quant_paletted(i_quantize *quant, i_img *img) {
1449 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1451 i_palidx trans[256];
1455 /* build a translation table */
1456 for (i = 0; i < i_colorcount(img); ++i) {
1458 i_getcolors(img, i, &c, 1);
1459 trans[i] = in_palette(&c, quant, quant->mc_count);
1462 for (y = 0; y < img->ysize; ++y) {
1463 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1464 for (x = 0; x < img->xsize; ++x) {
1474 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1476 Internal. Low-level function that does the high-level GIF processing
1479 Returns non-zero on success.
1485 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1486 unsigned char *result;
1488 ColorMapObject *map;
1489 int scrw = 0, scrh = 0;
1490 int imgn, orig_count, orig_size;
1496 i_img **glob_imgs; /* images that will use the global color map */
1498 i_color *orig_colors = quant->mc_colors;
1499 i_color *glob_colors = NULL;
1500 int glob_color_count;
1502 int glob_want_trans;
1503 int glob_paletted; /* the global map was made from the image palettes */
1504 int colors_paletted;
1509 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1510 quant, gf, imgs, count));
1512 /* *((char *)0) = 1; */ /* used to break into the debugger */
1515 i_push_error(0, "No images provided to write");
1516 return 0; /* what are you smoking? */
1519 i_mempool_init(&mp);
1521 /* sanity is nice */
1522 if (quant->mc_size > 256)
1523 quant->mc_size = 256;
1524 if (quant->mc_count > quant->mc_size)
1525 quant->mc_count = quant->mc_size;
1527 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1529 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1533 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1534 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1536 glob_want_trans = 0;
1537 for (imgn = 0; imgn < count; ++imgn) {
1539 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1540 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1541 if (imgs[imgn]->xsize + posx > scrw)
1542 scrw = imgs[imgn]->xsize + posx;
1543 if (imgs[imgn]->ysize + posy > scrh)
1544 scrh = imgs[imgn]->ysize + posy;
1545 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1546 localmaps[imgn] = 0;
1547 if (localmaps[imgn])
1550 if (imgs[imgn]->channels == 4) {
1551 glob_want_trans = 1;
1553 glob_imgs[glob_img_count++] = imgs[imgn];
1556 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1558 orig_count = quant->mc_count;
1559 orig_size = quant->mc_size;
1561 if (glob_img_count) {
1563 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1564 quant->mc_colors = glob_colors;
1565 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1566 /* we have some images that want to use the global map */
1567 if (glob_want_trans && quant->mc_count == 256) {
1568 mm_log((2, " disabling transparency for global map - no space\n"));
1569 glob_want_trans = 0;
1571 if (glob_want_trans && quant->mc_size == 256) {
1572 mm_log((2, " reserving color for transparency\n"));
1575 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1580 quant_makemap(quant, glob_imgs, glob_img_count);
1582 glob_color_count = quant->mc_count;
1583 quant->mc_colors = orig_colors;
1586 /* use the global map if we have one, otherwise use the local map */
1589 quant->mc_colors = glob_colors;
1590 quant->mc_count = glob_color_count;
1591 want_trans = glob_want_trans && imgs[0]->channels == 4;
1593 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1595 if (gif_background < 0)
1597 if (gif_background >= glob_color_count)
1601 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1602 if (has_common_palette(imgs, 1, quant, want_trans)) {
1603 colors_paletted = 1;
1606 colors_paletted = 0;
1607 quant_makemap(quant, imgs, 1);
1610 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1611 i_mempool_destroy(&mp);
1612 quant->mc_colors = orig_colors;
1614 mm_log((1, "Error in MakeMapObject"));
1619 /* since we don't know how big some the local palettes could be
1620 we need to base the bits on the maximum number of colors */
1621 while (orig_size > (1 << color_bits))
1625 int count = quant->mc_count;
1628 while (count > (1 << color_bits))
1632 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1633 gif_background, map) == GIF_ERROR) {
1634 i_mempool_destroy(&mp);
1635 quant->mc_colors = orig_colors;
1637 i_push_error(0, "Could not save screen descriptor");
1641 mm_log((1, "Error in EGifPutScreenDesc."));
1646 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1648 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1651 if (!localmaps[0]) {
1653 colors_paletted = glob_paletted;
1656 /* if this image has a global map the colors in quant don't
1657 belong to this image, so build a palette */
1659 /* generate the local map for this image */
1660 quant->mc_colors = orig_colors;
1661 quant->mc_size = orig_size;
1662 quant->mc_count = orig_count;
1663 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1665 /* if the caller gives us too many colours we can't do transparency */
1666 if (want_trans && quant->mc_count == 256)
1668 /* if they want transparency but give us a big size, make it smaller
1669 to give room for a transparency colour */
1670 if (want_trans && quant->mc_size == 256)
1672 if (has_common_palette(imgs, 1, quant, want_trans)) {
1673 colors_paletted = 1;
1676 colors_paletted = 0;
1677 quant_makemap(quant, imgs, 1);
1679 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1680 i_mempool_destroy(&mp);
1682 quant->mc_colors = orig_colors;
1683 mm_log((1, "Error in MakeMapObject"));
1688 /* the map we wrote was the map for this image - don't set the local
1694 if (colors_paletted)
1695 result = quant_paletted(quant, imgs[0]);
1697 result = quant_translate(quant, imgs[0]);
1699 i_mempool_destroy(&mp);
1700 quant->mc_colors = orig_colors;
1705 quant_transparent(quant, result, imgs[0], quant->mc_count);
1706 trans_index = quant->mc_count;
1709 if (!do_ns_loop(gf, imgs[0])) {
1710 i_mempool_destroy(&mp);
1711 quant->mc_colors = orig_colors;
1715 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1716 i_mempool_destroy(&mp);
1717 quant->mc_colors = orig_colors;
1723 if (!do_comments(gf, imgs[0])) {
1724 i_mempool_destroy(&mp);
1725 quant->mc_colors = orig_colors;
1731 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1733 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1734 interlace, map) == GIF_ERROR) {
1735 i_mempool_destroy(&mp);
1736 quant->mc_colors = orig_colors;
1738 i_push_error(0, "Could not save image descriptor");
1740 mm_log((1, "Error in EGifPutImageDesc."));
1746 if (!do_write(gf, interlace, imgs[0], result)) {
1747 i_mempool_destroy(&mp);
1748 quant->mc_colors = orig_colors;
1755 /* that first awful image is out of the way, do the rest */
1756 for (imgn = 1; imgn < count; ++imgn) {
1757 if (localmaps[imgn]) {
1758 quant->mc_colors = orig_colors;
1759 quant->mc_count = orig_count;
1760 quant->mc_size = orig_size;
1762 want_trans = quant->transp != tr_none
1763 && imgs[imgn]->channels == 4;
1764 /* if the caller gives us too many colours we can't do transparency */
1765 if (want_trans && quant->mc_count == 256)
1767 /* if they want transparency but give us a big size, make it smaller
1768 to give room for a transparency colour */
1769 if (want_trans && quant->mc_size == 256)
1772 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1773 result = quant_paletted(quant, imgs[imgn]);
1776 quant_makemap(quant, imgs+imgn, 1);
1777 result = quant_translate(quant, imgs[imgn]);
1780 i_mempool_destroy(&mp);
1781 quant->mc_colors = orig_colors;
1783 mm_log((1, "error in quant_translate()"));
1787 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1788 trans_index = quant->mc_count;
1791 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1792 i_mempool_destroy(&mp);
1793 quant->mc_colors = orig_colors;
1796 mm_log((1, "Error in MakeMapObject."));
1801 quant->mc_colors = glob_colors;
1802 quant->mc_count = glob_color_count;
1804 result = quant_paletted(quant, imgs[imgn]);
1806 result = quant_translate(quant, imgs[imgn]);
1807 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1809 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1810 trans_index = quant->mc_count;
1815 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1816 i_mempool_destroy(&mp);
1817 quant->mc_colors = orig_colors;
1823 if (!do_comments(gf, imgs[imgn])) {
1824 i_mempool_destroy(&mp);
1825 quant->mc_colors = orig_colors;
1831 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1833 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1836 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1838 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1839 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1840 i_mempool_destroy(&mp);
1841 quant->mc_colors = orig_colors;
1843 i_push_error(0, "Could not save image descriptor");
1848 mm_log((1, "Error in EGifPutImageDesc."));
1854 if (!do_write(gf, interlace, imgs[imgn], result)) {
1855 i_mempool_destroy(&mp);
1856 quant->mc_colors = orig_colors;
1864 if (EGifCloseFile(gf) == GIF_ERROR) {
1865 i_mempool_destroy(&mp);
1867 i_push_error(0, "Could not close GIF file");
1868 mm_log((1, "Error in EGifCloseFile\n"));
1873 for (i = 0; i < glob_color_count; ++i)
1874 orig_colors[i] = glob_colors[i];
1877 i_mempool_destroy(&mp);
1878 quant->mc_colors = orig_colors;
1884 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1886 General high-level function to write a GIF to a file.
1888 Writes the GIF images to the specified file handle using the options
1889 in quant and opts. See L<image.h/i_quantize> and
1890 L<image.h/i_gif_opts>.
1892 Returns non-zero on success.
1898 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
1902 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
1903 quant, fd, imgs, count));
1905 gif_set_version(quant, imgs, count);
1907 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1909 i_push_error(0, "Cannot create GIF file object");
1910 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1914 return i_writegif_low(quant, gf, imgs, count);
1917 #if IM_GIFMAJOR >= 4
1920 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1922 Internal. Wrapper for the user write callback function.
1927 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1929 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1931 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
1937 =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)
1939 General high-level function to write a GIF using callbacks to send
1942 Returns non-zero on success.
1948 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1949 int maxlength, i_img **imgs, int count)
1951 #if IM_GIFMAJOR >= 4
1953 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1958 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
1959 quant, cb, userdata, maxlength, imgs, count));
1961 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1963 i_push_error(0, "Cannot create GIF file object");
1964 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1965 i_free_gen_write_data(gwd, 0);
1969 result = i_writegif_low(quant, gf, imgs, count);
1970 return i_free_gen_write_data(gwd, result);
1973 i_push_error(0, "callbacks not supported with giflib3");
1979 #if IM_GIFMAJOR >= 4
1982 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1983 io_glue *ig = (io_glue *)gft->UserData;
1985 return ig->writecb(ig, data, length);
1991 =item i_writegif_wiol(ig, quant, opts, imgs, count)
1996 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
1998 io_glue_commit_types(ig);
2000 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
2001 int fd = dup(ig->source.fdseek.fd);
2003 i_push_error(errno, "dup() failed");
2006 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2007 is called - dup it so the caller's fd isn't closed */
2008 return i_writegif_gen(quant, fd, imgs, count);
2011 #if IM_GIFMAJOR >= 4
2012 GifFileType *GifFile;
2017 gif_set_version(quant, imgs, count);
2019 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2021 i_push_error(0, "Cannot create giflib callback object");
2022 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2026 result = i_writegif_low(quant, GifFile, imgs, count);
2033 i_push_error(0, "callbacks not supported with giflib3");
2041 =item gif_error_msg(int code)
2043 Grabs the most recent giflib error code from GifLastError() and
2044 returns a string that describes that error.
2046 The returned pointer points to a static buffer, either from a literal
2047 C string or a static buffer.
2052 static char const *gif_error_msg(int code) {
2053 static char msg[80];
2056 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2057 return "Failed to open given file";
2059 case E_GIF_ERR_WRITE_FAILED:
2060 return "Write failed";
2062 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2063 return "Screen descriptor already passed to giflib";
2065 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2066 return "Image descriptor already passed to giflib";
2068 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2069 return "Neither global nor local color map set";
2071 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2072 return "Too much pixel data passed to giflib";
2074 case E_GIF_ERR_NOT_ENOUGH_MEM:
2075 return "Out of memory";
2077 case E_GIF_ERR_DISK_IS_FULL:
2078 return "Disk is full";
2080 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2081 return "File close failed";
2083 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2084 return "File not writable";
2086 case D_GIF_ERR_OPEN_FAILED:
2087 return "Failed to open file";
2089 case D_GIF_ERR_READ_FAILED:
2090 return "Failed to read from file";
2092 case D_GIF_ERR_NOT_GIF_FILE:
2093 return "File is not a GIF file";
2095 case D_GIF_ERR_NO_SCRN_DSCR:
2096 return "No screen descriptor detected - invalid file";
2098 case D_GIF_ERR_NO_IMAG_DSCR:
2099 return "No image descriptor detected - invalid file";
2101 case D_GIF_ERR_NO_COLOR_MAP:
2102 return "No global or local color map found";
2104 case D_GIF_ERR_WRONG_RECORD:
2105 return "Wrong record type detected - invalid file?";
2107 case D_GIF_ERR_DATA_TOO_BIG:
2108 return "Data in file too big for image";
2110 case D_GIF_ERR_NOT_ENOUGH_MEM:
2111 return "Out of memory";
2113 case D_GIF_ERR_CLOSE_FAILED:
2114 return "Close failed";
2116 case D_GIF_ERR_NOT_READABLE:
2117 return "File not opened for read";
2119 case D_GIF_ERR_IMAGE_DEFECT:
2120 return "Defective image";
2122 case D_GIF_ERR_EOF_TOO_SOON:
2123 return "Unexpected EOF - invalid file";
2126 sprintf(msg, "Unknown giflib error code %d", code);
2132 =item gif_push_error()
2134 Utility function that takes the current GIF error code, converts it to
2135 an error message and pushes it on the error stack.
2140 static void gif_push_error(void) {
2141 int code = GifLastError(); /* clears saved error */
2143 i_push_error(code, gif_error_msg(code));
2149 The Netscape loop extension isn't implemented. Giflib's extension
2150 writing code doesn't seem to support writing named extensions in this
2153 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2154 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2157 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2158 supplied interface in giflib 4.1.0 causes a SEGV in
2159 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2163 Arnar M. Hrafnkelsson, addi@umich.edu