4 /* XXX: Reading still needs to support reading all those gif properties */
9 gif.c - read and write gif files for Imager
18 int max_colours; // number of bits per colour
19 int pixdev; // how much noise to add
20 i_color fixed[N]; // fixed palette entries
21 int fixedlen; // number of fixed colours
22 int success; // non-zero on success
23 char *data; // a GIF file in memory
24 int length; // how big data is
25 int reader(char *, char *, int, int);
26 int writer(char *, char *, int);
27 char *userdata; // user's data, whatever it is
31 img = i_readgif(fd, &colour_table, &colours);
32 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
33 success = i_writegifmc(img, fd, max_colours);
34 img = i_readgif_scalar(data, length, &colour_table, &colours);
35 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
36 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
37 success = i_writegif_callback(&quant, writer, userdata, maxlength,
42 This source file provides the C level interface to reading and writing
45 This has been tested with giflib 3 and 4, though you lose the callback
46 functionality with giflib3.
55 static char const *gif_error_msg(int code);
56 static void gif_push_error(void);
60 static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
65 Internal. A structure passed to the reader function used for reading
68 Used with giflib 4 and later.
73 struct gif_scalar_info {
80 =item my_gif_inputfunc(GifFileType *gft, GifByteType *buf, int length)
82 Internal. The reader callback passed to giflib.
84 Used with giflib 4 and later.
90 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
91 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
92 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
94 if (gsi->cpos == gsi->length) return 0;
95 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
96 memcpy(buf,gsi->data+gsi->cpos,length);
105 /* Make some variables global, so we could access them faster: */
108 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
109 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
115 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
116 GifColorType *mapentry;
118 int colourmapsize = colourmap->ColorCount;
120 if(colours) *colours = colourmapsize;
121 if(!colour_table) return;
123 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
124 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
126 for(q=0; q<colourmapsize; q++) {
127 mapentry = &colourmap->Colors[q];
128 (*colour_table)[q*3 + 0] = mapentry->Red;
129 (*colour_table)[q*3 + 1] = mapentry->Green;
130 (*colour_table)[q*3 + 2] = mapentry->Blue;
136 =item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
138 Internal. Low-level function for reading a GIF file. The caller must
139 create the appropriate GifFileType object and pass it in.
145 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
147 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
148 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
149 ColorMapObject *ColorMap;
151 GifRecordType RecordType;
152 GifByteType *Extension;
155 static GifColorType *ColorMapEntry;
158 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
160 /* it's possible that the caller has called us with *colour_table being
161 non-NULL, but we check that to see if we need to free an allocated
162 colour table on error.
164 if (colour_table) *colour_table = NULL;
166 BackGround = GifFile->SBackGroundColor;
167 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
170 ColorMapSize = ColorMap->ColorCount;
171 i_colortable_copy(colour_table, colours, ColorMap);
176 im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
178 Size = GifFile->SWidth * sizeof(GifPixelType);
180 GifRow = (GifRowType) mymalloc(Size);
182 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
184 /* Scan the content of the GIF file and load the image(s) in: */
186 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
188 i_push_error(0, "Unable to get record type");
189 if (colour_table && *colour_table) {
190 myfree(*colour_table);
191 *colour_table = NULL;
194 DGifCloseFile(GifFile);
198 switch (RecordType) {
199 case IMAGE_DESC_RECORD_TYPE:
200 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
202 i_push_error(0, "Unable to get image descriptor");
203 if (colour_table && *colour_table) {
204 myfree(*colour_table);
205 *colour_table = NULL;
208 DGifCloseFile(GifFile);
212 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
213 mm_log((1, "Adding local colormap\n"));
214 ColorMapSize = ColorMap->ColorCount;
216 i_colortable_copy(colour_table, colours, ColorMap);
220 /* No colormap and we are about to read in the image - abandon for now */
221 mm_log((1, "Going in with no colormap\n"));
222 i_push_error(0, "Image does not have a local or a global color map");
223 /* we can't have allocated a colour table here */
225 DGifCloseFile(GifFile);
229 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
230 Col = GifFile->Image.Left;
231 Width = GifFile->Image.Width;
232 Height = GifFile->Image.Height;
234 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
236 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
237 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
238 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
239 if (colour_table && *colour_table) {
240 myfree(*colour_table);
241 *colour_table = NULL;
244 DGifCloseFile(GifFile);
247 if (GifFile->Image.Interlace) {
249 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
251 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
253 i_push_error(0, "Reading GIF line");
254 if (colour_table && *colour_table) {
255 myfree(*colour_table);
256 *colour_table = NULL;
259 DGifCloseFile(GifFile);
263 for (x = 0; x < Width; x++) {
264 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
265 col.rgb.r = ColorMapEntry->Red;
266 col.rgb.g = ColorMapEntry->Green;
267 col.rgb.b = ColorMapEntry->Blue;
268 i_ppix(im,Col+x,j,&col);
274 for (i = 0; i < Height; i++) {
275 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
277 i_push_error(0, "Reading GIF line");
278 if (colour_table && *colour_table) {
279 myfree(*colour_table);
280 *colour_table = NULL;
283 DGifCloseFile(GifFile);
287 for (x = 0; x < Width; x++) {
288 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
289 col.rgb.r = ColorMapEntry->Red;
290 col.rgb.g = ColorMapEntry->Green;
291 col.rgb.b = ColorMapEntry->Blue;
292 i_ppix(im, Col+x, Row, &col);
298 case EXTENSION_RECORD_TYPE:
299 /* Skip any extension blocks in file: */
300 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
302 i_push_error(0, "Reading extension record");
303 if (colour_table && *colour_table) {
304 myfree(*colour_table);
305 *colour_table = NULL;
308 DGifCloseFile(GifFile);
311 while (Extension != NULL) {
312 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
314 i_push_error(0, "reading next block of extension");
315 if (colour_table && *colour_table) {
316 myfree(*colour_table);
317 *colour_table = NULL;
320 DGifCloseFile(GifFile);
325 case TERMINATE_RECORD_TYPE:
327 default: /* Should be traps by DGifGetRecordType. */
330 } while (RecordType != TERMINATE_RECORD_TYPE);
334 if (DGifCloseFile(GifFile) == GIF_ERROR) {
336 i_push_error(0, "Closing GIF file object");
337 if (colour_table && *colour_table) {
338 myfree(*colour_table);
339 *colour_table = NULL;
348 =item i_readgif(int fd, int **colour_table, int *colours)
350 Reads in a GIF file from a file handle and converts it to an Imager
353 Returns the palette for the object in colour_table for colours
356 Returns NULL on failure.
362 i_readgif(int fd, int **colour_table, int *colours) {
363 GifFileType *GifFile;
367 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
369 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
371 i_push_error(0, "Cannot create giflib file object");
372 mm_log((1,"i_readgif: Unable to open file\n"));
376 return i_readgif_low(GifFile, colour_table, colours);
381 Internal function called by i_readgif_multi_low() in error handling
384 static void free_images(i_img **imgs, int count) {
386 for (i = 0; i < count; ++i)
387 i_img_destroy(imgs[i]);
392 =item i_readgif_multi_low(GifFileType *gf, int *count)
394 Reads one of more gif images from the given GIF file.
396 Returns a pointer to an array of i_img *, and puts the count into
399 Unlike the normal i_readgif*() functions the images are paletted
400 images rather than a combined RGB image.
402 This functions sets tags on the images returned:
408 the offset of the image from the left of the "screen" ("Image Left
413 the offset of the image from the top of the "screen" ("Image Top Position")
417 non-zero if the image was interlaced ("Interlace Flag")
419 =item gif_screen_width
421 =item gif_screen_height
423 the size of the logical screen ("Logical Screen Width",
424 "Logical Screen Height")
428 Non-zero if this image had a local color map.
432 The index in the global colormap of the logical screen's background
433 color. This is only set if the current image uses the global
436 =item gif_trans_index
438 The index of the color in the colormap used for transparency. If the
439 image has a transparency then it is returned as a 4 channel image with
440 the alpha set to zero in this palette entry. ("Transparent Color Index")
444 The delay until the next frame is displayed, in 1/100 of a second.
449 whether or not a user input is expected before continuing (view dependent)
454 how the next frame is displayed ("Disposal Method")
458 the number of loops from the Netscape Loop extension. This may be zero.
462 the first block of the first gif comment before each image.
466 Where applicable, the ("name") is the name of that field from the GIF89
472 i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
474 int i, j, Size, Width, Height, ExtCode, Count, x;
475 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
476 ColorMapObject *ColorMap;
478 GifRecordType RecordType;
479 GifByteType *Extension;
483 int trans_index; /* transparent index if we see a GCE */
484 int gif_delay; /* delay from a GCE */
485 int user_input; /* user input flag from a GCE */
486 int disposal; /* disposal method from a GCE */
489 char *comment = NULL; /* a comment */
490 i_img **results = NULL;
491 int result_alloc = 0;
496 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
498 BackGround = GifFile->SBackGroundColor;
500 Size = GifFile->SWidth * sizeof(GifPixelType);
502 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
503 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
505 /* Scan the content of the GIF file and load the image(s) in: */
507 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
509 i_push_error(0, "Unable to get record type");
510 free_images(results, *count);
511 DGifCloseFile(GifFile);
515 switch (RecordType) {
516 case IMAGE_DESC_RECORD_TYPE:
517 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
519 i_push_error(0, "Unable to get image descriptor");
520 free_images(results, *count);
521 DGifCloseFile(GifFile);
525 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
526 mm_log((1, "Adding local colormap\n"));
527 ColorMapSize = ColorMap->ColorCount;
529 /* No colormap and we are about to read in the image -
531 mm_log((1, "Going in with no colormap\n"));
532 i_push_error(0, "Image does not have a local or a global color map");
533 free_images(results, *count);
534 DGifCloseFile(GifFile);
538 Width = GifFile->Image.Width;
539 Height = GifFile->Image.Height;
541 if (got_gce && trans_index >= 0)
543 img = i_img_pal_new(Width, Height, channels, 256);
544 /* populate the palette of the new image */
545 for (i = 0; i < ColorMapSize; ++i) {
547 col.rgba.r = ColorMap->Colors[i].Red;
548 col.rgba.g = ColorMap->Colors[i].Green;
549 col.rgba.b = ColorMap->Colors[i].Blue;
550 if (channels == 4 && trans_index == i)
555 i_addcolors(img, &col, 1);
558 if (*count > result_alloc) {
559 if (result_alloc == 0) {
561 results = mymalloc(result_alloc * sizeof(i_img *));
566 newresults = myrealloc(results, result_alloc * sizeof(i_img *));
569 results[*count-1] = img;
570 i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
572 i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
573 i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
574 i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
575 i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
576 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
577 i_tags_addn(&img->tags, "gif_background", 0,
578 GifFile->SBackGroundColor);
580 if (GifFile->Image.ColorMap) {
581 i_tags_addn(&img->tags, "gif_localmap", 0, 1);
585 if (trans_index >= 0)
586 i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
587 i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
588 i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
589 i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
593 i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
595 i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
601 mm_log((1,"i_readgif_multi: Image %d at (%d, %d) [%dx%d]: \n",
602 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
604 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
605 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
606 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
607 free_images(results, *count);
608 DGifCloseFile(GifFile);
611 if (GifFile->Image.Interlace) {
612 for (Count = i = 0; i < 4; i++) {
613 for (j = InterlacedOffset[i]; j < Height;
614 j += InterlacedJumps[i]) {
616 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
618 i_push_error(0, "Reading GIF line");
619 free_images(results, *count);
620 DGifCloseFile(GifFile);
624 i_ppal(img, 0, Width, j, GifRow);
629 for (i = 0; i < Height; i++) {
630 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
632 i_push_error(0, "Reading GIF line");
633 free_images(results, *count);
634 DGifCloseFile(GifFile);
638 i_ppal(img, 0, Width, i, GifRow);
642 case EXTENSION_RECORD_TYPE:
643 /* Skip any extension blocks in file: */
644 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
646 i_push_error(0, "Reading extension record");
647 free_images(results, *count);
648 DGifCloseFile(GifFile);
651 if (ExtCode == 0xF9) {
653 if (Extension[1] & 1)
654 trans_index = Extension[4];
657 gif_delay = Extension[2] + 256 * Extension[3];
658 user_input = (Extension[0] & 2) != 0;
659 disposal = (Extension[0] >> 2) & 3;
661 if (ExtCode == 0xFF && *Extension == 11) {
662 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
663 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
665 i_push_error(0, "reading loop extension");
666 free_images(results, *count);
667 DGifCloseFile(GifFile);
670 if (Extension && *Extension == 3) {
672 ns_loop = Extension[2] + 256 * Extension[3];
676 else if (ExtCode == 0xFE) {
677 /* while it's possible for a GIF file to contain more than one
678 comment, I'm only implementing a single comment per image,
679 with the comment saved into the following image.
680 If someone wants more than that they can implement it.
681 I also don't handle comments that take more than one block.
684 comment = mymalloc(*Extension+1);
685 memcpy(comment, Extension+1, *Extension);
686 comment[*Extension] = '\0';
689 while (Extension != NULL) {
690 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
692 i_push_error(0, "reading next block of extension");
693 free_images(results, *count);
694 DGifCloseFile(GifFile);
699 case TERMINATE_RECORD_TYPE:
701 default: /* Should be trapped by DGifGetRecordType. */
704 } while (RecordType != TERMINATE_RECORD_TYPE);
708 i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
716 if (DGifCloseFile(GifFile) == GIF_ERROR) {
718 i_push_error(0, "Closing GIF file object");
719 free_images(results, *count);
727 =item i_readgif_multi(int fd, int *count)
732 i_readgif_multi(int fd, int *count) {
733 GifFileType *GifFile;
737 mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
739 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
741 i_push_error(0, "Cannot create giflib file object");
742 mm_log((1,"i_readgif: Unable to open file\n"));
746 return i_readgif_multi_low(GifFile, count);
750 =item i_readgif_multi_scalar(char *data, int length, int *count)
755 i_readgif_multi_scalar(char *data, int length, int *count) {
757 GifFileType *GifFile;
758 struct gif_scalar_info gsi;
766 mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
767 data, length, count));
769 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
771 i_push_error(0, "Cannot create giflib callback object");
772 mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
776 return i_readgif_multi_low(GifFile, count);
783 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
785 Read a GIF file into an Imager RGB file, the data of the GIF file is
786 retreived by callin the user supplied callback function.
788 This function is only used with giflib 4 and higher.
794 i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
796 GifFileType *GifFile;
799 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
803 mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
804 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
806 i_push_error(0, "Cannot create giflib callback object");
807 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
812 result = i_readgif_multi_low(GifFile, count);
813 free_gen_read_data(gci);
822 =item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
824 Write I<img> to the file handle I<fd>. The resulting GIF will use a
825 maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
826 colours taken from I<fixed>.
828 Returns non-zero on success.
834 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
839 memset(&quant, 0, sizeof(quant));
840 memset(&opts, 0, sizeof(opts));
841 quant.make_colors = mc_addi;
842 quant.mc_colors = colors;
843 quant.mc_size = 1<<max_colors;
844 quant.mc_count = fixedlen;
845 memcpy(colors, fixed, fixedlen * sizeof(i_color));
846 quant.translate = pt_perturb;
847 quant.perturb = pixdev;
848 return i_writegif_gen(&quant, fd, &im, 1, &opts);
852 =item i_writegifmc(i_img *im, int fd, int max_colors)
854 Write I<img> to the file handle I<fd>. The resulting GIF will use a
855 maximum of 1<<I<max_colours> colours.
857 Returns non-zero on success.
863 i_writegifmc(i_img *im, int fd, int max_colors) {
868 memset(&quant, 0, sizeof(quant));
869 memset(&opts, 0, sizeof(opts));
870 quant.make_colors = mc_none; /* ignored for pt_giflib */
871 quant.mc_colors = colors;
872 quant.mc_size = 1 << max_colors;
874 quant.translate = pt_giflib;
875 return i_writegif_gen(&quant, fd, &im, 1, &opts);
880 =item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
882 Reads a GIF file from an in memory copy of the file. This can be used
883 if you get the 'file' from some source other than an actual file (or
884 some other file handle).
886 This function is only available with giflib 4 and higher.
891 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
893 GifFileType *GifFile;
894 struct gif_scalar_info gsi;
902 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
903 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
905 i_push_error(0, "Cannot create giflib callback object");
906 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
910 return i_readgif_low(GifFile, colour_table, colours);
919 =item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
921 Internal. The reader callback wrapper passed to giflib.
923 This function is only used with giflib 4 and higher.
929 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
930 return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
937 =item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
939 Read a GIF file into an Imager RGB file, the data of the GIF file is
940 retreived by callin the user supplied callback function.
942 This function is only used with giflib 4 and higher.
948 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
950 GifFileType *GifFile;
953 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
957 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
958 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
960 i_push_error(0, "Cannot create giflib callback object");
961 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
966 result = i_readgif_low(GifFile, colour_table, colours);
967 free_gen_read_data(gci);
976 =item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
978 Internal. Low level image write function. Writes in interlace if
979 that was requested in the GIF options.
981 Returns non-zero on success.
986 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
987 if (opts->interlace) {
989 for (i = 0; i < 4; ++i) {
990 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
991 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
993 i_push_error(0, "Could not save image data:");
994 mm_log((1, "Error in EGifPutLine\n"));
1003 for (y = 0; y < img->ysize; ++y) {
1004 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
1006 i_push_error(0, "Could not save image data:");
1007 mm_log((1, "Error in EGifPutLine\n"));
1019 =item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1021 Internal. Writes the GIF graphics control extension, if necessary.
1023 Returns non-zero on success.
1027 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
1029 unsigned char gce[4] = {0};
1033 gce[3] = trans_index;
1036 if (index < opts->delay_count) {
1037 gce[1] = opts->delays[index] % 256;
1038 gce[2] = opts->delays[index] / 256;
1041 if (index < opts->user_input_count) {
1042 if (opts->user_input_flags[index])
1046 if (index < opts->disposal_count) {
1047 gce[0] |= (opts->disposal[index] & 3) << 2;
1051 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1053 i_push_error(0, "Could not save GCE");
1060 =item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1062 Internal. Add the Netscape2.0 loop extension block, if requested.
1064 The code for this function is currently "#if 0"ed out since the giflib
1065 extension writing code currently doesn't seem to support writing
1066 application extension blocks.
1070 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1072 /* EGifPutExtension() doesn't appear to handle application
1073 extension blocks in any way
1074 Since giflib wraps the fd with a FILE * (and puts that in its
1075 private data), we can't do an end-run and write the data
1077 There's no open interface that takes a FILE * either, so we
1078 can't workaround it that way either.
1079 If giflib's callback interface wasn't broken by default, I'd
1080 force file writes to use callbacks, but it is broken by default.
1083 /* yes this was another attempt at supporting the loop extension */
1084 if (opts->loop_count) {
1085 unsigned char nsle[12] = "NETSCAPE2.0";
1086 unsigned char subblock[3];
1087 if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
1089 i_push_error(0, "writing loop extension");
1093 subblock[1] = opts->loop_count % 256;
1094 subblock[2] = opts->loop_count / 256;
1095 if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
1097 i_push_error(0, "writing loop extention sub-block");
1100 if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
1102 i_push_error(0, "writing loop extension terminator");
1111 =item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
1113 Create a giflib color map object from an Imager color map.
1118 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
1120 GifColorType colors[256];
1122 int size = quant->mc_count;
1124 ColorMapObject *map;
1126 for (i = 0; i < quant->mc_count; ++i) {
1127 colors[i].Red = quant->mc_colors[i].rgb.r;
1128 colors[i].Green = quant->mc_colors[i].rgb.g;
1129 colors[i].Blue = quant->mc_colors[i].rgb.b;
1132 colors[size].Red = opts->tran_color.rgb.r;
1133 colors[size].Green = opts->tran_color.rgb.g;
1134 colors[size].Blue = opts->tran_color.rgb.b;
1138 while (map_size < size)
1140 /* giflib spews for 1 colour maps, reasonable, I suppose */
1144 map = MakeMapObject(map_size, colors);
1145 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
1148 i_push_error(0, "Could not create color map object");
1155 =item gif_set_version(i_quantize *quant, i_gif_opts *opts)
1157 We need to call EGifSetGifVersion() before opening the file - put that
1160 Unfortunately giflib 4.1.0 crashes when we use this. Internally
1161 giflib 4.1.0 has code:
1163 static char *GifVersionPrefix = GIF87_STAMP;
1165 and the code that sets the version internally does:
1167 strncpy(&GifVersionPrefix[3], Version, 3);
1169 which is very broken.
1171 Failing to set the correct GIF version doesn't seem to cause a problem
1177 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
1178 /* the following crashed giflib
1179 the EGifSetGifVersion() is seriously borked in giflib
1180 it's less borked in the ungiflib beta, but we don't have a mechanism
1182 if (opts->delay_count
1183 || opts->user_input_count
1184 || opts->disposal_count
1186 || quant->transp != tr_none)
1187 EGifSetGifVersion("89a");
1189 EGifSetGifVersion("87a");
1194 in_palette(i_color *c, i_quantize *quant, int size) {
1197 for (i = 0; i < size; ++i) {
1198 if (c->channel[0] == quant->mc_colors[i].channel[0]
1199 && c->channel[1] == quant->mc_colors[i].channel[1]
1200 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1209 =item has_common_palette(imgs, count, quant, want_trans)
1211 Tests if all the given images are paletted and have a common palette,
1212 if they do it builds that palette.
1214 A possible improvement might be to eliminate unused colors in the
1219 has_common_palette(i_img **imgs, int count, i_quantize *quant, int want_trans,
1221 int size = quant->mc_count;
1227 /* we try to build a common palette here, if we can manage that, then
1228 that's the palette we use */
1229 for (imgn = 0; imgn < count; ++imgn) {
1230 if (imgs[imgn]->type != i_palette_type)
1233 if (opts->eliminate_unused) {
1234 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1236 memset(used, 0, sizeof(used));
1238 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1239 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1240 for (x = 0; x < imgs[imgn]->xsize; ++x)
1247 /* assume all are in use */
1248 memset(used, 1, sizeof(used));
1251 for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
1254 i_getcolors(imgs[imgn], i, &c, 1);
1256 if (in_palette(&c, quant, size) < 0) {
1257 if (size < quant->mc_size) {
1258 quant->mc_colors[size++] = c;
1261 /* oops, too many colors */
1269 quant->mc_count = size;
1275 quant_paletted(i_quantize *quant, i_img *img) {
1276 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1278 i_palidx trans[256];
1282 /* build a translation table */
1283 for (i = 0; i < i_colorcount(img); ++i) {
1285 i_getcolors(img, i, &c, 1);
1286 trans[i] = in_palette(&c, quant, quant->mc_count);
1289 for (y = 0; y < img->ysize; ++y) {
1290 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1291 for (x = 0; x < img->xsize; ++x) {
1301 =item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1303 Internal. Low-level function that does the high-level GIF processing
1306 Returns non-zero on success.
1312 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
1314 unsigned char *result;
1316 ColorMapObject *map;
1317 int scrw = 0, scrh = 0;
1318 int imgn, orig_count, orig_size;
1322 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
1323 quant, gf, imgs, count, opts));
1325 /**((char *)0) = 1;*/
1326 /* sanity is nice */
1327 if (quant->mc_size > 256)
1328 quant->mc_size = 256;
1329 if (quant->mc_count > quant->mc_size)
1330 quant->mc_count = quant->mc_size;
1332 for (imgn = 0; imgn < count; ++imgn) {
1333 if (imgn < opts->position_count) {
1334 if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
1335 scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
1336 if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
1337 scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
1340 if (imgs[imgn]->xsize > scrw)
1341 scrw = imgs[imgn]->xsize;
1342 if (imgs[imgn]->ysize > scrh)
1343 scrh = imgs[imgn]->ysize;
1348 i_push_error(0, "No images provided to write");
1349 return 0; /* what are you smoking? */
1352 orig_count = quant->mc_count;
1353 orig_size = quant->mc_size;
1355 if (opts->each_palette) {
1356 int want_trans = quant->transp != tr_none
1357 && imgs[0]->channels == 4;
1359 /* if the caller gives us too many colours we can't do transparency */
1360 if (want_trans && quant->mc_count == 256)
1362 /* if they want transparency but give us a big size, make it smaller
1363 to give room for a transparency colour */
1364 if (want_trans && quant->mc_size == 256)
1367 /* we always generate a global palette - this lets systems with a
1368 broken giflib work */
1369 if (has_common_palette(imgs, 1, quant, want_trans, opts)) {
1370 result = quant_paletted(quant, imgs[0]);
1373 quant_makemap(quant, imgs, 1);
1374 result = quant_translate(quant, imgs[0]);
1377 trans_index = quant->mc_count;
1378 quant_transparent(quant, result, imgs[0], trans_index);
1381 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1384 mm_log((1, "Error in MakeMapObject."));
1389 while (quant->mc_size > (1 << color_bits))
1392 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
1394 i_push_error(0, "Could not save screen descriptor");
1398 mm_log((1, "Error in EGifPutScreenDesc."));
1403 if (!do_ns_loop(gf, opts))
1406 if (!do_gce(gf, 0, opts, want_trans, trans_index)) {
1411 if (opts->position_count) {
1412 posx = opts->positions[0].x;
1413 posy = opts->positions[0].y;
1417 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1418 opts->interlace, NULL) == GIF_ERROR) {
1420 i_push_error(0, "Could not save image descriptor");
1422 mm_log((1, "Error in EGifPutImageDesc."));
1425 if (!do_write(gf, opts, imgs[0], result)) {
1430 for (imgn = 1; imgn < count; ++imgn) {
1431 quant->mc_count = orig_count;
1432 quant->mc_size = orig_size;
1433 want_trans = quant->transp != tr_none
1434 && imgs[0]->channels == 4;
1435 /* if the caller gives us too many colours we can't do transparency */
1436 if (want_trans && quant->mc_count == 256)
1438 /* if they want transparency but give us a big size, make it smaller
1439 to give room for a transparency colour */
1440 if (want_trans && quant->mc_size == 256)
1443 if (has_common_palette(imgs+imgn, 1, quant, want_trans, opts)) {
1444 result = quant_paletted(quant, imgs[imgn]);
1447 quant_makemap(quant, imgs+imgn, 1);
1448 result = quant_translate(quant, imgs[imgn]);
1451 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1452 trans_index = quant->mc_count;
1455 if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
1460 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1463 mm_log((1, "Error in MakeMapObject."));
1466 if (imgn < opts->position_count) {
1467 posx = opts->positions[imgn].x;
1468 posy = opts->positions[imgn].y;
1472 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1473 imgs[imgn]->ysize, opts->interlace,
1474 map) == GIF_ERROR) {
1476 i_push_error(0, "Could not save image descriptor");
1480 mm_log((1, "Error in EGifPutImageDesc."));
1485 if (!do_write(gf, opts, imgs[imgn], result)) {
1495 int do_quant_paletted = 0;
1497 /* get a palette entry for the transparency iff we have an image
1498 with an alpha channel */
1500 for (imgn = 0; imgn < count; ++imgn) {
1501 if (imgs[imgn]->channels == 4) {
1506 want_trans = want_trans && quant->transp != tr_none
1507 && quant->mc_count < 256;
1508 if (want_trans && quant->mc_size == 256)
1511 /* handle the first image separately - since we allow giflib
1512 conversion and giflib doesn't give us a separate function to build
1515 /* produce a colour map */
1516 if (has_common_palette(imgs, count, quant, want_trans, opts)) {
1517 result = quant_paletted(quant, imgs[0]);
1518 ++do_quant_paletted;
1521 quant_makemap(quant, imgs, count);
1522 result = quant_translate(quant, imgs[0]);
1525 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
1528 mm_log((1, "Error in MakeMapObject"));
1532 while (quant->mc_count > (1 << color_bits))
1535 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
1537 i_push_error(0, "Could not save screen descriptor");
1541 mm_log((1, "Error in EGifPutScreenDesc."));
1546 if (!do_ns_loop(gf, opts))
1549 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
1554 if (opts->position_count) {
1555 posx = opts->positions[0].x;
1556 posy = opts->positions[0].y;
1560 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1561 opts->interlace, NULL) == GIF_ERROR) {
1563 i_push_error(0, "Could not save image descriptor");
1565 mm_log((1, "Error in EGifPutImageDesc."));
1568 if (want_trans && imgs[0]->channels == 4)
1569 quant_transparent(quant, result, imgs[0], quant->mc_count);
1571 if (!do_write(gf, opts, imgs[0], result)) {
1578 for (imgn = 1; imgn < count; ++imgn) {
1580 if (do_quant_paletted)
1581 result = quant_paletted(quant, imgs[imgn]);
1583 result = quant_translate(quant, imgs[imgn]);
1584 local_trans = want_trans && imgs[imgn]->channels == 4;
1586 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
1587 if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
1592 if (imgn < opts->position_count) {
1593 posx = opts->positions[imgn].x;
1594 posy = opts->positions[imgn].y;
1598 if (EGifPutImageDesc(gf, posx, posy,
1599 imgs[imgn]->xsize, imgs[imgn]->ysize,
1600 opts->interlace, NULL) == GIF_ERROR) {
1602 i_push_error(0, "Could not save image descriptor");
1605 mm_log((1, "Error in EGifPutImageDesc."));
1608 if (!do_write(gf, opts, imgs[imgn], result)) {
1616 if (EGifCloseFile(gf) == GIF_ERROR) {
1618 i_push_error(0, "Could not close GIF file");
1619 mm_log((1, "Error in EGifCloseFile\n"));
1627 =item i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts)
1629 General high-level function to write a GIF to a file.
1631 Writes the GIF images to the specified file handle using the options
1632 in quant and opts. See L<image.h/i_quantize> and
1633 L<image.h/i_gif_opts>.
1635 Returns non-zero on success.
1641 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
1646 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
1647 quant, fd, imgs, count, opts));
1649 gif_set_version(quant, opts);
1651 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
1653 i_push_error(0, "Cannot create GIF file object");
1654 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1658 return i_writegif_low(quant, gf, imgs, count, opts);
1661 #if IM_GIFMAJOR >= 4
1664 =item gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1666 Internal. Wrapper for the user write callback function.
1671 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
1673 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
1675 return i_gen_writer(gwd, (char*)data, size) ? size : 0;
1681 =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)
1683 General high-level function to write a GIF using callbacks to send
1686 Returns non-zero on success.
1692 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
1693 int maxlength, i_img **imgs, int count, i_gif_opts *opts)
1695 #if IM_GIFMAJOR >= 4
1697 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
1698 /* giflib declares this incorrectly as EgifOpen */
1699 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
1704 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
1705 quant, cb, userdata, maxlength, imgs, count, opts));
1707 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
1709 i_push_error(0, "Cannot create GIF file object");
1710 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
1711 free_gen_write_data(gwd, 0);
1715 result = i_writegif_low(quant, gf, imgs, count, opts);
1716 return free_gen_write_data(gwd, result);
1723 =item gif_error_msg(int code)
1725 Grabs the most recent giflib error code from GifLastError() and
1726 returns a string that describes that error.
1728 The returned pointer points to a static buffer, either from a literal
1729 C string or a static buffer.
1733 static char const *gif_error_msg(int code) {
1734 static char msg[80];
1737 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1738 return "Failed to open given file";
1740 case E_GIF_ERR_WRITE_FAILED:
1741 return "Write failed";
1743 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1744 return "Screen descriptor already passed to giflib";
1746 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1747 return "Image descriptor already passed to giflib";
1749 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1750 return "Neither global nor local color map set";
1752 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1753 return "Too much pixel data passed to giflib";
1755 case E_GIF_ERR_NOT_ENOUGH_MEM:
1756 return "Out of memory";
1758 case E_GIF_ERR_DISK_IS_FULL:
1759 return "Disk is full";
1761 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1762 return "File close failed";
1764 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1765 return "File not writable";
1767 case D_GIF_ERR_OPEN_FAILED:
1768 return "Failed to open file";
1770 case D_GIF_ERR_READ_FAILED:
1771 return "Failed to read from file";
1773 case D_GIF_ERR_NOT_GIF_FILE:
1774 return "File is not a GIF file";
1776 case D_GIF_ERR_NO_SCRN_DSCR:
1777 return "No screen descriptor detected - invalid file";
1779 case D_GIF_ERR_NO_IMAG_DSCR:
1780 return "No image descriptor detected - invalid file";
1782 case D_GIF_ERR_NO_COLOR_MAP:
1783 return "No global or local color map found";
1785 case D_GIF_ERR_WRONG_RECORD:
1786 return "Wrong record type detected - invalid file?";
1788 case D_GIF_ERR_DATA_TOO_BIG:
1789 return "Data in file too big for image";
1791 case D_GIF_ERR_NOT_ENOUGH_MEM:
1792 return "Out of memory";
1794 case D_GIF_ERR_CLOSE_FAILED:
1795 return "Close failed";
1797 case D_GIF_ERR_NOT_READABLE:
1798 return "File not opened for read";
1800 case D_GIF_ERR_IMAGE_DEFECT:
1801 return "Defective image";
1803 case D_GIF_ERR_EOF_TOO_SOON:
1804 return "Unexpected EOF - invalid file";
1807 sprintf(msg, "Unknown giflib error code %d", code);
1813 =item gif_push_error()
1815 Utility function that takes the current GIF error code, converts it to
1816 an error message and pushes it on the error stack.
1821 static void gif_push_error(void) {
1822 int code = GifLastError(); /* clears saved error */
1824 i_push_error(code, gif_error_msg(code));
1830 The Netscape loop extension isn't implemented. Giflib's extension
1831 writing code doesn't seem to support writing named extensions in this
1834 A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1835 problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1838 The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1839 supplied interface in giflib 4.1.0 causes a SEGV in
1840 EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1844 Arnar M. Hrafnkelsson, addi@umich.edu