7 gif.c - read and write gif files for Imager
16 int max_colours; // number of bits per colour
17 int pixdev; // how much noise to add
18 i_color fixed[N]; // fixed palette entries
19 int fixedlen; // number of fixed colours
20 int success; // non-zero on success
21 char *data; // a GIF file in memory
22 int length; // how big data is
23 int reader(char *, char *, int, int);
24 int writer(char *, char *, int);
25 char *userdata; // user's data, whatever it is
29 img = i_readgif(fd, &colour_table, &colours);
30 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
31 success = i_writegifmc(img, fd, max_colours);
32 img = i_readgif_scalar(data, length, &colour_table, &colours);
33 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
34 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
35 success = i_writegif_callback(&quant, writer, userdata, maxlength,
40 This source file provides the C level interface to reading and writing
43 This has been tested with giflib 3 and 4, though you lose the callback
44 functionality with giflib3.
53 static char const *gif_error_msg(int code);
54 static void gif_push_error();
61 Internal. A structure passed to the reader function used for reading
64 Used with giflib 4 and later.
69 struct gif_scalar_info {
76 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
78 Internal. The reader callback passed to giflib.
80 Used with giflib 4 and later.
86 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
87 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
88 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
90 if (gsi->cpos == gsi->length) return 0;
91 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
92 memcpy(buf,gsi->data+gsi->cpos,length);
100 This file needs a complete rewrite
102 This file needs a complete rewrite
104 Maybe not anymore, though reading still needs to support reading
105 all those gif properties.
108 /* Make some variables global, so we could access them faster: */
111 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
112 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
114 /* static ColorMapObject *ColorMap; */
119 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
120 GifColorType *mapentry;
122 int colourmapsize = colourmap->ColorCount;
124 if(colours) *colours = colourmapsize;
125 if(!colour_table) return;
127 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
128 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
130 for(q=0; q<colourmapsize; q++) {
131 mapentry = &colourmap->Colors[q];
132 (*colour_table)[q*3 + 0] = mapentry->Red;
133 (*colour_table)[q*3 + 1] = mapentry->Green;
134 (*colour_table)[q*3 + 2] = mapentry->Blue;
140 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
142 Internal. Low-level function for reading a GIF file. The caller must
143 create the appropriate GifFileType object and pass it in.
149 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
151 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
152 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
153 ColorMapObject *ColorMap;
155 GifRecordType RecordType;
156 GifByteType *Extension;
159 static GifColorType *ColorMapEntry;
162 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
164 /* it's possible that the caller has called us with *colour_table being
165 non-NULL, but we check that to see if we need to free an allocated
166 colour table on error.
168 if (colour_table) *colour_table = NULL;
170 BackGround = GifFile->SBackGroundColor;
171 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
174 ColorMapSize = ColorMap->ColorCount;
175 i_colortable_copy(colour_table, colours, ColorMap);
180 im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
182 Size = GifFile->SWidth * sizeof(GifPixelType);
184 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
185 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
187 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
189 /* Scan the content of the GIF file and load the image(s) in: */
191 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
193 i_push_error(0, "Unable to get record type");
194 if (colour_table && *colour_table) {
195 myfree(*colour_table);
196 *colour_table = NULL;
199 DGifCloseFile(GifFile);
203 switch (RecordType) {
204 case IMAGE_DESC_RECORD_TYPE:
205 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
207 i_push_error(0, "Unable to get image descriptor");
208 if (colour_table && *colour_table) {
209 myfree(*colour_table);
210 *colour_table = NULL;
213 DGifCloseFile(GifFile);
217 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
218 mm_log((1, "Adding local colormap\n"));
219 ColorMapSize = ColorMap->ColorCount;
221 i_colortable_copy(colour_table, colours, ColorMap);
225 /* No colormap and we are about to read in the image - abandon for now */
226 mm_log((1, "Going in with no colormap\n"));
227 i_push_error(0, "Image does not have a local or a global color map");
228 /* we can't have allocated a colour table here */
230 DGifCloseFile(GifFile);
234 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
235 Col = GifFile->Image.Left;
236 Width = GifFile->Image.Width;
237 Height = GifFile->Image.Height;
239 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
241 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
242 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
243 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
244 if (colour_table && *colour_table) {
245 myfree(*colour_table);
246 *colour_table = NULL;
249 DGifCloseFile(GifFile);
252 if (GifFile->Image.Interlace) {
254 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
256 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
258 i_push_error(0, "Reading GIF line");
259 if (colour_table && *colour_table) {
260 myfree(*colour_table);
261 *colour_table = NULL;
264 DGifCloseFile(GifFile);
268 for (x = 0; x < Width; x++) {
269 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
270 col.rgb.r = ColorMapEntry->Red;
271 col.rgb.g = ColorMapEntry->Green;
272 col.rgb.b = ColorMapEntry->Blue;
273 i_ppix(im,Col+x,j,&col);
279 for (i = 0; i < Height; i++) {
280 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
282 i_push_error(0, "Reading GIF line");
283 if (colour_table && *colour_table) {
284 myfree(*colour_table);
285 *colour_table = NULL;
288 DGifCloseFile(GifFile);
292 for (x = 0; x < Width; x++) {
293 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
294 col.rgb.r = ColorMapEntry->Red;
295 col.rgb.g = ColorMapEntry->Green;
296 col.rgb.b = ColorMapEntry->Blue;
297 i_ppix(im, Col+x, Row, &col);
303 case EXTENSION_RECORD_TYPE:
304 /* Skip any extension blocks in file: */
305 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
307 i_push_error(0, "Reading extension record");
308 if (colour_table && *colour_table) {
309 myfree(*colour_table);
310 *colour_table = NULL;
313 DGifCloseFile(GifFile);
316 while (Extension != NULL) {
317 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
319 i_push_error(0, "reading next block of extension");
320 if (colour_table && *colour_table) {
321 myfree(*colour_table);
322 *colour_table = NULL;
325 DGifCloseFile(GifFile);
330 case TERMINATE_RECORD_TYPE:
332 default: /* Should be traps by DGifGetRecordType. */
335 } while (RecordType != TERMINATE_RECORD_TYPE);
339 if (DGifCloseFile(GifFile) == GIF_ERROR) {
341 i_push_error(0, "Closing GIF file object");
342 if (colour_table && *colour_table) {
343 myfree(*colour_table);
344 *colour_table = NULL;
353 =item i_readgif(int fd, int **colour_table, int *colours)
355 Reads in a GIF file from a file handle and converts it to an Imager
358 Returns the palette for the object in colour_table for colours
361 Returns NULL on failure.
367 i_readgif(int fd, int **colour_table, int *colours) {
368 GifFileType *GifFile;
372 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
374 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
376 i_push_error(0, "Cannot create giflib file object");
377 mm_log((1,"i_readgif: Unable to open file\n"));
381 return i_readgif_low(GifFile, colour_table, colours);
385 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
387 Write I<img> to the file handle I<fd>. The resulting GIF will use a
388 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
389 colours taken from I<fixed>.
391 Returns non-zero on success.
397 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
403 memset(&quant, 0, sizeof(quant));
404 memset(&opts, 0, sizeof(opts));
405 quant.make_colors = mc_addi;
406 quant.mc_colors = colors;
407 quant.mc_size = 1<<max_colors;
408 quant.mc_count = fixedlen;
409 memcpy(colors, fixed, fixedlen * sizeof(i_color));
410 quant.translate = pt_perturb;
411 quant.perturb = pixdev;
412 return i_writegif_gen(&quant, fd, &im, 1, &opts);
416 =item i_writegifmc(i_img *im, int fd, int max_colors)
418 Write I<img> to the file handle I<fd>. The resulting GIF will use a
419 maximum of 1<<I<max_colours> colours.
421 Returns non-zero on success.
427 i_writegifmc(i_img *im, int fd, int max_colors) {
432 memset(&quant, 0, sizeof(quant));
433 memset(&opts, 0, sizeof(opts));
434 quant.make_colors = mc_none; /* ignored for pt_giflib */
435 quant.mc_colors = colors;
436 quant.mc_size = 1 << max_colors;
438 quant.translate = pt_giflib;
439 return i_writegif_gen(&quant, fd, &im, 1, &opts);
444 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
446 Reads a GIF file from an in memory copy of the file. This can be used
447 if you get the 'file' from some source other than an actual file (or
448 some other file handle).
450 This function is only available with giflib 4 and higher.
455 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
457 GifFileType *GifFile;
458 struct gif_scalar_info gsi;
466 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
467 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
469 i_push_error(0, "Cannot create giflib callback object");
470 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
474 return i_readgif_low(GifFile, colour_table, colours);
483 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
485 Internal. The reader callback wrapper passed to giflib.
487 This function is only used with giflib 4 and higher.
493 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
494 return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
501 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
503 Read a GIF file into an Imager RGB file, the data of the GIF file is
504 retreived by callin the user supplied callback function.
506 This function is only used with giflib 4 and higher.
512 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
514 GifFileType *GifFile;
517 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
521 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
522 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
524 i_push_error(0, "Cannot create giflib callback object");
525 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
530 result = i_readgif_low(GifFile, colour_table, colours);
531 free_gen_read_data(gci);
540 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
542 Internal. Low level image write function. Writes in interlace if
543 that was requested in the GIF options.
545 Returns non-zero on success.
550 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
551 if (opts->interlace) {
553 for (i = 0; i < 4; ++i) {
554 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
555 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
557 i_push_error(0, "Could not save image data:");
558 mm_log((1, "Error in EGifPutLine\n"));
567 for (y = 0; y < img->ysize; ++y) {
568 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
570 i_push_error(0, "Could not save image data:");
571 mm_log((1, "Error in EGifPutLine\n"));
583 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
585 Internal. Writes the GIF graphics control extension, if necessary.
587 Returns non-zero on success.
591 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
593 unsigned char gce[4] = {0};
597 gce[3] = trans_index;
600 if (index < opts->delay_count) {
601 gce[1] = opts->delays[index] % 256;
602 gce[2] = opts->delays[index] / 256;
605 if (index < opts->user_input_count) {
606 if (opts->user_input_flags[index])
610 if (index < opts->disposal_count) {
611 gce[0] |= (opts->disposal[index] & 3) << 2;
615 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
617 i_push_error(0, "Could not save GCE");
624 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
626 Internal. Add the Netscape2.0 loop extension block, if requested.
628 The code for this function is currently "#if 0"ed out since the giflib
629 extension writing code currently doesn't seem to support writing
630 application extension blocks.
634 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
636 /* EGifPutExtension() doesn't appear to handle application
637 extension blocks in any way
638 Since giflib wraps the fd with a FILE * (and puts that in its
639 private data), we can't do an end-run and write the data
641 There's no open interface that takes a FILE * either, so we
642 can't workaround it that way either.
643 If giflib's callback interface wasn't broken by default, I'd
644 force file writes to use callbacks, but it is broken by default.
647 /* yes this was another attempt at supporting the loop extension */
648 if (opts->loop_count) {
649 unsigned char nsle[12] = "NETSCAPE2.0";
650 unsigned char subblock[3];
651 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
653 i_push_error(0, "writing loop extension");
657 subblock[1] = opts->loop_count % 256;
658 subblock[2] = opts->loop_count / 256;
659 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
661 i_push_error(0, "writing loop extention sub-block");
664 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
666 i_push_error(0, "writing loop extension terminator");
675 =item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
677 Create a giflib color map object from an Imager color map.
682 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
684 GifColorType colors[256];
686 int size = quant->mc_count;
690 for (i = 0; i < quant->mc_count; ++i) {
691 colors[i].Red = quant->mc_colors[i].rgb.r;
692 colors[i].Green = quant->mc_colors[i].rgb.g;
693 colors[i].Blue = quant->mc_colors[i].rgb.b;
696 colors[size].Red = opts->tran_color.rgb.r;
697 colors[size].Green = opts->tran_color.rgb.g;
698 colors[size].Blue = opts->tran_color.rgb.b;
702 while (map_size < size)
704 /* giflib spews for 1 colour maps, reasonable, I suppose */
708 map = MakeMapObject(map_size, colors);
709 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
712 i_push_error(0, "Could not create color map object");
719 =item gif_set_version(i_quantize *quant, i_gif_opts *opts)
721 We need to call EGifSetGifVersion() before opening the file - put that
724 Unfortunately giflib 4.1.0 crashes when we use this. Internally
725 giflib 4.1.0 has code:
727 static char *GifVersionPrefix = GIF87_STAMP;
729 and the code that sets the version internally does:
731 strncpy(&GifVersionPrefix[3], Version, 3);
733 which is very broken.
735 Failing to set the correct GIF version doesn't seem to cause a problem
741 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
742 /* the following crashed giflib
743 the EGifSetGifVersion() is seriously borked in giflib
744 it's less borked in the ungiflib beta, but we don't have a mechanism
746 if (opts->delay_count
747 || opts->user_input_count
748 || opts->disposal_count
750 || quant->transp != tr_none)
751 EGifSetGifVersion("89a");
753 EGifSetGifVersion("87a");
758 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
760 Internal. Low-level function that does the high-level GIF processing
763 Returns non-zero on success.
769 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
771 unsigned char *result;
774 int scrw = 0, scrh = 0;
775 int imgn, orig_count, orig_size;
778 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
779 quant, gf, imgs, count, opts));
781 /**((char *)0) = 1;*/
783 if (quant->mc_size > 256)
784 quant->mc_size = 256;
785 if (quant->mc_count > quant->mc_size)
786 quant->mc_count = quant->mc_size;
788 for (imgn = 0; imgn < count; ++imgn) {
789 if (imgn < opts->position_count) {
790 if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
791 scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
792 if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
793 scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
796 if (imgs[imgn]->xsize > scrw)
797 scrw = imgs[imgn]->xsize;
798 if (imgs[imgn]->ysize > scrh)
799 scrh = imgs[imgn]->ysize;
804 i_push_error(0, "No images provided to write");
805 return 0; /* what are you smoking? */
808 orig_count = quant->mc_count;
809 orig_size = quant->mc_size;
811 if (opts->each_palette) {
812 int want_trans = quant->transp != tr_none
813 && imgs[0]->channels == 4;
815 /* if the caller gives us too many colours we can't do transparency */
816 if (want_trans && quant->mc_count == 256)
818 /* if they want transparency but give us a big size, make it smaller
819 to give room for a transparency colour */
820 if (want_trans && quant->mc_size == 256)
823 /* we always generate a global palette - this lets systems with a
824 broken giflib work */
825 quant_makemap(quant, imgs, 1);
826 result = quant_translate(quant, imgs[0]);
829 quant_transparent(quant, result, imgs[0], quant->mc_count);
831 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
834 mm_log((1, "Error in MakeMapObject."));
839 while (quant->mc_size > (1 << color_bits))
842 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
844 i_push_error(0, "Could not save screen descriptor");
848 mm_log((1, "Error in EGifPutScreenDesc."));
853 if (!do_ns_loop(gf, opts))
856 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
861 if (opts->position_count) {
862 posx = opts->positions[0].x;
863 posy = opts->positions[0].y;
867 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
868 opts->interlace, NULL) == GIF_ERROR) {
870 i_push_error(0, "Could not save image descriptor");
872 mm_log((1, "Error in EGifPutImageDesc."));
875 if (!do_write(gf, opts, imgs[0], result)) {
880 for (imgn = 1; imgn < count; ++imgn) {
881 quant->mc_count = orig_count;
882 quant->mc_size = orig_size;
883 want_trans = quant->transp != tr_none
884 && imgs[0]->channels == 4;
885 /* if the caller gives us too many colours we can't do transparency */
886 if (want_trans && quant->mc_count == 256)
888 /* if they want transparency but give us a big size, make it smaller
889 to give room for a transparency colour */
890 if (want_trans && quant->mc_size == 256)
893 quant_makemap(quant, imgs+imgn, 1);
894 result = quant_translate(quant, imgs[imgn]);
896 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
898 if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
903 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
906 mm_log((1, "Error in MakeMapObject."));
909 if (imgn < opts->position_count) {
910 posx = opts->positions[imgn].x;
911 posy = opts->positions[imgn].y;
915 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
916 imgs[imgn]->ysize, opts->interlace,
919 i_push_error(0, "Could not save image descriptor");
923 mm_log((1, "Error in EGifPutImageDesc."));
928 if (!do_write(gf, opts, imgs[imgn], result)) {
939 /* get a palette entry for the transparency iff we have an image
940 with an alpha channel */
942 for (imgn = 0; imgn < count; ++imgn) {
943 if (imgs[imgn]->channels == 4) {
948 want_trans = want_trans && quant->transp != tr_none
949 && quant->mc_count < 256;
950 if (want_trans && quant->mc_size == 256)
953 /* handle the first image separately - since we allow giflib
954 conversion and giflib doesn't give us a separate function to build
957 /* produce a colour map */
958 quant_makemap(quant, imgs, count);
959 result = quant_translate(quant, imgs[0]);
961 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
964 mm_log((1, "Error in MakeMapObject"));
968 while (quant->mc_count > (1 << color_bits))
971 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
973 i_push_error(0, "Could not save screen descriptor");
977 mm_log((1, "Error in EGifPutScreenDesc."));
982 if (!do_ns_loop(gf, opts))
985 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
990 if (opts->position_count) {
991 posx = opts->positions[0].x;
992 posy = opts->positions[0].y;
996 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
997 opts->interlace, NULL) == GIF_ERROR) {
999 i_push_error(0, "Could not save image descriptor");
1001 mm_log((1, "Error in EGifPutImageDesc."));
1004 if (want_trans && imgs[0]->channels == 4)
1005 quant_transparent(quant, result, imgs[0], quant->mc_count);
1007 if (!do_write(gf, opts, imgs[0], result)) {
1014 for (imgn = 1; imgn < count; ++imgn) {
1016 result = quant_translate(quant, imgs[imgn]);
1017 local_trans = want_trans && imgs[imgn]->channels == 4;
1019 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1020 if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
1025 if (imgn < opts->position_count) {
1026 posx = opts->positions[imgn].x;
1027 posy = opts->positions[imgn].y;
1031 if (EGifPutImageDesc(gf, posx, posy,
1032 imgs[imgn]->xsize, imgs[imgn]->ysize,
1033 opts->interlace, NULL) == GIF_ERROR) {
1035 i_push_error(0, "Could not save image descriptor");
1038 mm_log((1, "Error in EGifPutImageDesc."));
1041 if (!do_write(gf, opts, imgs[imgn], result)) {
1049 if (EGifCloseFile(gf) == GIF_ERROR) {
1051 i_push_error(0, "Could not close GIF file");
1052 mm_log((1, "Error in EGifCloseFile\n"));
1060 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1062 General high-level function to write a GIF to a file.
1064 Writes the GIF images to the specified file handle using the options
1065 in quant and opts. See L<image.h/i_quantize> and
1066 L<image.h/i_gif_opts>.
1068 Returns non-zero on success.
1074 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
1079 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
1080 quant, fd, imgs, count, opts));
1082 gif_set_version(quant, opts);
1084 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1086 i_push_error(0, "Cannot create GIF file object");
1087 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1091 return i_writegif_low(quant, gf, imgs, count, opts);
1094 #if IM_GIFMAJOR >= 4
1097 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1099 Internal. Wrapper for the user write callback function.
1104 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1106 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1108 return i_gen_writer(gwd, data, size) ? size : 0;
1114 =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)
1116 General high-level function to write a GIF using callbacks to send
1119 Returns non-zero on success.
1125 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1126 int maxlength, i_img **imgs, int count, i_gif_opts *opts)
1128 #if IM_GIFMAJOR >= 4
1130 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1131 /* giflib declares this incorrectly as EgifOpen */
1132 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
1137 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
1138 quant, cb, userdata, maxlength, imgs, count, opts));
1140 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1142 i_push_error(0, "Cannot create GIF file object");
1143 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1144 free_gen_write_data(gwd, 0);
1148 result = i_writegif_low(quant, gf, imgs, count, opts);
1149 return free_gen_write_data(gwd, result);
1156 =item gif_error_msg(int code)
1158 Grabs the most recent giflib error code from GifLastError() and
1159 returns a string that describes that error.
1161 The returned pointer points to a static buffer, either from a literal
1162 C string or a static buffer.
1166 static char const *gif_error_msg(int code) {
1167 static char msg[80];
1170 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1171 return "Failed to open given file";
1173 case E_GIF_ERR_WRITE_FAILED:
1174 return "Write failed";
1176 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1177 return "Screen descriptor already passed to giflib";
1179 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1180 return "Image descriptor already passed to giflib";
1182 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1183 return "Neither global nor local color map set";
1185 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1186 return "Too much pixel data passed to giflib";
1188 case E_GIF_ERR_NOT_ENOUGH_MEM:
1189 return "Out of memory";
1191 case E_GIF_ERR_DISK_IS_FULL:
1192 return "Disk is full";
1194 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1195 return "File close failed";
1197 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1198 return "File not writable";
1200 case D_GIF_ERR_OPEN_FAILED:
1201 return "Failed to open file";
1203 case D_GIF_ERR_READ_FAILED:
1204 return "Failed to read from file";
1206 case D_GIF_ERR_NOT_GIF_FILE:
1207 return "File is not a GIF file";
1209 case D_GIF_ERR_NO_SCRN_DSCR:
1210 return "No screen descriptor detected - invalid file";
1212 case D_GIF_ERR_NO_IMAG_DSCR:
1213 return "No image descriptor detected - invalid file";
1215 case D_GIF_ERR_NO_COLOR_MAP:
1216 return "No global or local color map found";
1218 case D_GIF_ERR_WRONG_RECORD:
1219 return "Wrong record type detected - invalid file?";
1221 case D_GIF_ERR_DATA_TOO_BIG:
1222 return "Data in file too big for image";
1224 case D_GIF_ERR_NOT_ENOUGH_MEM:
1225 return "Out of memory";
1227 case D_GIF_ERR_CLOSE_FAILED:
1228 return "Close failed";
1230 case D_GIF_ERR_NOT_READABLE:
1231 return "File not opened for read";
1233 case D_GIF_ERR_IMAGE_DEFECT:
1234 return "Defective image";
1236 case D_GIF_ERR_EOF_TOO_SOON:
1237 return "Unexpected EOF - invalid file";
1240 sprintf(msg, "Unknown giflib error code %d", code);
1246 =item gif_push_error()
1248 Utility function that takes the current GIF error code, converts it to
1249 an error message and pushes it on the error stack.
1254 static void gif_push_error() {
1255 int code = GifLastError(); /* clears saved error */
1257 i_push_error(code, gif_error_msg(code));
1263 The Netscape loop extension isn't implemented. Giflib's extension
1264 writing code doesn't seem to support writing named extensions in this
1267 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1268 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1271 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1272 supplied interface in giflib 4.1.0 causes a SEGV in
1273 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1277 Arnar M. Hrafnkelsson, addi@umich.edu