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.
58 Internal. A structure passed to the reader function used for reading
61 Used with giflib 4 and later.
66 struct gif_scalar_info {
73 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
75 Internal. The reader callback passed to giflib.
77 Used with giflib 4 and later.
83 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
84 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
85 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
87 if (gsi->cpos == gsi->length) return 0;
88 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
89 memcpy(buf,gsi->data+gsi->cpos,length);
97 This file needs a complete rewrite
99 This file needs a complete rewrite
101 Maybe not anymore, though reading still needs to support reading
102 all those gif properties.
105 /* 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... */
113 static ColorMapObject *ColorMap;
116 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
118 Internal. Low-level function for reading a GIF file. The caller must
119 create the appropriate GifFileType object and pass it in.
124 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
126 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
127 GifRecordType RecordType;
128 GifByteType *Extension;
131 static GifColorType *ColorMapEntry;
134 /* unsigned char *Buffer, *BufferP; */
136 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
138 BackGround = GifFile->SBackGroundColor;
139 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
140 ColorMapSize = ColorMap->ColorCount;
142 /* **************************************** */
143 if(colour_table != NULL) {
145 *colour_table=mymalloc(sizeof(int *) * ColorMapSize * 3);
146 if(*colour_table == NULL)
147 m_fatal(0,"Failed to allocate memory for GIF colour table, aborted.");
149 memset(*colour_table, 0, sizeof(int *) * ColorMapSize * 3);
151 for(q=0; q<ColorMapSize; q++) {
152 ColorMapEntry = &ColorMap->Colors[q];
153 (*colour_table)[q*3 + 0]=ColorMapEntry->Red;
154 (*colour_table)[q*3 + 1]=ColorMapEntry->Green;
155 (*colour_table)[q*3 + 2]=ColorMapEntry->Blue;
159 if(colours != NULL) {
160 *colours = ColorMapSize;
163 /* **************************************** */
164 im=i_img_empty_ch(NULL,GifFile->SWidth,GifFile->SHeight,3);
166 Size = GifFile->SWidth * sizeof(GifPixelType);
168 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
169 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
171 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
173 /* Scan the content of the GIF file and load the image(s) in: */
175 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
180 switch (RecordType) {
181 case IMAGE_DESC_RECORD_TYPE:
182 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
186 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
187 Col = GifFile->Image.Left;
188 Width = GifFile->Image.Width;
189 Height = GifFile->Image.Height;
191 mm_log((1,"i_readgif: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
193 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
194 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
195 fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
198 if (GifFile->Image.Interlace) {
200 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
202 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
207 for (x = 0; x < GifFile->SWidth; x++) {
208 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
209 col.rgb.r = ColorMapEntry->Red;
210 col.rgb.g = ColorMapEntry->Green;
211 col.rgb.b = ColorMapEntry->Blue;
218 for (i = 0; i < Height; i++) {
219 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
220 mm_log((1,"fatal\n"));
224 for (x = 0; x < GifFile->SWidth; x++) {
225 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
226 col.rgb.r = ColorMapEntry->Red;
227 col.rgb.g = ColorMapEntry->Green;
228 col.rgb.b = ColorMapEntry->Blue;
229 i_ppix(im,x,Row,&col);
235 case EXTENSION_RECORD_TYPE:
236 /* Skip any extension blocks in file: */
237 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
238 mm_log((1,"fatal\n"));
241 while (Extension != NULL) {
242 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
243 mm_log((1,"fatal\n"));
248 case TERMINATE_RECORD_TYPE:
250 default: /* Should be traps by DGifGetRecordType. */
253 } while (RecordType != TERMINATE_RECORD_TYPE);
257 if (DGifCloseFile(GifFile) == GIF_ERROR) {
265 =item i_readgif(int fd, int **colour_table, int *colours)
267 Reads in a GIF file from a file handle and converts it to an Imager
270 Returns the palette for the object in colour_table for colours
273 Returns NULL on failure.
279 i_readgif(int fd, int **colour_table, int *colours) {
280 GifFileType *GifFile;
282 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
284 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
285 mm_log((1,"i_readgif: Unable to open file\n"));
289 return i_readgif_low(GifFile, colour_table, colours);
293 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
295 Write I<img> to the file handle I<fd>. The resulting GIF will use a
296 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
297 colours taken from I<fixed>.
299 Returns non-zero on success.
305 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
311 memset(&quant, 0, sizeof(quant));
312 memset(&opts, 0, sizeof(opts));
313 quant.make_colors = mc_addi;
314 quant.mc_colors = colors;
315 quant.mc_size = 1<<max_colors;
316 quant.mc_count = fixedlen;
317 memcpy(colors, fixed, fixedlen * sizeof(i_color));
318 quant.translate = pt_perturb;
319 quant.perturb = pixdev;
320 return i_writegif_gen(&quant, fd, &im, 1, &opts);
324 =item i_writegifmc(i_img *im, int fd, int max_colors)
326 Write I<img> to the file handle I<fd>. The resulting GIF will use a
327 maximum of 1<<I<max_colours> colours.
329 Returns non-zero on success.
335 i_writegifmc(i_img *im, int fd, int max_colors) {
340 memset(&quant, 0, sizeof(quant));
341 memset(&opts, 0, sizeof(opts));
342 quant.make_colors = mc_none; /* ignored for pt_giflib */
343 quant.mc_colors = colors;
344 quant.mc_size = 1 << max_colors;
346 quant.translate = pt_giflib;
347 return i_writegif_gen(&quant, fd, &im, 1, &opts);
353 i_writegifex(i_img *im, int fd) {
359 /* I don't think this works
360 note that RedBuffer is never allocated - TC
364 i_writegifex(i_img *im,int fd) {
365 int colors, xsize, ysize, channels;
366 int x,y,ColorMapSize;
371 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,*OutputBuffer = NULL;
372 ColorMapObject *OutputColorMap = NULL;
373 GifFileType *GifFile;
378 mm_log((1,"i_writegif(0x%x,fd %d)\n",im,fd));
380 if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }
384 channels=im->channels;
390 for(x=0;x<xsize;x++) for(y=0;y<ysize;y++) {
392 colors+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
393 /* if (colors > maxc) { octt_delete(ct); }
394 We'll just bite the bullet */
397 ColorMapSize = (colors > 256) ? 256 : colors;
399 Size = ((long) im->xsize) * im->ysize * sizeof(GifByteType);
401 if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
402 m_fatal(0,"Failed to allocate memory for Output colormap.");
403 if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
404 m_fatal(0,"Failed to allocate memory for output buffer.");
406 if (QuantizeBuffer(im->xsize, im->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
407 OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
408 mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
414 if (im->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
416 if ((GifFile = EGifOpenFileHandle(fd)) == NULL) {
417 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
421 if (EGifPutScreenDesc(GifFile,im->xsize, im->ysize, colors, 0,OutputColorMap) == GIF_ERROR ||
422 EGifPutImageDesc(GifFile,0, 0, im->xsize, im->ysize, FALSE, NULL) == GIF_ERROR) {
423 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
424 if (GifFile != NULL) EGifCloseFile(GifFile);
430 for (y = 0; y < im->ysize; y++) {
431 if (EGifPutLine(GifFile, Ptr, im->xsize) == GIF_ERROR) {
432 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
433 if (GifFile != NULL) EGifCloseFile(GifFile);
440 if (EGifCloseFile(GifFile) == GIF_ERROR) {
441 mm_log((1,"Error in EGifCloseFile, unable to write image.\n"));
450 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
452 Reads a GIF file from an in memory copy of the file. This can be used
453 if you get the 'file' from some source other than an actual file (or
454 some other file handle).
456 This function is only available with giflib 4 and higher.
461 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
463 GifFileType *GifFile;
465 struct gif_scalar_info gsi;
471 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
472 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
473 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
477 return i_readgif_low(GifFile, colour_table, colours);
486 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
488 Internal. The reader callback wrapper passed to giflib.
490 This function is only used with giflib 4 and higher.
496 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
497 return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
504 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
506 Read a GIF file into an Imager RGB file, the data of the GIF file is
507 retreived by callin the user supplied callback function.
509 This function is only used with giflib 4 and higher.
515 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
517 GifFileType *GifFile;
520 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
522 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
523 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
524 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
529 result = i_readgif_low(GifFile, colour_table, colours);
530 free_gen_read_data(gci);
539 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
541 Internal. Low level image write function. Writes in interlace if
542 that was requested in the GIF options.
544 Returns non-zero on success.
549 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
550 if (opts->interlace) {
552 for (i = 0; i < 4; ++i) {
553 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
554 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
555 mm_log((1, "Error in EGifPutLine\n"));
564 for (y = 0; y < img->ysize; ++y) {
565 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
566 mm_log((1, "Error in EGifPutLine\n"));
578 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
580 Internal. Writes the GIF graphics control extension, if necessary.
582 Returns non-zero on success.
586 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
588 unsigned char gce[4] = {0};
592 gce[3] = trans_index;
595 if (index < opts->delay_count) {
596 gce[1] = opts->delays[index] % 256;
597 gce[2] = opts->delays[index] / 256;
600 if (index < opts->user_input_count) {
601 if (opts->user_input_flags[index])
605 if (index < opts->disposal_count) {
606 gce[0] |= (opts->disposal[index] & 3) << 2;
610 return EGifPutExtension(gf, 0xF9, sizeof(gce), gce) != GIF_ERROR;
616 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
618 Internal. Add the Netscape2.0 loop extension block, if requested.
620 The code for this function is currently "#if 0"ed out since the giflib
621 extension writing code currently doesn't seem to support writing
622 application extension blocks.
626 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
629 /* EGifPutExtension() doesn't appear to handle application
630 extension blocks in any way
631 Since giflib wraps the fd with a FILE * (and puts that in its
632 private data), we can't do an end-run and write the data
634 There's no open interface that takes a FILE * either, so we
635 can't workaround it that way either.
636 If giflib's callback interface wasn't broken by default, I'd
637 force file writes to use callbacks, but it is broken by default.
639 if (opts->loop_count) {
640 unsigned char nsle[15] = "NETSCAPE2.0";
643 nsle[13] = opts->loop_count % 256;
644 nsle[14] = opts->loop_count / 256;
645 return EGifPutExtension(gf, 0xFF, sizeof(nsle), nsle) != GIF_ERROR;
652 =item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
654 Create a giflib color map object from an Imager color map.
659 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
661 GifColorType colors[256];
663 int size = quant->mc_count;
666 for (i = 0; i < quant->mc_count; ++i) {
667 colors[i].Red = quant->mc_colors[i].rgb.r;
668 colors[i].Green = quant->mc_colors[i].rgb.g;
669 colors[i].Blue = quant->mc_colors[i].rgb.b;
672 colors[size].Red = opts->tran_color.rgb.r;
673 colors[size].Green = opts->tran_color.rgb.g;
674 colors[size].Blue = opts->tran_color.rgb.b;
678 while (map_size < size)
680 /* giflib spews for 1 colour maps, reasonable, I suppose */
683 return MakeMapObject(map_size, colors);
687 =item gif_set_version(i_quantize *quant, i_gif_opts *opts)
689 We need to call EGifSetGifVersion() before opening the file - put that
692 Unfortunately giflib 4.1.0 crashes when we use this. Internally
693 giflib 4.1.0 has code:
695 static char *GifVersionPrefix = GIF87_STAMP;
697 and the code that sets the version internally does:
699 strncpy(&GifVersionPrefix[3], Version, 3);
701 which is very broken.
703 Failing to set the correct GIF version doesn't seem to cause a problem
709 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
710 /* the following crashed giflib
711 the EGifSetGifVersion() is seriously borked in giflib
712 it's less borked in the ungiflib beta, but we don't have a mechanism
714 if (opts->delay_count
715 || opts->user_input_count
716 || opts->disposal_count
718 || quant->transp != tr_none)
719 EGifSetGifVersion("89a");
721 EGifSetGifVersion("87a");
726 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
728 Internal. Low-level function that does the high-level GIF processing
731 Returns non-zero on success.
737 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
739 unsigned char *result;
742 int scrw = 0, scrh = 0;
743 int imgn, orig_count, orig_size;
746 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
747 quant, gf, imgs, count, opts));
749 /**((char *)0) = 1;*/
751 if (quant->mc_size > 256)
752 quant->mc_size = 256;
753 if (quant->mc_count > quant->mc_size)
754 quant->mc_count = quant->mc_size;
756 for (imgn = 0; imgn < count; ++imgn) {
757 if (imgn < opts->position_count) {
758 if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
759 scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
760 if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
761 scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
764 if (imgs[imgn]->xsize > scrw)
765 scrw = imgs[imgn]->xsize;
766 if (imgs[imgn]->ysize > scrh)
767 scrh = imgs[imgn]->ysize;
773 return 0; /* what are you smoking? */
775 orig_count = quant->mc_count;
776 orig_size = quant->mc_size;
778 if (opts->each_palette) {
779 int want_trans = quant->transp != tr_none
780 && imgs[0]->channels == 4;
782 /* if the caller gives us too many colours we can't do transparency */
783 if (want_trans && quant->mc_count == 256)
785 /* if they want transparency but give us a big size, make it smaller
786 to give room for a transparency colour */
787 if (want_trans && quant->mc_size == 256)
790 /* we always generate a global palette - this lets systems with a
791 broken giflib work */
792 quant_makemap(quant, imgs, 1);
793 result = quant_translate(quant, imgs[0]);
796 quant_transparent(quant, result, imgs[0], quant->mc_count);
798 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
801 mm_log((1, "Error in MakeMapObject."));
806 while (quant->mc_size > (1 << color_bits))
809 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
813 mm_log((1, "Error in EGifPutScreenDesc."));
818 if (!do_ns_loop(gf, opts))
821 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
826 if (opts->position_count) {
827 posx = opts->positions[0].x;
828 posy = opts->positions[0].y;
832 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
833 opts->interlace, NULL) == GIF_ERROR) {
835 mm_log((1, "Error in EGifPutImageDesc."));
838 if (!do_write(gf, opts, imgs[0], result)) {
843 for (imgn = 1; imgn < count; ++imgn) {
844 quant->mc_count = orig_count;
845 quant->mc_size = orig_size;
846 want_trans = quant->transp != tr_none
847 && imgs[0]->channels == 4;
848 /* if the caller gives us too many colours we can't do transparency */
849 if (want_trans && quant->mc_count == 256)
851 /* if they want transparency but give us a big size, make it smaller
852 to give room for a transparency colour */
853 if (want_trans && quant->mc_size == 256)
856 quant_makemap(quant, imgs+imgn, 1);
857 result = quant_translate(quant, imgs[imgn]);
859 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
861 if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
866 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
869 mm_log((1, "Error in MakeMapObject."));
872 if (imgn < opts->position_count) {
873 posx = opts->positions[imgn].x;
874 posy = opts->positions[imgn].y;
878 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
879 imgs[imgn]->ysize, opts->interlace,
884 mm_log((1, "Error in EGifPutImageDesc."));
889 if (!do_write(gf, opts, imgs[imgn], result)) {
900 /* get a palette entry for the transparency iff we have an image
901 with an alpha channel */
903 for (imgn = 0; imgn < count; ++imgn) {
904 if (imgs[imgn]->channels == 4) {
909 want_trans = want_trans && quant->transp != tr_none
910 && quant->mc_count < 256;
911 if (want_trans && quant->mc_size == 256)
914 /* handle the first image separately - since we allow giflib
915 conversion and giflib doesn't give us a separate function to build
918 /* produce a colour map */
919 quant_makemap(quant, imgs, count);
920 result = quant_translate(quant, imgs[0]);
922 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
925 mm_log((1, "Error in MakeMapObject"));
929 while (quant->mc_count > (1 << color_bits))
932 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
936 mm_log((1, "Error in EGifPutScreenDesc."));
941 if (!do_ns_loop(gf, opts))
944 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
949 if (opts->position_count) {
950 posx = opts->positions[0].x;
951 posy = opts->positions[0].y;
955 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
956 opts->interlace, NULL) == GIF_ERROR) {
958 mm_log((1, "Error in EGifPutImageDesc."));
961 if (want_trans && imgs[0]->channels == 4)
962 quant_transparent(quant, result, imgs[0], quant->mc_count);
964 if (!do_write(gf, opts, imgs[0], result)) {
971 for (imgn = 1; imgn < count; ++imgn) {
973 result = quant_translate(quant, imgs[imgn]);
974 local_trans = want_trans && imgs[imgn]->channels == 4;
976 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
977 if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
982 if (imgn < opts->position_count) {
983 posx = opts->positions[imgn].x;
984 posy = opts->positions[imgn].y;
988 if (EGifPutImageDesc(gf, posx, posy,
989 imgs[imgn]->xsize, imgs[imgn]->ysize,
990 opts->interlace, NULL) == GIF_ERROR) {
993 mm_log((1, "Error in EGifPutImageDesc."));
996 if (!do_write(gf, opts, imgs[imgn], result)) {
1004 if (EGifCloseFile(gf) == GIF_ERROR) {
1005 mm_log((1, "Error in EGifCloseFile\n"));
1013 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1015 General high-level function to write a GIF to a file.
1017 Writes the GIF images to the specified file handle using the options
1018 in quant and opts. See L<image.h/i_quantize> and
1019 L<image.h/i_gif_opts>.
1021 Returns non-zero on success.
1027 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
1031 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
1032 quant, fd, imgs, count, opts));
1034 gif_set_version(quant, opts);
1036 mm_log((1, "i_writegif_gen: set ops\n"));
1038 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1039 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1043 return i_writegif_low(quant, gf, imgs, count, opts);
1046 #if IM_GIFMAJOR >= 4
1049 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1051 Internal. Wrapper for the user write callback function.
1056 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1058 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1060 return i_gen_writer(gwd, data, size) ? size : 0;
1066 =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)
1068 General high-level function to write a GIF using callbacks to send
1071 Returns non-zero on success.
1077 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1078 int maxlength, i_img **imgs, int count, i_gif_opts *opts)
1080 #if IM_GIFMAJOR >= 4
1082 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1083 /* giflib declares this incorrectly as EgifOpen */
1084 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
1087 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
1088 quant, cb, userdata, maxlength, imgs, count, opts));
1090 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1091 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1092 free_gen_write_data(gwd, 0);
1096 result = i_writegif_low(quant, gf, imgs, count, opts);
1097 return free_gen_write_data(gwd, result);
1106 The Netscape loop extension isn't implemented. Giflib's extension
1107 writing code doesn't seem to support writing named extensions in this
1110 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1111 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1114 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1115 supplied interface in giflib 4.1.0 causes a SEGV in
1116 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1120 Arnar M. Hrafnkelsson, addi@umich.edu