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: */
113 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
114 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
116 static ColorMapObject *ColorMap;
121 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colormap) {
122 GifColorType *mapentry;
124 int colormapsize = colormap->ColorCount;
126 if(colours) *colours = colormapsize;
127 if(!colour_table) return;
129 *colour_table = mymalloc(sizeof(int *) * colormapsize * 3);
130 memset(*colour_table, 0, sizeof(int *) * colormapsize * 3);
132 for(q=0; q<ColorMapSize; q++) {
133 mapentry = &colormap->Colors[q];
134 (*colour_table)[q*3 + 0] = mapentry->Red;
135 (*colour_table)[q*3 + 1] = mapentry->Green;
136 (*colour_table)[q*3 + 2] = mapentry->Blue;
142 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
144 Internal. Low-level function for reading a GIF file. The caller must
145 create the appropriate GifFileType object and pass it in.
151 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
153 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
154 int cmapcnt = 0, ImageNum = 0;
157 GifRecordType RecordType;
158 GifByteType *Extension;
161 static GifColorType *ColorMapEntry;
164 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
166 /* it's possible that the caller has called us with *colour_table being
167 non-NULL, but we check that to see if we need to free an allocated
168 colour table on error.
171 *colour_table = NULL;
173 BackGround = GifFile->SBackGroundColor;
174 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
177 ColorMapSize = ColorMap->ColorCount;
178 i_colortable_copy(colour_table, colours, ColorMap);
183 im = i_img_empty_ch(NULL,GifFile->SWidth,GifFile->SHeight,3);
185 Size = GifFile->SWidth * sizeof(GifPixelType);
187 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
188 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
190 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
192 /* Scan the content of the GIF file and load the image(s) in: */
194 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
196 i_push_error(0, "Unable to get record type");
197 if (colour_table && *colour_table) {
199 *colour_table = NULL;
202 DGifCloseFile(GifFile);
206 switch (RecordType) {
207 case IMAGE_DESC_RECORD_TYPE:
208 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
210 i_push_error(0, "Unable to get image descriptor");
211 if (colour_table && *colour_table) {
213 *colour_table = NULL;
216 DGifCloseFile(GifFile);
221 if (ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) ) {
222 mm_log((1, "Adding local colormap\n"));
223 ColorMapSize = ColorMap->ColorCount;
224 i_colortable_copy(colour_table, colours, ColorMap);
227 /* No colormap and we are about to read in the image - abandon for now */
228 mm_log((1, "Going in with no colormap\n"));
229 i_push_error(0, "Image does not have a local or a global color map");
230 /* we can't have allocated a colour table here */
232 DGifCloseFile(GifFile);
236 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
237 Col = GifFile->Image.Left;
238 Width = GifFile->Image.Width;
239 Height = GifFile->Image.Height;
241 mm_log((1,"i_readgif: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
243 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
244 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
245 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
246 if (colour_table && *colour_table) {
248 *colour_table = NULL;
251 DGifCloseFile(GifFile);
254 if (GifFile->Image.Interlace) {
256 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
258 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
260 i_push_error(0, "Reading GIF line");
261 if (colour_table && *colour_table) {
263 *colour_table = NULL;
266 DGifCloseFile(GifFile);
270 for (x = 0; x < GifFile->SWidth; x++) {
271 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
272 col.rgb.r = ColorMapEntry->Red;
273 col.rgb.g = ColorMapEntry->Green;
274 col.rgb.b = ColorMapEntry->Blue;
281 for (i = 0; i < Height; i++) {
282 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
284 i_push_error(0, "Reading GIF line");
285 if (colour_table && *colour_table) {
287 *colour_table = NULL;
290 DGifCloseFile(GifFile);
294 for (x = 0; x < GifFile->SWidth; x++) {
295 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
296 col.rgb.r = ColorMapEntry->Red;
297 col.rgb.g = ColorMapEntry->Green;
298 col.rgb.b = ColorMapEntry->Blue;
299 i_ppix(im,x,Row,&col);
305 case EXTENSION_RECORD_TYPE:
306 /* Skip any extension blocks in file: */
307 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
309 i_push_error(0, "Reading extension record");
310 if (colour_table && *colour_table) {
312 *colour_table = NULL;
315 DGifCloseFile(GifFile);
318 while (Extension != NULL) {
319 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
321 i_push_error(0, "reading next block of extension");
322 if (colour_table && *colour_table) {
324 *colour_table = NULL;
327 DGifCloseFile(GifFile);
332 case TERMINATE_RECORD_TYPE:
334 default: /* Should be traps by DGifGetRecordType. */
337 } while (RecordType != TERMINATE_RECORD_TYPE);
341 if (DGifCloseFile(GifFile) == GIF_ERROR) {
343 i_push_error(0, "Closing GIF file object");
344 if (colour_table && *colour_table) {
346 *colour_table = NULL;
355 =item i_readgif(int fd, int **colour_table, int *colours)
357 Reads in a GIF file from a file handle and converts it to an Imager
360 Returns the palette for the object in colour_table for colours
363 Returns NULL on failure.
369 i_readgif(int fd, int **colour_table, int *colours) {
370 GifFileType *GifFile;
374 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
376 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
378 i_push_error(0, "Cannot create giflib file object");
379 mm_log((1,"i_readgif: Unable to open file\n"));
383 return i_readgif_low(GifFile, colour_table, colours);
387 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
389 Write I<img> to the file handle I<fd>. The resulting GIF will use a
390 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
391 colours taken from I<fixed>.
393 Returns non-zero on success.
399 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
405 memset(&quant, 0, sizeof(quant));
406 memset(&opts, 0, sizeof(opts));
407 quant.make_colors = mc_addi;
408 quant.mc_colors = colors;
409 quant.mc_size = 1<<max_colors;
410 quant.mc_count = fixedlen;
411 memcpy(colors, fixed, fixedlen * sizeof(i_color));
412 quant.translate = pt_perturb;
413 quant.perturb = pixdev;
414 return i_writegif_gen(&quant, fd, &im, 1, &opts);
418 =item i_writegifmc(i_img *im, int fd, int max_colors)
420 Write I<img> to the file handle I<fd>. The resulting GIF will use a
421 maximum of 1<<I<max_colours> colours.
423 Returns non-zero on success.
429 i_writegifmc(i_img *im, int fd, int max_colors) {
434 memset(&quant, 0, sizeof(quant));
435 memset(&opts, 0, sizeof(opts));
436 quant.make_colors = mc_none; /* ignored for pt_giflib */
437 quant.mc_colors = colors;
438 quant.mc_size = 1 << max_colors;
440 quant.translate = pt_giflib;
441 return i_writegif_gen(&quant, fd, &im, 1, &opts);
447 i_writegifex(i_img *im, int fd) {
453 /* I don't think this works
454 note that RedBuffer is never allocated - TC
458 i_writegifex(i_img *im,int fd) {
459 int colors, xsize, ysize, channels;
460 int x,y,ColorMapSize;
465 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,*OutputBuffer = NULL;
466 ColorMapObject *OutputColorMap = NULL;
467 GifFileType *GifFile;
472 mm_log((1,"i_writegif(0x%x,fd %d)\n",im,fd));
474 if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }
478 channels=im->channels;
484 for(x=0;x<xsize;x++) for(y=0;y<ysize;y++) {
486 colors+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
487 /* if (colors > maxc) { octt_delete(ct); }
488 We'll just bite the bullet */
491 ColorMapSize = (colors > 256) ? 256 : colors;
493 Size = ((long) im->xsize) * im->ysize * sizeof(GifByteType);
495 if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
496 m_fatal(0,"Failed to allocate memory for Output colormap.");
497 if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
498 m_fatal(0,"Failed to allocate memory for output buffer.");
500 if (QuantizeBuffer(im->xsize, im->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
501 OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
502 mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
508 if (im->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
510 if ((GifFile = EGifOpenFileHandle(fd)) == NULL) {
511 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
515 if (EGifPutScreenDesc(GifFile,im->xsize, im->ysize, colors, 0,OutputColorMap) == GIF_ERROR ||
516 EGifPutImageDesc(GifFile,0, 0, im->xsize, im->ysize, FALSE, NULL) == GIF_ERROR) {
517 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
518 if (GifFile != NULL) EGifCloseFile(GifFile);
524 for (y = 0; y < im->ysize; y++) {
525 if (EGifPutLine(GifFile, Ptr, im->xsize) == GIF_ERROR) {
526 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
527 if (GifFile != NULL) EGifCloseFile(GifFile);
534 if (EGifCloseFile(GifFile) == GIF_ERROR) {
535 mm_log((1,"Error in EGifCloseFile, unable to write image.\n"));
544 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
546 Reads a GIF file from an in memory copy of the file. This can be used
547 if you get the 'file' from some source other than an actual file (or
548 some other file handle).
550 This function is only available with giflib 4 and higher.
555 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
557 GifFileType *GifFile;
558 struct gif_scalar_info gsi;
566 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
567 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
569 i_push_error(0, "Cannot create giflib callback object");
570 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
574 return i_readgif_low(GifFile, colour_table, colours);
583 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
585 Internal. The reader callback wrapper passed to giflib.
587 This function is only used with giflib 4 and higher.
593 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
594 return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
601 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
603 Read a GIF file into an Imager RGB file, the data of the GIF file is
604 retreived by callin the user supplied callback function.
606 This function is only used with giflib 4 and higher.
612 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
614 GifFileType *GifFile;
617 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
621 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
622 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
624 i_push_error(0, "Cannot create giflib callback object");
625 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
630 result = i_readgif_low(GifFile, colour_table, colours);
631 free_gen_read_data(gci);
640 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
642 Internal. Low level image write function. Writes in interlace if
643 that was requested in the GIF options.
645 Returns non-zero on success.
650 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
651 if (opts->interlace) {
653 for (i = 0; i < 4; ++i) {
654 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
655 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
657 i_push_error(0, "Could not save image data:");
658 mm_log((1, "Error in EGifPutLine\n"));
667 for (y = 0; y < img->ysize; ++y) {
668 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
670 i_push_error(0, "Could not save image data:");
671 mm_log((1, "Error in EGifPutLine\n"));
683 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
685 Internal. Writes the GIF graphics control extension, if necessary.
687 Returns non-zero on success.
691 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
693 unsigned char gce[4] = {0};
697 gce[3] = trans_index;
700 if (index < opts->delay_count) {
701 gce[1] = opts->delays[index] % 256;
702 gce[2] = opts->delays[index] / 256;
705 if (index < opts->user_input_count) {
706 if (opts->user_input_flags[index])
710 if (index < opts->disposal_count) {
711 gce[0] |= (opts->disposal[index] & 3) << 2;
715 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
717 i_push_error(0, "Could not save GCE");
724 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
726 Internal. Add the Netscape2.0 loop extension block, if requested.
728 The code for this function is currently "#if 0"ed out since the giflib
729 extension writing code currently doesn't seem to support writing
730 application extension blocks.
734 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
736 /* EGifPutExtension() doesn't appear to handle application
737 extension blocks in any way
738 Since giflib wraps the fd with a FILE * (and puts that in its
739 private data), we can't do an end-run and write the data
741 There's no open interface that takes a FILE * either, so we
742 can't workaround it that way either.
743 If giflib's callback interface wasn't broken by default, I'd
744 force file writes to use callbacks, but it is broken by default.
747 /* yes this was another attempt at supporting the loop extension */
748 if (opts->loop_count) {
749 unsigned char nsle[12] = "NETSCAPE2.0";
750 unsigned char subblock[3];
751 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
753 i_push_error(0, "writing loop extension");
757 subblock[1] = opts->loop_count % 256;
758 subblock[2] = opts->loop_count / 256;
759 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
761 i_push_error(0, "writing loop extention sub-block");
764 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
766 i_push_error(0, "writing loop extension terminator");
775 =item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
777 Create a giflib color map object from an Imager color map.
782 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
784 GifColorType colors[256];
786 int size = quant->mc_count;
790 for (i = 0; i < quant->mc_count; ++i) {
791 colors[i].Red = quant->mc_colors[i].rgb.r;
792 colors[i].Green = quant->mc_colors[i].rgb.g;
793 colors[i].Blue = quant->mc_colors[i].rgb.b;
796 colors[size].Red = opts->tran_color.rgb.r;
797 colors[size].Green = opts->tran_color.rgb.g;
798 colors[size].Blue = opts->tran_color.rgb.b;
802 while (map_size < size)
804 /* giflib spews for 1 colour maps, reasonable, I suppose */
807 map = MakeMapObject(map_size, colors);
810 i_push_error(0, "Could not create color map object");
817 =item gif_set_version(i_quantize *quant, i_gif_opts *opts)
819 We need to call EGifSetGifVersion() before opening the file - put that
822 Unfortunately giflib 4.1.0 crashes when we use this. Internally
823 giflib 4.1.0 has code:
825 static char *GifVersionPrefix = GIF87_STAMP;
827 and the code that sets the version internally does:
829 strncpy(&GifVersionPrefix[3], Version, 3);
831 which is very broken.
833 Failing to set the correct GIF version doesn't seem to cause a problem
839 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
840 /* the following crashed giflib
841 the EGifSetGifVersion() is seriously borked in giflib
842 it's less borked in the ungiflib beta, but we don't have a mechanism
844 if (opts->delay_count
845 || opts->user_input_count
846 || opts->disposal_count
848 || quant->transp != tr_none)
849 EGifSetGifVersion("89a");
851 EGifSetGifVersion("87a");
856 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
858 Internal. Low-level function that does the high-level GIF processing
861 Returns non-zero on success.
867 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
869 unsigned char *result;
872 int scrw = 0, scrh = 0;
873 int imgn, orig_count, orig_size;
876 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
877 quant, gf, imgs, count, opts));
879 /**((char *)0) = 1;*/
881 if (quant->mc_size > 256)
882 quant->mc_size = 256;
883 if (quant->mc_count > quant->mc_size)
884 quant->mc_count = quant->mc_size;
886 for (imgn = 0; imgn < count; ++imgn) {
887 if (imgn < opts->position_count) {
888 if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
889 scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
890 if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
891 scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
894 if (imgs[imgn]->xsize > scrw)
895 scrw = imgs[imgn]->xsize;
896 if (imgs[imgn]->ysize > scrh)
897 scrh = imgs[imgn]->ysize;
902 i_push_error(0, "No images provided to write");
903 return 0; /* what are you smoking? */
906 orig_count = quant->mc_count;
907 orig_size = quant->mc_size;
909 if (opts->each_palette) {
910 int want_trans = quant->transp != tr_none
911 && imgs[0]->channels == 4;
913 /* if the caller gives us too many colours we can't do transparency */
914 if (want_trans && quant->mc_count == 256)
916 /* if they want transparency but give us a big size, make it smaller
917 to give room for a transparency colour */
918 if (want_trans && quant->mc_size == 256)
921 /* we always generate a global palette - this lets systems with a
922 broken giflib work */
923 quant_makemap(quant, imgs, 1);
924 result = quant_translate(quant, imgs[0]);
927 quant_transparent(quant, result, imgs[0], quant->mc_count);
929 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
932 mm_log((1, "Error in MakeMapObject."));
937 while (quant->mc_size > (1 << color_bits))
940 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
942 i_push_error(0, "Could not save screen descriptor");
946 mm_log((1, "Error in EGifPutScreenDesc."));
951 if (!do_ns_loop(gf, opts))
954 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
959 if (opts->position_count) {
960 posx = opts->positions[0].x;
961 posy = opts->positions[0].y;
965 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
966 opts->interlace, NULL) == GIF_ERROR) {
968 i_push_error(0, "Could not save image descriptor");
970 mm_log((1, "Error in EGifPutImageDesc."));
973 if (!do_write(gf, opts, imgs[0], result)) {
978 for (imgn = 1; imgn < count; ++imgn) {
979 quant->mc_count = orig_count;
980 quant->mc_size = orig_size;
981 want_trans = quant->transp != tr_none
982 && imgs[0]->channels == 4;
983 /* if the caller gives us too many colours we can't do transparency */
984 if (want_trans && quant->mc_count == 256)
986 /* if they want transparency but give us a big size, make it smaller
987 to give room for a transparency colour */
988 if (want_trans && quant->mc_size == 256)
991 quant_makemap(quant, imgs+imgn, 1);
992 result = quant_translate(quant, imgs[imgn]);
994 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
996 if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
1001 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1004 mm_log((1, "Error in MakeMapObject."));
1007 if (imgn < opts->position_count) {
1008 posx = opts->positions[imgn].x;
1009 posy = opts->positions[imgn].y;
1013 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1014 imgs[imgn]->ysize, opts->interlace,
1015 map) == GIF_ERROR) {
1017 i_push_error(0, "Could not save image descriptor");
1021 mm_log((1, "Error in EGifPutImageDesc."));
1026 if (!do_write(gf, opts, imgs[imgn], result)) {
1037 /* get a palette entry for the transparency iff we have an image
1038 with an alpha channel */
1040 for (imgn = 0; imgn < count; ++imgn) {
1041 if (imgs[imgn]->channels == 4) {
1046 want_trans = want_trans && quant->transp != tr_none
1047 && quant->mc_count < 256;
1048 if (want_trans && quant->mc_size == 256)
1051 /* handle the first image separately - since we allow giflib
1052 conversion and giflib doesn't give us a separate function to build
1055 /* produce a colour map */
1056 quant_makemap(quant, imgs, count);
1057 result = quant_translate(quant, imgs[0]);
1059 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1062 mm_log((1, "Error in MakeMapObject"));
1066 while (quant->mc_count > (1 << color_bits))
1069 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
1071 i_push_error(0, "Could not save screen descriptor");
1075 mm_log((1, "Error in EGifPutScreenDesc."));
1080 if (!do_ns_loop(gf, opts))
1083 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
1088 if (opts->position_count) {
1089 posx = opts->positions[0].x;
1090 posy = opts->positions[0].y;
1094 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1095 opts->interlace, NULL) == GIF_ERROR) {
1097 i_push_error(0, "Could not save image descriptor");
1099 mm_log((1, "Error in EGifPutImageDesc."));
1102 if (want_trans && imgs[0]->channels == 4)
1103 quant_transparent(quant, result, imgs[0], quant->mc_count);
1105 if (!do_write(gf, opts, imgs[0], result)) {
1112 for (imgn = 1; imgn < count; ++imgn) {
1114 result = quant_translate(quant, imgs[imgn]);
1115 local_trans = want_trans && imgs[imgn]->channels == 4;
1117 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1118 if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
1123 if (imgn < opts->position_count) {
1124 posx = opts->positions[imgn].x;
1125 posy = opts->positions[imgn].y;
1129 if (EGifPutImageDesc(gf, posx, posy,
1130 imgs[imgn]->xsize, imgs[imgn]->ysize,
1131 opts->interlace, NULL) == GIF_ERROR) {
1133 i_push_error(0, "Could not save image descriptor");
1136 mm_log((1, "Error in EGifPutImageDesc."));
1139 if (!do_write(gf, opts, imgs[imgn], result)) {
1147 if (EGifCloseFile(gf) == GIF_ERROR) {
1149 i_push_error(0, "Could not close GIF file");
1150 mm_log((1, "Error in EGifCloseFile\n"));
1158 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1160 General high-level function to write a GIF to a file.
1162 Writes the GIF images to the specified file handle using the options
1163 in quant and opts. See L<image.h/i_quantize> and
1164 L<image.h/i_gif_opts>.
1166 Returns non-zero on success.
1172 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
1177 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
1178 quant, fd, imgs, count, opts));
1180 gif_set_version(quant, opts);
1182 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1184 i_push_error(0, "Cannot create GIF file object");
1185 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1189 return i_writegif_low(quant, gf, imgs, count, opts);
1192 #if IM_GIFMAJOR >= 4
1195 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1197 Internal. Wrapper for the user write callback function.
1202 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1204 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1206 return i_gen_writer(gwd, data, size) ? size : 0;
1212 =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)
1214 General high-level function to write a GIF using callbacks to send
1217 Returns non-zero on success.
1223 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1224 int maxlength, i_img **imgs, int count, i_gif_opts *opts)
1226 #if IM_GIFMAJOR >= 4
1228 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1229 /* giflib declares this incorrectly as EgifOpen */
1230 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
1235 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
1236 quant, cb, userdata, maxlength, imgs, count, opts));
1238 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1240 i_push_error(0, "Cannot create GIF file object");
1241 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1242 free_gen_write_data(gwd, 0);
1246 result = i_writegif_low(quant, gf, imgs, count, opts);
1247 return free_gen_write_data(gwd, result);
1254 =item gif_error_msg(int code)
1256 Grabs the most recent giflib error code from GifLastError() and
1257 returns a string that describes that error.
1259 The returned pointer points to a static buffer, either from a literal
1260 C string or a static buffer.
1264 static char const *gif_error_msg(int code) {
1265 static char msg[80];
1268 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1269 return "Failed to open given file";
1271 case E_GIF_ERR_WRITE_FAILED:
1272 return "Write failed";
1274 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1275 return "Screen descriptor already passed to giflib";
1277 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1278 return "Image descriptor already passed to giflib";
1280 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1281 return "Neither global nor local color map set";
1283 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1284 return "Too much pixel data passed to giflib";
1286 case E_GIF_ERR_NOT_ENOUGH_MEM:
1287 return "Out of memory";
1289 case E_GIF_ERR_DISK_IS_FULL:
1290 return "Disk is full";
1292 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1293 return "File close failed";
1295 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1296 return "File not writable";
1298 case D_GIF_ERR_OPEN_FAILED:
1299 return "Failed to open file";
1301 case D_GIF_ERR_READ_FAILED:
1302 return "Failed to read from file";
1304 case D_GIF_ERR_NOT_GIF_FILE:
1305 return "File is not a GIF file";
1307 case D_GIF_ERR_NO_SCRN_DSCR:
1308 return "No screen descriptor detected - invalid file";
1310 case D_GIF_ERR_NO_IMAG_DSCR:
1311 return "No image descriptor detected - invalid file";
1313 case D_GIF_ERR_NO_COLOR_MAP:
1314 return "No global or local color map found";
1316 case D_GIF_ERR_WRONG_RECORD:
1317 return "Wrong record type detected - invalid file?";
1319 case D_GIF_ERR_DATA_TOO_BIG:
1320 return "Data in file too big for image";
1322 case D_GIF_ERR_NOT_ENOUGH_MEM:
1323 return "Out of memory";
1325 case D_GIF_ERR_CLOSE_FAILED:
1326 return "Close failed";
1328 case D_GIF_ERR_NOT_READABLE:
1329 return "File not opened for read";
1331 case D_GIF_ERR_IMAGE_DEFECT:
1332 return "Defective image";
1334 case D_GIF_ERR_EOF_TOO_SOON:
1335 return "Unexpected EOF - invalid file";
1338 sprintf(msg, "Unknown giflib error code %d", code);
1344 =item gif_push_error()
1346 Utility function that takes the current GIF error code, converts it to
1347 an error message and pushes it on the error stack.
1352 static void gif_push_error() {
1353 int code = GifLastError(); /* clears saved error */
1355 i_push_error(code, gif_error_msg(code));
1361 The Netscape loop extension isn't implemented. Giflib's extension
1362 writing code doesn't seem to support writing named extensions in this
1365 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1366 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1369 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1370 supplied interface in giflib 4.1.0 causes a SEGV in
1371 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1375 Arnar M. Hrafnkelsson, addi@umich.edu