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;
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;
1393 /* we try to build a common palette here, if we can manage that, then
1394 that's the palette we use */
1395 for (imgn = 0; imgn < count; ++imgn) {
1396 int eliminate_unused;
1397 if (imgs[imgn]->type != i_palette_type)
1400 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1401 &eliminate_unused)) {
1402 eliminate_unused = 1;
1405 if (eliminate_unused) {
1406 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1408 memset(used, 0, sizeof(used));
1410 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1411 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1412 for (x = 0; x < imgs[imgn]->xsize; ++x)
1419 /* assume all are in use */
1420 memset(used, 1, sizeof(used));
1423 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1426 i_getcolors(imgs[imgn], i, &c, 1);
1428 if (in_palette(&c, quant, size) < 0) {
1429 if (size < quant->mc_size) {
1430 quant->mc_colors[size++] = c;
1433 /* oops, too many colors */
1441 quant->mc_count = size;
1447 quant_paletted(i_quantize *quant, i_img *img) {
1448 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1450 i_palidx trans[256];
1454 /* build a translation table */
1455 for (i = 0; i < i_colorcount(img); ++i) {
1457 i_getcolors(img, i, &c, 1);
1458 trans[i] = in_palette(&c, quant, quant->mc_count);
1461 for (y = 0; y < img->ysize; ++y) {
1462 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1463 for (x = 0; x < img->xsize; ++x) {
1473 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1475 Internal. Low-level function that does the high-level GIF processing
1478 Returns non-zero on success.
1484 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
1485 unsigned char *result;
1487 ColorMapObject *map;
1488 int scrw = 0, scrh = 0;
1489 int imgn, orig_count, orig_size;
1495 i_img **glob_imgs; /* images that will use the global color map */
1497 i_color *orig_colors = quant->mc_colors;
1498 i_color *glob_colors = NULL;
1499 int glob_color_count;
1500 int glob_want_trans;
1501 int glob_paletted; /* the global map was made from the image palettes */
1502 int colors_paletted;
1507 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1508 quant, gf, imgs, count));
1510 /* *((char *)0) = 1; */ /* used to break into the debugger */
1513 i_push_error(0, "No images provided to write");
1514 return 0; /* what are you smoking? */
1517 i_mempool_init(&mp);
1519 /* sanity is nice */
1520 if (quant->mc_size > 256)
1521 quant->mc_size = 256;
1522 if (quant->mc_count > quant->mc_size)
1523 quant->mc_count = quant->mc_size;
1525 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1527 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
1531 localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
1532 glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
1534 glob_want_trans = 0;
1535 for (imgn = 0; imgn < count; ++imgn) {
1537 i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
1538 i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
1539 if (imgs[imgn]->xsize + posx > scrw)
1540 scrw = imgs[imgn]->xsize + posx;
1541 if (imgs[imgn]->ysize + posy > scrh)
1542 scrh = imgs[imgn]->ysize + posy;
1543 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
1544 localmaps[imgn] = 0;
1545 if (localmaps[imgn])
1548 if (imgs[imgn]->channels == 4) {
1549 glob_want_trans = 1;
1551 glob_imgs[glob_img_count++] = imgs[imgn];
1554 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
1556 orig_count = quant->mc_count;
1557 orig_size = quant->mc_size;
1559 if (glob_img_count) {
1561 glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
1562 quant->mc_colors = glob_colors;
1563 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1564 /* we have some images that want to use the global map */
1565 if (glob_want_trans && quant->mc_count == 256) {
1566 mm_log((2, " disabling transparency for global map - no space\n"));
1567 glob_want_trans = 0;
1569 if (glob_want_trans && quant->mc_size == 256) {
1570 mm_log((2, " reserving color for transparency\n"));
1573 if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
1578 quant_makemap(quant, glob_imgs, glob_img_count);
1580 glob_color_count = quant->mc_count;
1581 quant->mc_colors = orig_colors;
1584 /* use the global map if we have one, otherwise use the local map */
1587 quant->mc_colors = glob_colors;
1588 quant->mc_count = glob_color_count;
1589 want_trans = glob_want_trans && imgs[0]->channels == 4;
1591 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1593 if (gif_background < 0)
1595 if (gif_background >= glob_color_count)
1599 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1600 if (has_common_palette(imgs, 1, quant, want_trans)) {
1601 colors_paletted = 1;
1604 colors_paletted = 0;
1605 quant_makemap(quant, imgs, 1);
1608 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1609 i_mempool_destroy(&mp);
1610 quant->mc_colors = orig_colors;
1612 mm_log((1, "Error in MakeMapObject"));
1617 /* since we don't know how big some the local palettes could be
1618 we need to base the bits on the maximum number of colors */
1619 while (orig_size > (1 << color_bits))
1623 int count = quant->mc_count;
1626 while (count > (1 << color_bits))
1630 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1631 gif_background, map) == GIF_ERROR) {
1632 i_mempool_destroy(&mp);
1633 quant->mc_colors = orig_colors;
1635 i_push_error(0, "Could not save screen descriptor");
1639 mm_log((1, "Error in EGifPutScreenDesc."));
1644 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1646 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1649 if (!localmaps[0]) {
1651 colors_paletted = glob_paletted;
1654 /* if this image has a global map the colors in quant don't
1655 belong to this image, so build a palette */
1657 /* generate the local map for this image */
1658 quant->mc_colors = orig_colors;
1659 quant->mc_size = orig_size;
1660 quant->mc_count = orig_count;
1661 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1663 /* if the caller gives us too many colours we can't do transparency */
1664 if (want_trans && quant->mc_count == 256)
1666 /* if they want transparency but give us a big size, make it smaller
1667 to give room for a transparency colour */
1668 if (want_trans && quant->mc_size == 256)
1670 if (has_common_palette(imgs, 1, quant, want_trans)) {
1671 colors_paletted = 1;
1674 colors_paletted = 0;
1675 quant_makemap(quant, imgs, 1);
1677 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
1678 i_mempool_destroy(&mp);
1680 quant->mc_colors = orig_colors;
1681 mm_log((1, "Error in MakeMapObject"));
1686 /* the map we wrote was the map for this image - don't set the local
1692 if (colors_paletted)
1693 result = quant_paletted(quant, imgs[0]);
1695 result = quant_translate(quant, imgs[0]);
1697 i_mempool_destroy(&mp);
1698 quant->mc_colors = orig_colors;
1703 quant_transparent(quant, result, imgs[0], quant->mc_count);
1704 trans_index = quant->mc_count;
1707 if (!do_ns_loop(gf, imgs[0])) {
1708 i_mempool_destroy(&mp);
1709 quant->mc_colors = orig_colors;
1713 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
1714 i_mempool_destroy(&mp);
1715 quant->mc_colors = orig_colors;
1721 if (!do_comments(gf, imgs[0])) {
1722 i_mempool_destroy(&mp);
1723 quant->mc_colors = orig_colors;
1729 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1731 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1732 interlace, map) == GIF_ERROR) {
1733 i_mempool_destroy(&mp);
1734 quant->mc_colors = orig_colors;
1736 i_push_error(0, "Could not save image descriptor");
1738 mm_log((1, "Error in EGifPutImageDesc."));
1744 if (!do_write(gf, interlace, imgs[0], result)) {
1745 i_mempool_destroy(&mp);
1746 quant->mc_colors = orig_colors;
1753 /* that first awful image is out of the way, do the rest */
1754 for (imgn = 1; imgn < count; ++imgn) {
1755 if (localmaps[imgn]) {
1756 quant->mc_colors = orig_colors;
1757 quant->mc_count = orig_count;
1758 quant->mc_size = orig_size;
1760 want_trans = quant->transp != tr_none
1761 && imgs[imgn]->channels == 4;
1762 /* if the caller gives us too many colours we can't do transparency */
1763 if (want_trans && quant->mc_count == 256)
1765 /* if they want transparency but give us a big size, make it smaller
1766 to give room for a transparency colour */
1767 if (want_trans && quant->mc_size == 256)
1770 if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
1771 result = quant_paletted(quant, imgs[imgn]);
1774 quant_makemap(quant, imgs+imgn, 1);
1775 result = quant_translate(quant, imgs[imgn]);
1778 i_mempool_destroy(&mp);
1779 quant->mc_colors = orig_colors;
1781 mm_log((1, "error in quant_translate()"));
1785 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1786 trans_index = quant->mc_count;
1789 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
1790 i_mempool_destroy(&mp);
1791 quant->mc_colors = orig_colors;
1794 mm_log((1, "Error in MakeMapObject."));
1799 quant->mc_colors = glob_colors;
1800 quant->mc_count = glob_color_count;
1802 result = quant_paletted(quant, imgs[imgn]);
1804 result = quant_translate(quant, imgs[imgn]);
1805 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1807 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1808 trans_index = quant->mc_count;
1813 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
1814 i_mempool_destroy(&mp);
1815 quant->mc_colors = orig_colors;
1821 if (!do_comments(gf, imgs[imgn])) {
1822 i_mempool_destroy(&mp);
1823 quant->mc_colors = orig_colors;
1829 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1831 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1834 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1836 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1837 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
1838 i_mempool_destroy(&mp);
1839 quant->mc_colors = orig_colors;
1841 i_push_error(0, "Could not save image descriptor");
1846 mm_log((1, "Error in EGifPutImageDesc."));
1852 if (!do_write(gf, interlace, imgs[imgn], result)) {
1853 i_mempool_destroy(&mp);
1854 quant->mc_colors = orig_colors;
1862 if (EGifCloseFile(gf) == GIF_ERROR) {
1863 i_mempool_destroy(&mp);
1865 i_push_error(0, "Could not close GIF file");
1866 mm_log((1, "Error in EGifCloseFile\n"));
1871 for (i = 0; i < glob_color_count; ++i)
1872 orig_colors[i] = glob_colors[i];
1875 i_mempool_destroy(&mp);
1876 quant->mc_colors = orig_colors;
1882 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1884 General high-level function to write a GIF to a file.
1886 Writes the GIF images to the specified file handle using the options
1887 in quant and opts. See L<image.h/i_quantize> and
1888 L<image.h/i_gif_opts>.
1890 Returns non-zero on success.
1896 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
1900 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
1901 quant, fd, imgs, count));
1903 gif_set_version(quant, imgs, count);
1905 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1907 i_push_error(0, "Cannot create GIF file object");
1908 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1912 return i_writegif_low(quant, gf, imgs, count);
1915 #if IM_GIFMAJOR >= 4
1918 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1920 Internal. Wrapper for the user write callback function.
1925 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1927 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1929 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
1935 =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)
1937 General high-level function to write a GIF using callbacks to send
1940 Returns non-zero on success.
1946 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1947 int maxlength, i_img **imgs, int count)
1949 #if IM_GIFMAJOR >= 4
1951 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1956 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
1957 quant, cb, userdata, maxlength, imgs, count));
1959 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1961 i_push_error(0, "Cannot create GIF file object");
1962 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1963 i_free_gen_write_data(gwd, 0);
1967 result = i_writegif_low(quant, gf, imgs, count);
1968 return i_free_gen_write_data(gwd, result);
1971 i_push_error(0, "callbacks not supported with giflib3");
1977 #if IM_GIFMAJOR >= 4
1980 io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1981 io_glue *ig = (io_glue *)gft->UserData;
1983 return ig->writecb(ig, data, length);
1989 =item i_writegif_wiol(ig, quant, opts, imgs, count)
1994 i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
1996 io_glue_commit_types(ig);
1998 if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
1999 int fd = dup(ig->source.fdseek.fd);
2001 i_push_error(errno, "dup() failed");
2004 /* giflib opens the fd with fdopen(), which is then closed when fclose()
2005 is called - dup it so the caller's fd isn't closed */
2006 return i_writegif_gen(quant, fd, imgs, count);
2009 #if IM_GIFMAJOR >= 4
2010 GifFileType *GifFile;
2015 gif_set_version(quant, imgs, count);
2017 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
2019 i_push_error(0, "Cannot create giflib callback object");
2020 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
2024 result = i_writegif_low(quant, GifFile, imgs, count);
2031 i_push_error(0, "callbacks not supported with giflib3");
2039 =item gif_error_msg(int code)
2041 Grabs the most recent giflib error code from GifLastError() and
2042 returns a string that describes that error.
2044 The returned pointer points to a static buffer, either from a literal
2045 C string or a static buffer.
2050 static char const *gif_error_msg(int code) {
2051 static char msg[80];
2054 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
2055 return "Failed to open given file";
2057 case E_GIF_ERR_WRITE_FAILED:
2058 return "Write failed";
2060 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
2061 return "Screen descriptor already passed to giflib";
2063 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
2064 return "Image descriptor already passed to giflib";
2066 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
2067 return "Neither global nor local color map set";
2069 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
2070 return "Too much pixel data passed to giflib";
2072 case E_GIF_ERR_NOT_ENOUGH_MEM:
2073 return "Out of memory";
2075 case E_GIF_ERR_DISK_IS_FULL:
2076 return "Disk is full";
2078 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
2079 return "File close failed";
2081 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
2082 return "File not writable";
2084 case D_GIF_ERR_OPEN_FAILED:
2085 return "Failed to open file";
2087 case D_GIF_ERR_READ_FAILED:
2088 return "Failed to read from file";
2090 case D_GIF_ERR_NOT_GIF_FILE:
2091 return "File is not a GIF file";
2093 case D_GIF_ERR_NO_SCRN_DSCR:
2094 return "No screen descriptor detected - invalid file";
2096 case D_GIF_ERR_NO_IMAG_DSCR:
2097 return "No image descriptor detected - invalid file";
2099 case D_GIF_ERR_NO_COLOR_MAP:
2100 return "No global or local color map found";
2102 case D_GIF_ERR_WRONG_RECORD:
2103 return "Wrong record type detected - invalid file?";
2105 case D_GIF_ERR_DATA_TOO_BIG:
2106 return "Data in file too big for image";
2108 case D_GIF_ERR_NOT_ENOUGH_MEM:
2109 return "Out of memory";
2111 case D_GIF_ERR_CLOSE_FAILED:
2112 return "Close failed";
2114 case D_GIF_ERR_NOT_READABLE:
2115 return "File not opened for read";
2117 case D_GIF_ERR_IMAGE_DEFECT:
2118 return "Defective image";
2120 case D_GIF_ERR_EOF_TOO_SOON:
2121 return "Unexpected EOF - invalid file";
2124 sprintf(msg, "Unknown giflib error code %d", code);
2130 =item gif_push_error()
2132 Utility function that takes the current GIF error code, converts it to
2133 an error message and pushes it on the error stack.
2138 static void gif_push_error(void) {
2139 int code = GifLastError(); /* clears saved error */
2141 i_push_error(code, gif_error_msg(code));
2147 The Netscape loop extension isn't implemented. Giflib's extension
2148 writing code doesn't seem to support writing named extensions in this
2151 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
2152 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
2155 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
2156 supplied interface in giflib 4.1.0 causes a SEGV in
2157 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
2161 Arnar M. Hrafnkelsson, addi@umich.edu