-#include "image.h"
+#include "imageri.h"
#include <gif_lib.h>
+#ifdef _MSC_VER
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <errno.h>
+/* XXX: Reading still needs to support reading all those gif properties */
/*
=head1 NAME
*/
static char const *gif_error_msg(int code);
-static void gif_push_error();
+static void gif_push_error(void);
#if IM_GIFMAJOR >= 4
+static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
+
/*
=item gif_scalar_info
#endif
-/*
- This file needs a complete rewrite
-
- This file needs a complete rewrite
- Maybe not anymore, though reading still needs to support reading
- all those gif properties.
-*/
/* Make some variables global, so we could access them faster: */
InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
-/* static ColorMapObject *ColorMap; */
static
if(colours) *colours = colourmapsize;
if(!colour_table) return;
- *colour_table = mymalloc(sizeof(int *) * colourmapsize * 3);
- memset(*colour_table, 0, sizeof(int *) * colourmapsize * 3);
+ *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
+ memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
for(q=0; q<colourmapsize; q++) {
mapentry = &colourmap->Colors[q];
non-NULL, but we check that to see if we need to free an allocated
colour table on error.
*/
- if (colour_table)
- *colour_table = NULL;
+ if (colour_table) *colour_table = NULL;
BackGround = GifFile->SBackGroundColor;
ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
cmapcnt++;
}
+ if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
+ if (colour_table && *colour_table) {
+ myfree(*colour_table);
+ *colour_table = NULL;
+ }
+ DGifCloseFile(GifFile);
+ mm_log((1, "i_readgif: image size exceeds limits\n"));
+ return NULL;
+ }
- im = i_img_empty_ch(NULL,GifFile->SWidth,GifFile->SHeight,3);
+ im = i_img_empty_ch(NULL, GifFile->SWidth, GifFile->SHeight, 3);
+ if (!im) {
+ if (colour_table && *colour_table) {
+ myfree(*colour_table);
+ *colour_table = NULL;
+ }
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
Size = GifFile->SWidth * sizeof(GifPixelType);
- if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
- m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
+ GifRow = mymalloc(Size);
for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
gif_push_error();
i_push_error(0, "Unable to get record type");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
gif_push_error();
i_push_error(0, "Unable to get image descriptor");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
}
- if ( cmapcnt == 0) {
- if (ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) ) {
- mm_log((1, "Adding local colormap\n"));
- ColorMapSize = ColorMap->ColorCount;
+ if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
+ mm_log((1, "Adding local colormap\n"));
+ ColorMapSize = ColorMap->ColorCount;
+ if ( cmapcnt == 0) {
i_colortable_copy(colour_table, colours, ColorMap);
cmapcnt++;
- } else {
- /* No colormap and we are about to read in the image - abandon for now */
- mm_log((1, "Going in with no colormap\n"));
- i_push_error(0, "Image does not have a local or a global color map");
- /* we can't have allocated a colour table here */
- i_img_destroy(im);
- DGifCloseFile(GifFile);
- return NULL;
}
+ } else {
+ /* No colormap and we are about to read in the image - abandon for now */
+ mm_log((1, "Going in with no colormap\n"));
+ i_push_error(0, "Image does not have a local or a global color map");
+ /* we can't have allocated a colour table here */
+ myfree(GifRow);
+ i_img_destroy(im);
+ DGifCloseFile(GifFile);
+ return NULL;
}
+
Row = GifFile->Image.Top; /* Image Position relative to Screen. */
Col = GifFile->Image.Left;
Width = GifFile->Image.Width;
Height = GifFile->Image.Height;
ImageNum++;
- mm_log((1,"i_readgif: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
+ mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
- return(0);
+ return NULL;
}
if (GifFile->Image.Interlace) {
for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
Count++;
- if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
gif_push_error();
i_push_error(0, "Reading GIF line");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
}
- for (x = 0; x < GifFile->SWidth; x++) {
+ for (x = 0; x < Width; x++) {
ColorMapEntry = &ColorMap->Colors[GifRow[x]];
col.rgb.r = ColorMapEntry->Red;
col.rgb.g = ColorMapEntry->Green;
col.rgb.b = ColorMapEntry->Blue;
- i_ppix(im,x,j,&col);
+ i_ppix(im,Col+x,j,&col);
}
}
}
else {
for (i = 0; i < Height; i++) {
- if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
gif_push_error();
i_push_error(0, "Reading GIF line");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
}
- for (x = 0; x < GifFile->SWidth; x++) {
+ for (x = 0; x < Width; x++) {
ColorMapEntry = &ColorMap->Colors[GifRow[x]];
col.rgb.r = ColorMapEntry->Red;
col.rgb.g = ColorMapEntry->Green;
col.rgb.b = ColorMapEntry->Blue;
- i_ppix(im, x, Row, &col);
+ i_ppix(im, Col+x, Row, &col);
}
Row++;
}
gif_push_error();
i_push_error(0, "Reading extension record");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
gif_push_error();
i_push_error(0, "reading next block of extension");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
+ myfree(GifRow);
i_img_destroy(im);
DGifCloseFile(GifFile);
return NULL;
gif_push_error();
i_push_error(0, "Closing GIF file object");
if (colour_table && *colour_table) {
- free(*colour_table);
+ myfree(*colour_table);
*colour_table = NULL;
}
i_img_destroy(im);
return NULL;
}
+
+ i_tags_add(&im->tags, "i_format", 0, "gif", -1, 0);
+
return im;
}
}
/*
-=item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
-
-Write I<img> to the file handle I<fd>. The resulting GIF will use a
-maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
-colours taken from I<fixed>.
-Returns non-zero on success.
+Internal function called by i_readgif_multi_low() in error handling
-=cut
*/
-
-undef_int
-i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
-{
- i_color colors[256];
- i_quantize quant;
- i_gif_opts opts;
+static void free_images(i_img **imgs, int count) {
+ int i;
- memset(&quant, 0, sizeof(quant));
- memset(&opts, 0, sizeof(opts));
- quant.make_colors = mc_addi;
- quant.mc_colors = colors;
- quant.mc_size = 1<<max_colors;
- quant.mc_count = fixedlen;
- memcpy(colors, fixed, fixedlen * sizeof(i_color));
- quant.translate = pt_perturb;
- quant.perturb = pixdev;
- return i_writegif_gen(&quant, fd, &im, 1, &opts);
+ if (count) {
+ for (i = 0; i < count; ++i)
+ i_img_destroy(imgs[i]);
+ myfree(imgs);
+ }
}
/*
-=item i_writegifmc(i_img *im, int fd, int max_colors)
-
-Write I<img> to the file handle I<fd>. The resulting GIF will use a
-maximum of 1<<I<max_colours> colours.
-
-Returns non-zero on success.
-
-=cut
-*/
+=item i_readgif_multi_low(GifFileType *gf, int *count, int page)
-undef_int
-i_writegifmc(i_img *im, int fd, int max_colors) {
- i_color colors[256];
- i_quantize quant;
- i_gif_opts opts;
-
- memset(&quant, 0, sizeof(quant));
- memset(&opts, 0, sizeof(opts));
- quant.make_colors = mc_none; /* ignored for pt_giflib */
- quant.mc_colors = colors;
- quant.mc_size = 1 << max_colors;
- quant.mc_count = 0;
- quant.translate = pt_giflib;
- return i_writegif_gen(&quant, fd, &im, 1, &opts);
-}
-
-#if 1
-
-undef_int
-i_writegifex(i_img *im, int fd) {
- return 0;
-}
-
-#else
+Reads one of more gif images from the given GIF file.
-/* I don't think this works
- note that RedBuffer is never allocated - TC
-*/
-
-undef_int
-i_writegifex(i_img *im,int fd) {
- int colors, xsize, ysize, channels;
- int x,y,ColorMapSize;
- unsigned long Size;
+Returns a pointer to an array of i_img *, and puts the count into
+*count.
- struct octt *ct;
+If page is not -1 then the given image _only_ is returned from the
+file, where the first image is 0, the second 1 and so on.
- GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,*OutputBuffer = NULL;
- ColorMapObject *OutputColorMap = NULL;
- GifFileType *GifFile;
- GifByteType *Ptr;
-
- i_color val;
+Unlike the normal i_readgif*() functions the images are paletted
+images rather than a combined RGB image.
- mm_log((1,"i_writegif(0x%x,fd %d)\n",im,fd));
-
- if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }
+This functions sets tags on the images returned:
- xsize=im->xsize;
- ysize=im->ysize;
- channels=im->channels;
+=over
- colors=0;
- ct=octt_new();
+=item gif_left
- colors=0;
- for(x=0;x<xsize;x++) for(y=0;y<ysize;y++) {
- i_gpix(im,x,y,&val);
- colors+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
- /* if (colors > maxc) { octt_delete(ct); }
- We'll just bite the bullet */
- }
+the offset of the image from the left of the "screen" ("Image Left
+Position")
- ColorMapSize = (colors > 256) ? 256 : colors;
-
- Size = ((long) im->xsize) * im->ysize * sizeof(GifByteType);
-
- if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
- m_fatal(0,"Failed to allocate memory for Output colormap.");
- if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
- m_fatal(0,"Failed to allocate memory for output buffer.");
-
- if (QuantizeBuffer(im->xsize, im->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
- OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
- mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
- return(0);
- }
+=item gif_top
+the offset of the image from the top of the "screen" ("Image Top Position")
- myfree(RedBuffer);
- if (im->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
-
- if ((GifFile = EGifOpenFileHandle(fd)) == NULL) {
- mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
- return(0);
- }
-
- if (EGifPutScreenDesc(GifFile,im->xsize, im->ysize, colors, 0,OutputColorMap) == GIF_ERROR ||
- EGifPutImageDesc(GifFile,0, 0, im->xsize, im->ysize, FALSE, NULL) == GIF_ERROR) {
- mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
- if (GifFile != NULL) EGifCloseFile(GifFile);
- return(0);
- }
+=item gif_interlace
- Ptr = OutputBuffer;
+non-zero if the image was interlaced ("Interlace Flag")
- for (y = 0; y < im->ysize; y++) {
- if (EGifPutLine(GifFile, Ptr, im->xsize) == GIF_ERROR) {
- mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
- if (GifFile != NULL) EGifCloseFile(GifFile);
- return(0);
- }
-
- Ptr += im->xsize;
- }
-
- if (EGifCloseFile(GifFile) == GIF_ERROR) {
- mm_log((1,"Error in EGifCloseFile, unable to write image.\n"));
- return(0);
- }
- return(1);
-}
+=item gif_screen_width
-#endif
+=item gif_screen_height
-/*
-=item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
+the size of the logical screen ("Logical Screen Width",
+"Logical Screen Height")
-Reads a GIF file from an in memory copy of the file. This can be used
-if you get the 'file' from some source other than an actual file (or
-some other file handle).
+=item gif_local_map
-This function is only available with giflib 4 and higher.
+Non-zero if this image had a local color map.
-=cut
-*/
-i_img*
-i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
-#if IM_GIFMAJOR >= 4
- GifFileType *GifFile;
- struct gif_scalar_info gsi;
+=item gif_background
- i_clear_error();
+The index in the global colormap of the logical screen's background
+color. This is only set if the current image uses the global
+colormap.
- gsi.cpos=0;
- gsi.length=length;
- gsi.data=data;
+=item gif_trans_index
- mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
- if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
- gif_push_error();
- i_push_error(0, "Cannot create giflib callback object");
- mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
- return NULL;
- }
+The index of the color in the colormap used for transparency. If the
+image has a transparency then it is returned as a 4 channel image with
+the alpha set to zero in this palette entry. ("Transparent Color Index")
- return i_readgif_low(GifFile, colour_table, colours);
-#else
- return NULL;
-#endif
-}
+=item gif_delay
-#if IM_GIFMAJOR >= 4
+The delay until the next frame is displayed, in 1/100 of a second.
+("Delay Time").
-/*
-=item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
+=item gif_user_input
-Internal. The reader callback wrapper passed to giflib.
+whether or not a user input is expected before continuing (view dependent)
+("User Input Flag").
-This function is only used with giflib 4 and higher.
+=item gif_disposal
-=cut
-*/
+how the next frame is displayed ("Disposal Method")
-static int
-gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
- return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
-}
+=item gif_loop
-#endif
+the number of loops from the Netscape Loop extension. This may be zero.
+=item gif_comment
-/*
-=item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
+the first block of the first gif comment before each image.
-Read a GIF file into an Imager RGB file, the data of the GIF file is
-retreived by callin the user supplied callback function.
+=back
-This function is only used with giflib 4 and higher.
+Where applicable, the ("name") is the name of that field from the GIF89
+standard.
=cut
*/
-i_img*
-i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
-#if IM_GIFMAJOR >= 4
- GifFileType *GifFile;
- i_img *result;
-
- i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
-
- i_clear_error();
+i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
+ i_img *img;
+ int i, j, Size, Width, Height, ExtCode, Count;
+ int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
+ ColorMapObject *ColorMap;
+
+ GifRecordType RecordType;
+ GifByteType *Extension;
- mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
- if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
- gif_push_error();
- i_push_error(0, "Cannot create giflib callback object");
- mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
- myfree(gci);
- return NULL;
- }
-
- result = i_readgif_low(GifFile, colour_table, colours);
- free_gen_read_data(gci);
-
- return result;
-#else
- return NULL;
-#endif
-}
+ GifRowType GifRow;
+ int got_gce = 0;
+ int trans_index = 0; /* transparent index if we see a GCE */
+ int gif_delay = 0; /* delay from a GCE */
+ int user_input = 0; /* user input flag from a GCE */
+ int disposal = 0; /* disposal method from a GCE */
+ int got_ns_loop = 0;
+ int ns_loop = 0;
+ char *comment = NULL; /* a comment */
+ i_img **results = NULL;
+ int result_alloc = 0;
+ int channels;
+
+ *count = 0;
-/*
-=item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
+ mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
-Internal. Low level image write function. Writes in interlace if
-that was requested in the GIF options.
+ BackGround = GifFile->SBackGroundColor;
-Returns non-zero on success.
+ Size = GifFile->SWidth * sizeof(GifPixelType);
+
+ if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
+ i_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
-=cut
-*/
-static undef_int
-do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
- if (opts->interlace) {
- int i, j;
- for (i = 0; i < 4; ++i) {
- for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
- if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image data:");
- mm_log((1, "Error in EGifPutLine\n"));
- EGifCloseFile(gf);
- return 0;
- }
- }
+ /* Scan the content of the GIF file and load the image(s) in: */
+ do {
+ if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Unable to get record type");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
}
- }
- else {
- int y;
- for (y = 0; y < img->ysize; ++y) {
- if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
+
+ switch (RecordType) {
+ case IMAGE_DESC_RECORD_TYPE:
+ if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
gif_push_error();
- i_push_error(0, "Could not save image data:");
- mm_log((1, "Error in EGifPutLine\n"));
- EGifCloseFile(gf);
- return 0;
+ i_push_error(0, "Unable to get image descriptor");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
}
- data += img->xsize;
- }
- }
-
- return 1;
-}
-
-/*
-=item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
-
-Internal. Writes the GIF graphics control extension, if necessary.
-Returns non-zero on success.
-
-=cut
-*/
-static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
-{
- unsigned char gce[4] = {0};
- int want_gce = 0;
- if (want_trans) {
- gce[0] |= 1;
- gce[3] = trans_index;
- ++want_gce;
- }
- if (index < opts->delay_count) {
- gce[1] = opts->delays[index] % 256;
- gce[2] = opts->delays[index] / 256;
- ++want_gce;
- }
- if (index < opts->user_input_count) {
- if (opts->user_input_flags[index])
- gce[0] |= 2;
- ++want_gce;
- }
- if (index < opts->disposal_count) {
- gce[0] |= (opts->disposal[index] & 3) << 2;
+ Width = GifFile->Image.Width;
+ Height = GifFile->Image.Height;
+ if (page == -1 || page == ImageNum) {
+ if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
+ mm_log((1, "Adding local colormap\n"));
+ ColorMapSize = ColorMap->ColorCount;
+ } else {
+ /* No colormap and we are about to read in the image -
+ abandon for now */
+ mm_log((1, "Going in with no colormap\n"));
+ i_push_error(0, "Image does not have a local or a global color map");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
+ }
+
+ channels = 3;
+ if (got_gce && trans_index >= 0)
+ channels = 4;
+ if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
+ free_images(results, *count);
+ mm_log((1, "i_readgif: image size exceeds limits\n"));
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
+ }
+ img = i_img_pal_new(Width, Height, channels, 256);
+ if (!img) {
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ /* populate the palette of the new image */
+ mm_log((1, "ColorMapSize %d\n", ColorMapSize));
+ for (i = 0; i < ColorMapSize; ++i) {
+ i_color col;
+ col.rgba.r = ColorMap->Colors[i].Red;
+ col.rgba.g = ColorMap->Colors[i].Green;
+ col.rgba.b = ColorMap->Colors[i].Blue;
+ if (channels == 4 && trans_index == i)
+ col.rgba.a = 0;
+ else
+ col.rgba.a = 255;
+
+ i_addcolors(img, &col, 1);
+ }
+ ++*count;
+ if (*count > result_alloc) {
+ if (result_alloc == 0) {
+ result_alloc = 5;
+ results = mymalloc(result_alloc * sizeof(i_img *));
+ }
+ else {
+ /* myrealloc never fails (it just dies if it can't allocate) */
+ result_alloc *= 2;
+ results = myrealloc(results, result_alloc * sizeof(i_img *));
+ }
+ }
+ results[*count-1] = img;
+ i_tags_add(&img->tags, "i_format", 0, "gif", -1, 0);
+ i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
+ /**(char *)0 = 1;*/
+ i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
+ i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
+ i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
+ i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
+ if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
+ i_tags_addn(&img->tags, "gif_background", 0,
+ GifFile->SBackGroundColor);
+ }
+ if (GifFile->Image.ColorMap) {
+ i_tags_addn(&img->tags, "gif_localmap", 0, 1);
+ }
+ if (got_gce) {
+ if (trans_index >= 0) {
+ i_color trans;
+ i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
+ i_getcolors(img, trans_index, &trans, 1);
+ i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
+ }
+ i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
+ i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
+ i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
+ }
+ got_gce = 0;
+ if (got_ns_loop)
+ i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
+ if (comment) {
+ i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
+ myfree(comment);
+ comment = NULL;
+ }
+
+ mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
+ ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
+
+ if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
+ GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
+ i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return(0);
+ }
+
+ if (GifFile->Image.Interlace) {
+ for (Count = i = 0; i < 4; i++) {
+ for (j = InterlacedOffset[i]; j < Height;
+ j += InterlacedJumps[i]) {
+ Count++;
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading GIF line");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
+ }
+
+ i_ppal(img, 0, Width, j, GifRow);
+ }
+ }
+ }
+ else {
+ for (i = 0; i < Height; i++) {
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading GIF line");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ myfree(GifRow);
+ return NULL;
+ }
+
+ i_ppal(img, 0, Width, i, GifRow);
+ }
+ }
+
+ /* must be only one image wanted and that was it */
+ if (page != -1) {
+ myfree(GifRow);
+ DGifCloseFile(GifFile);
+ return results;
+ }
+ }
+ else {
+ /* skip the image */
+ /* whether interlaced or not, it has the same number of lines */
+ /* giflib does't have an interface to skip the image data */
+ for (i = 0; i < Height; i++) {
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading GIF line");
+ free_images(results, *count);
+ myfree(GifRow);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ }
+
+ /* kill the comment so we get the right comment for the page */
+ if (comment) {
+ myfree(comment);
+ comment = NULL;
+ }
+ }
+ ImageNum++;
+ break;
+ case EXTENSION_RECORD_TYPE:
+ /* Skip any extension blocks in file: */
+ if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading extension record");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ if (ExtCode == 0xF9) {
+ got_gce = 1;
+ if (Extension[1] & 1)
+ trans_index = Extension[4];
+ else
+ trans_index = -1;
+ gif_delay = Extension[2] + 256 * Extension[3];
+ user_input = (Extension[1] & 2) != 0;
+ disposal = (Extension[1] >> 2) & 7;
+ }
+ if (ExtCode == 0xFF && *Extension == 11) {
+ if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
+ if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "reading loop extension");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ if (Extension && *Extension == 3) {
+ got_ns_loop = 1;
+ ns_loop = Extension[2] + 256 * Extension[3];
+ }
+ }
+ }
+ else if (ExtCode == 0xFE) {
+ /* while it's possible for a GIF file to contain more than one
+ comment, I'm only implementing a single comment per image,
+ with the comment saved into the following image.
+ If someone wants more than that they can implement it.
+ I also don't handle comments that take more than one block.
+ */
+ if (!comment) {
+ comment = mymalloc(*Extension+1);
+ memcpy(comment, Extension+1, *Extension);
+ comment[*Extension] = '\0';
+ }
+ }
+ while (Extension != NULL) {
+ if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "reading next block of extension");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ }
+ break;
+ case TERMINATE_RECORD_TYPE:
+ break;
+ default: /* Should be trapped by DGifGetRecordType. */
+ break;
+ }
+ } while (RecordType != TERMINATE_RECORD_TYPE);
+
+ if (comment) {
+ if (*count) {
+ i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
+ strlen(comment), 0);
+ }
+ myfree(comment);
+ }
+
+ myfree(GifRow);
+
+ if (DGifCloseFile(GifFile) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Closing GIF file object");
+ free_images(results, *count);
+ return NULL;
+ }
+
+ if (ImageNum && page != -1) {
+ /* there were images, but the page selected wasn't found */
+ i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
+ free_images(results, *count);
+ return NULL;
+ }
+
+ return results;
+}
+
+#if IM_GIFMAJOR >= 4
+/* giflib declares this incorrectly as EgifOpen */
+extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
+
+static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
+#endif
+
+/*
+=item i_readgif_multi_wiol(ig, int *count)
+
+=cut
+*/
+
+i_img **
+i_readgif_multi_wiol(io_glue *ig, int *count) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ return i_readgif_multi(ig->source.fdseek.fd, count);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count, -1);
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+ }
+}
+
+/*
+=item i_readgif_multi(int fd, int *count)
+
+=cut
+*/
+i_img **
+i_readgif_multi(int fd, int *count) {
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
+
+ if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib file object");
+ mm_log((1,"i_readgif: Unable to open file\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count, -1);
+}
+
+/*
+=item i_readgif_multi_scalar(char *data, int length, int *count)
+
+=cut
+*/
+i_img **
+i_readgif_multi_scalar(char *data, int length, int *count) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ struct gif_scalar_info gsi;
+
+ i_clear_error();
+
+ gsi.cpos=0;
+ gsi.length=length;
+ gsi.data=data;
+
+ mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
+ data, length, count));
+
+ if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count, -1);
+#else
+ return NULL;
+#endif
+}
+
+/*
+=item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
+
+Read a GIF file into an Imager RGB file, the data of the GIF file is
+retreived by callin the user supplied callback function.
+
+This function is only used with giflib 4 and higher.
+
+=cut
+*/
+
+i_img**
+i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ i_img **result;
+
+ i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
+
+ i_clear_error();
+
+ mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
+ if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
+ myfree(gci);
+ return NULL;
+ }
+
+ result = i_readgif_multi_low(GifFile, count, -1);
+ i_free_gen_read_data(gci);
+
+ return result;
+#else
+ return NULL;
+#endif
+}
+
+/*
+=item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
+
+Write I<img> to the file handle I<fd>. The resulting GIF will use a
+maximum of 1<<I<max_colours> colours, with the first I<fixedlen>
+colours taken from I<fixed>.
+
+Returns non-zero on success.
+
+=cut
+*/
+
+undef_int
+i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
+ i_color colors[256];
+ i_quantize quant;
+
+ memset(&quant, 0, sizeof(quant));
+ quant.make_colors = mc_addi;
+ quant.mc_colors = colors;
+ quant.mc_size = 1<<max_colors;
+ quant.mc_count = fixedlen;
+ memcpy(colors, fixed, fixedlen * sizeof(i_color));
+ quant.translate = pt_perturb;
+ quant.perturb = pixdev;
+ return i_writegif_gen(&quant, fd, &im, 1);
+}
+
+/*
+=item i_writegifmc(i_img *im, int fd, int max_colors)
+
+Write I<img> to the file handle I<fd>. The resulting GIF will use a
+maximum of 1<<I<max_colours> colours.
+
+Returns non-zero on success.
+
+=cut
+*/
+
+undef_int
+i_writegifmc(i_img *im, int fd, int max_colors) {
+ i_color colors[256];
+ i_quantize quant;
+
+/* *(char *)0 = 1; */
+
+ memset(&quant, 0, sizeof(quant));
+ quant.make_colors = mc_none; /* ignored for pt_giflib */
+ quant.mc_colors = colors;
+ quant.mc_size = 1 << max_colors;
+ quant.mc_count = 0;
+ quant.translate = pt_giflib;
+ return i_writegif_gen(&quant, fd, &im, 1);
+}
+
+
+/*
+=item i_readgif_scalar(char *data, int length, int **colour_table, int *colours)
+
+Reads a GIF file from an in memory copy of the file. This can be used
+if you get the 'file' from some source other than an actual file (or
+some other file handle).
+
+This function is only available with giflib 4 and higher.
+
+=cut
+*/
+i_img*
+i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ struct gif_scalar_info gsi;
+
+ i_clear_error();
+
+ gsi.cpos=0;
+ gsi.length=length;
+ gsi.data=data;
+
+ mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
+ if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_low(GifFile, colour_table, colours);
+#else
+ return NULL;
+#endif
+}
+
+#if IM_GIFMAJOR >= 4
+
+/*
+=item gif_read_callback(GifFileType *gft, GifByteType *buf, int length)
+
+Internal. The reader callback wrapper passed to giflib.
+
+This function is only used with giflib 4 and higher.
+
+=cut
+*/
+
+static int
+gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
+ return i_gen_reader((i_gen_read_data *)gft->UserData, (char*)buf, length);
+}
+
+#endif
+
+
+/*
+=item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
+
+Read a GIF file into an Imager RGB file, the data of the GIF file is
+retreived by callin the user supplied callback function.
+
+This function is only used with giflib 4 and higher.
+
+=cut
+*/
+
+i_img*
+i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ i_img *result;
+
+ i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
+
+ i_clear_error();
+
+ mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
+ if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
+ myfree(gci);
+ return NULL;
+ }
+
+ result = i_readgif_low(GifFile, colour_table, colours);
+ i_free_gen_read_data(gci);
+
+ return result;
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+}
+
+#if IM_GIFMAJOR >= 4
+
+static int
+io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
+ io_glue *ig = (io_glue *)gft->UserData;
+
+ return ig->readcb(ig, buf, length);
+}
+
+#endif
+
+i_img *
+i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ int fd = dup(ig->source.fdseek.fd);
+ if (fd < 0) {
+ i_push_error(errno, "dup() failed");
+ return 0;
+ }
+ return i_readgif(fd, color_table, colors);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_low(GifFile, color_table, colors);
+
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+ }
+}
+
+/*
+=item i_readgif_single_low(GifFile, page)
+
+Lower level function to read a single image from a GIF.
+
+page must be non-negative.
+
+=cut
+*/
+static i_img *
+i_readgif_single_low(GifFileType *GifFile, int page) {
+ int count = 0;
+ i_img **imgs;
+
+ imgs = i_readgif_multi_low(GifFile, &count, page);
+
+ if (imgs && count) {
+ i_img *result = imgs[0];
+
+ myfree(imgs);
+ return result;
+ }
+ else {
+ /* i_readgif_multi_low() handles the errors appropriately */
+ return NULL;
+ }
+}
+
+/*
+=item i_readgif_single_wiol(ig, page)
+
+Read a single page from a GIF image file, where the page is indexed
+from 0.
+
+Returns NULL if the page isn't found.
+
+=cut
+*/
+
+i_img *
+i_readgif_single_wiol(io_glue *ig, int page) {
+ io_glue_commit_types(ig);
+
+ i_clear_error();
+
+ if (page < 0) {
+ i_push_error(0, "page must be non-negative");
+ return NULL;
+ }
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ GifFileType *GifFile;
+ int fd = dup(ig->source.fdseek.fd);
+ if (fd < 0) {
+ i_push_error(errno, "dup() failed");
+ return NULL;
+ }
+ if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib file object");
+ mm_log((1,"i_readgif: Unable to open file\n"));
+ return NULL;
+ }
+ return i_readgif_single_low(GifFile, page);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+
+ if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_single_low(GifFile, page);
+#else
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+ }
+}
+
+/*
+=item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
+
+Internal. Low level image write function. Writes in interlace if
+that was requested in the GIF options.
+
+Returns non-zero on success.
+
+=cut
+*/
+static undef_int
+do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
+ if (interlace) {
+ int i, j;
+ for (i = 0; i < 4; ++i) {
+ for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
+ if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Could not save image data:");
+ mm_log((1, "Error in EGifPutLine\n"));
+ EGifCloseFile(gf);
+ return 0;
+ }
+ }
+ }
+ }
+ else {
+ int y;
+ for (y = 0; y < img->ysize; ++y) {
+ if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Could not save image data:");
+ mm_log((1, "Error in EGifPutLine\n"));
+ EGifCloseFile(gf);
+ return 0;
+ }
+ data += img->xsize;
+ }
+ }
+
+ return 1;
+}
+
+/*
+=item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
+
+Internal. Writes the GIF graphics control extension, if necessary.
+
+Returns non-zero on success.
+
+=cut
+*/
+static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
+{
+ unsigned char gce[4] = {0};
+ int want_gce = 0;
+ int delay;
+ int user_input;
+ int disposal_method;
+
+ if (want_trans) {
+ gce[0] |= 1;
+ gce[3] = trans_index;
+ ++want_gce;
+ }
+ if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
+ gce[1] = delay % 256;
+ gce[2] = delay / 256;
+ ++want_gce;
+ }
+ if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
+ && user_input) {
+ gce[0] |= 2;
+ ++want_gce;
+ }
+ if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
+ gce[0] |= (disposal_method & 3) << 2;
++want_gce;
}
if (want_gce) {
return 1;
}
+/*
+=item do_comments(gf, img)
+
+Write any comments in the image.
+
+=cut
+*/
+static int do_comments(GifFileType *gf, i_img *img) {
+ int pos = -1;
+
+ while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
+ if (img->tags.tags[pos].data) {
+ if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
+ return 0;
+ }
+ }
+ else {
+ char buf[50];
+ sprintf(buf, "%d", img->tags.tags[pos].idata);
+ if (EGifPutComment(gf, buf) == GIF_ERROR) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
/*
=item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
Internal. Add the Netscape2.0 loop extension block, if requested.
-The code for this function is currently "#if 0"ed out since the giflib
-extension writing code currently doesn't seem to support writing
-application extension blocks.
+Giflib/libungif prior to 4.1.1 didn't support writing application
+extension blocks, so we don't attempt to write them for older versions.
+
+Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
+writing extension blocks so that they could only be written to files.
=cut
*/
-static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
+static int do_ns_loop(GifFileType *gf, i_img *img)
{
/* EGifPutExtension() doesn't appear to handle application
extension blocks in any way
If giflib's callback interface wasn't broken by default, I'd
force file writes to use callbacks, but it is broken by default.
*/
-#if 0
/* yes this was another attempt at supporting the loop extension */
- if (opts->loop_count) {
+#if IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1
+ int loop_count;
+ if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
unsigned char nsle[12] = "NETSCAPE2.0";
unsigned char subblock[3];
- if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
+ if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
gif_push_error();
i_push_error(0, "writing loop extension");
return 0;
}
subblock[0] = 1;
- subblock[1] = opts->loop_count % 256;
- subblock[2] = opts->loop_count / 256;
- if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
+ subblock[1] = loop_count % 256;
+ subblock[2] = loop_count / 256;
+ if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
gif_push_error();
i_push_error(0, "writing loop extention sub-block");
return 0;
}
- if (EGifPutExtension(gf, 0, 0, subblock) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "writing loop extension terminator");
- return 0;
- }
}
#endif
+
return 1;
}
/*
-=item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
+=item make_gif_map(i_quantize *quant, int want_trans)
Create a giflib color map object from an Imager color map.
=cut
*/
-static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
- int want_trans) {
+static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
+ int want_trans) {
GifColorType colors[256];
int i;
int size = quant->mc_count;
int map_size;
ColorMapObject *map;
+ i_color trans;
for (i = 0; i < quant->mc_count; ++i) {
colors[i].Red = quant->mc_colors[i].rgb.r;
colors[i].Blue = quant->mc_colors[i].rgb.b;
}
if (want_trans) {
- colors[size].Red = opts->tran_color.rgb.r;
- colors[size].Green = opts->tran_color.rgb.g;
- colors[size].Blue = opts->tran_color.rgb.b;
+ if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
+ trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
+ colors[size].Red = trans.rgb.r;
+ colors[size].Green = trans.rgb.g;
+ colors[size].Blue = trans.rgb.b;
++size;
}
map_size = 1;
/* giflib spews for 1 colour maps, reasonable, I suppose */
if (map_size == 1)
map_size = 2;
+ while (i < map_size) {
+ colors[i].Red = colors[i].Green = colors[i].Blue = 0;
+ ++i;
+ }
+
map = MakeMapObject(map_size, colors);
+ mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
if (!map) {
gif_push_error();
i_push_error(0, "Could not create color map object");
return NULL;
}
- return map;
+ return map;
+}
+
+/*
+=item gif_set_version(i_quantize *quant, i_img *imgs, int count)
+
+We need to call EGifSetGifVersion() before opening the file - put that
+common code here.
+
+Unfortunately giflib 4.1.0 crashes when we use this. Internally
+giflib 4.1.0 has code:
+
+ static char *GifVersionPrefix = GIF87_STAMP;
+
+and the code that sets the version internally does:
+
+ strncpy(&GifVersionPrefix[3], Version, 3);
+
+which is very broken.
+
+Failing to set the correct GIF version doesn't seem to cause a problem
+with readers.
+
+Modern versions (4.1.4 anyway) of giflib/libungif handle
+EGifSetGifVersion correctly.
+
+If t/t105gif.t crashes here then run Makefile.PL with
+--nogifsetversion, eg.:
+
+ perl Makefile.PL --nogifsetversion
+
+or install a less buggy giflib.
+
+=cut
+*/
+
+static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
+#if (IM_GIFMAJOR >= 4 || IM_GIFMAJOR == 4 && IM_GIFMINOR >= 1) \
+ && !defined(IM_NO_SET_GIF_VERSION)
+ int need_89a = 0;
+ int temp;
+ int i;
+
+ if (quant->transp != tr_none)
+ need_89a = 1;
+ else {
+ for (i = 0; i < count; ++i) {
+ if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
+ need_89a = 1;
+ break;
+ }
+ if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
+ need_89a = 1;
+ break;
+ }
+ if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
+ need_89a = 1;
+ break;
+ }
+ if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
+ need_89a = 1;
+ break;
+ }
+ }
+ }
+ if (need_89a)
+ EGifSetGifVersion("89a");
+ else
+ EGifSetGifVersion("87a");
+#endif
+}
+
+static int
+in_palette(i_color *c, i_quantize *quant, int size) {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (c->channel[0] == quant->mc_colors[i].channel[0]
+ && c->channel[1] == quant->mc_colors[i].channel[1]
+ && c->channel[2] == quant->mc_colors[i].channel[2]) {
+ return i;
+ }
+ }
+
+ return -1;
}
/*
-=item gif_set_version(i_quantize *quant, i_gif_opts *opts)
+=item has_common_palette(imgs, count, quant, want_trans)
-We need to call EGifSetGifVersion() before opening the file - put that
-common code here.
+Tests if all the given images are paletted and have a common palette,
+if they do it builds that palette.
-Unfortunately giflib 4.1.0 crashes when we use this. Internally
-giflib 4.1.0 has code:
+A possible improvement might be to eliminate unused colors in the
+images palettes.
- static char *GifVersionPrefix = GIF87_STAMP;
+=cut
+*/
+static int
+has_common_palette(i_img **imgs, int count, i_quantize *quant,
+ int want_trans) {
+ int size = quant->mc_count;
+ int i;
+ int imgn;
+ char used[256];
-and the code that sets the version internally does:
+ /* we try to build a common palette here, if we can manage that, then
+ that's the palette we use */
+ for (imgn = 0; imgn < count; ++imgn) {
+ int eliminate_unused;
+ if (imgs[imgn]->type != i_palette_type)
+ return 0;
- strncpy(&GifVersionPrefix[3], Version, 3);
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
+ &eliminate_unused)) {
+ eliminate_unused = 1;
+ }
-which is very broken.
+ if (eliminate_unused) {
+ i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
+ int x, y;
+ memset(used, 0, sizeof(used));
-Failing to set the correct GIF version doesn't seem to cause a problem
-with readers.
+ for (y = 0; y < imgs[imgn]->ysize; ++y) {
+ i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
+ for (x = 0; x < imgs[imgn]->xsize; ++x)
+ used[line[x]] = 1;
+ }
-=cut
-*/
+ myfree(line);
+ }
+ else {
+ /* assume all are in use */
+ memset(used, 1, sizeof(used));
+ }
-static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
- /* the following crashed giflib
- the EGifSetGifVersion() is seriously borked in giflib
- it's less borked in the ungiflib beta, but we don't have a mechanism
- to distinguish them
- if (opts->delay_count
- || opts->user_input_count
- || opts->disposal_count
- || opts->loop_count
- || quant->transp != tr_none)
- EGifSetGifVersion("89a");
- else
- EGifSetGifVersion("87a");
- */
+ for (i = 0; i < i_colorcount(imgs[imgn]); ++i) {
+ i_color c;
+
+ i_getcolors(imgs[imgn], i, &c, 1);
+ if (used[i]) {
+ if (in_palette(&c, quant, size) < 0) {
+ if (size < quant->mc_size) {
+ quant->mc_colors[size++] = c;
+ }
+ else {
+ /* oops, too many colors */
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ quant->mc_count = size;
+
+ return 1;
+}
+
+static i_palidx *
+quant_paletted(i_quantize *quant, i_img *img) {
+ i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
+ i_palidx *p = data;
+ i_palidx trans[256];
+ int i;
+ int x, y;
+
+ /* build a translation table */
+ for (i = 0; i < i_colorcount(img); ++i) {
+ i_color c;
+ i_getcolors(img, i, &c, 1);
+ trans[i] = in_palette(&c, quant, quant->mc_count);
+ }
+
+ for (y = 0; y < img->ysize; ++y) {
+ i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
+ for (x = 0; x < img->xsize; ++x) {
+ *p = trans[*p];
+ ++p;
+ }
+ }
+
+ return data;
}
/*
*/
static undef_int
-i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
- i_gif_opts *opts) {
- unsigned char *result;
+i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
+ unsigned char *result = NULL;
int color_bits;
ColorMapObject *map;
int scrw = 0, scrh = 0;
int imgn, orig_count, orig_size;
int posx, posy;
+ int trans_index = -1;
+ i_mempool mp;
+ int *localmaps;
+ int anylocal;
+ i_img **glob_imgs; /* images that will use the global color map */
+ int glob_img_count;
+ i_color *orig_colors = quant->mc_colors;
+ i_color *glob_colors = NULL;
+ int glob_color_count = 0;
+ int glob_want_trans;
+ int glob_paletted = 0; /* the global map was made from the image palettes */
+ int colors_paletted = 0;
+ int want_trans = 0;
+ int interlace;
+ int gif_background;
+
+ mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
+ quant, gf, imgs, count));
+
+ /* *((char *)0) = 1; */ /* used to break into the debugger */
+
+ if (count <= 0) {
+ i_push_error(0, "No images provided to write");
+ return 0; /* what are you smoking? */
+ }
- mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
- quant, gf, imgs, count, opts));
+ i_mempool_init(&mp);
- /**((char *)0) = 1;*/
/* sanity is nice */
if (quant->mc_size > 256)
quant->mc_size = 256;
if (quant->mc_count > quant->mc_size)
quant->mc_count = quant->mc_size;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
+ scrw = 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
+ scrw = 0;
+
+ anylocal = 0;
+ localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
+ glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
+ glob_img_count = 0;
+ glob_want_trans = 0;
for (imgn = 0; imgn < count; ++imgn) {
- if (imgn < opts->position_count) {
- if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
- scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
- if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
- scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
- }
+ posx = posy = 0;
+ i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
+ i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
+ if (imgs[imgn]->xsize + posx > scrw)
+ scrw = imgs[imgn]->xsize + posx;
+ if (imgs[imgn]->ysize + posy > scrh)
+ scrh = imgs[imgn]->ysize + posy;
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
+ localmaps[imgn] = 0;
+ if (localmaps[imgn])
+ anylocal = 1;
else {
- if (imgs[imgn]->xsize > scrw)
- scrw = imgs[imgn]->xsize;
- if (imgs[imgn]->ysize > scrh)
- scrh = imgs[imgn]->ysize;
+ if (imgs[imgn]->channels == 4) {
+ glob_want_trans = 1;
+ }
+ glob_imgs[glob_img_count++] = imgs[imgn];
}
}
-
- if (count <= 0) {
- i_push_error(0, "No images provided to write");
- return 0; /* what are you smoking? */
- }
+ glob_want_trans = glob_want_trans && quant->transp != tr_none ;
orig_count = quant->mc_count;
orig_size = quant->mc_size;
- if (opts->each_palette) {
- int want_trans = quant->transp != tr_none
- && imgs[0]->channels == 4;
-
- /* if the caller gives us too many colours we can't do transparency */
- if (want_trans && quant->mc_count == 256)
- want_trans = 0;
- /* if they want transparency but give us a big size, make it smaller
- to give room for a transparency colour */
- if (want_trans && quant->mc_size == 256)
+ if (glob_img_count) {
+ /* this is ugly */
+ glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
+ quant->mc_colors = glob_colors;
+ memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
+ /* we have some images that want to use the global map */
+ if (glob_want_trans && quant->mc_count == 256) {
+ mm_log((2, " disabling transparency for global map - no space\n"));
+ glob_want_trans = 0;
+ }
+ if (glob_want_trans && quant->mc_size == 256) {
+ mm_log((2, " reserving color for transparency\n"));
--quant->mc_size;
-
- /* we always generate a global palette - this lets systems with a
- broken giflib work */
- quant_makemap(quant, imgs, 1);
- result = quant_translate(quant, imgs[0]);
-
- if (want_trans)
- quant_transparent(quant, result, imgs[0], quant->mc_count);
-
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject."));
- return 0;
}
+ if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
+ glob_paletted = 1;
+ }
+ else {
+ glob_paletted = 0;
+ i_quant_makemap(quant, glob_imgs, glob_img_count);
+ }
+ glob_color_count = quant->mc_count;
+ quant->mc_colors = orig_colors;
+ }
- color_bits = 1;
- while (quant->mc_size > (1 << color_bits))
+ /* use the global map if we have one, otherwise use the local map */
+ gif_background = 0;
+ if (glob_colors) {
+ quant->mc_colors = glob_colors;
+ quant->mc_count = glob_color_count;
+ want_trans = glob_want_trans && imgs[0]->channels == 4;
+
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
+ gif_background = 0;
+ if (gif_background < 0)
+ gif_background = 0;
+ if (gif_background >= glob_color_count)
+ gif_background = 0;
+ }
+ else {
+ want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
+ if (has_common_palette(imgs, 1, quant, want_trans)) {
+ colors_paletted = 1;
+ }
+ else {
+ colors_paletted = 0;
+ i_quant_makemap(quant, imgs, 1);
+ }
+ }
+ if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ mm_log((1, "Error in MakeMapObject"));
+ return 0;
+ }
+ color_bits = 1;
+ if (anylocal) {
+ /* since we don't know how big some the local palettes could be
+ we need to base the bits on the maximum number of colors */
+ while (orig_size > (1 << color_bits))
+ ++color_bits;
+ }
+ else {
+ int count = quant->mc_count;
+ if (want_trans)
+ ++count;
+ while (count > (1 << color_bits))
++color_bits;
+ }
- if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save screen descriptor");
- FreeMapObject(map);
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutScreenDesc."));
- return 0;
- }
+ if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
+ gif_background, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ gif_push_error();
+ i_push_error(0, "Could not save screen descriptor");
FreeMapObject(map);
+ myfree(result);
+ EGifCloseFile(gf);
+ mm_log((1, "Error in EGifPutScreenDesc."));
+ return 0;
+ }
+ FreeMapObject(map);
- if (!do_ns_loop(gf, opts))
- return 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
+ posx = 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
+ posy = 0;
- if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (opts->position_count) {
- posx = opts->positions[0].x;
- posy = opts->positions[0].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
+ if (!localmaps[0]) {
+ map = NULL;
+ colors_paletted = glob_paletted;
+ }
+ else {
+ /* if this image has a global map the colors in quant don't
+ belong to this image, so build a palette */
+ if (glob_colors) {
+ /* generate the local map for this image */
+ quant->mc_colors = orig_colors;
+ quant->mc_size = orig_size;
+ quant->mc_count = orig_count;
+ want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
+
+ /* if the caller gives us too many colours we can't do transparency */
+ if (want_trans && quant->mc_count == 256)
+ want_trans = 0;
+ /* if they want transparency but give us a big size, make it smaller
+ to give room for a transparency colour */
+ if (want_trans && quant->mc_size == 256)
+ --quant->mc_size;
+ if (has_common_palette(imgs, 1, quant, want_trans)) {
+ colors_paletted = 1;
+ }
+ else {
+ colors_paletted = 0;
+ i_quant_makemap(quant, imgs, 1);
+ }
+ if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ EGifCloseFile(gf);
+ quant->mc_colors = orig_colors;
+ mm_log((1, "Error in MakeMapObject"));
+ return 0;
+ }
}
- if (!do_write(gf, opts, imgs[0], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
+ else {
+ /* the map we wrote was the map for this image - don't set the local
+ map */
+ map = NULL;
}
- for (imgn = 1; imgn < count; ++imgn) {
+ }
+
+ if (colors_paletted)
+ result = quant_paletted(quant, imgs[0]);
+ else
+ result = i_quant_translate(quant, imgs[0]);
+ if (!result) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ return 0;
+ }
+ if (want_trans) {
+ i_quant_transparent(quant, result, imgs[0], quant->mc_count);
+ trans_index = quant->mc_count;
+ }
+
+ if (!do_ns_loop(gf, imgs[0])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ return 0;
+ }
+
+ if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ myfree(result);
+ EGifCloseFile(gf);
+ return 0;
+ }
+
+ if (!do_comments(gf, imgs[0])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ myfree(result);
+ EGifCloseFile(gf);
+ return 0;
+ }
+
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
+ interlace = 0;
+ if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
+ interlace, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ gif_push_error();
+ i_push_error(0, "Could not save image descriptor");
+ EGifCloseFile(gf);
+ mm_log((1, "Error in EGifPutImageDesc."));
+ return 0;
+ }
+ if (map)
+ FreeMapObject(map);
+
+ if (!do_write(gf, interlace, imgs[0], result)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ myfree(result);
+ return 0;
+ }
+ myfree(result);
+
+ /* that first awful image is out of the way, do the rest */
+ for (imgn = 1; imgn < count; ++imgn) {
+ if (localmaps[imgn]) {
+ quant->mc_colors = orig_colors;
quant->mc_count = orig_count;
quant->mc_size = orig_size;
+
want_trans = quant->transp != tr_none
- && imgs[0]->channels == 4;
+ && imgs[imgn]->channels == 4;
/* if the caller gives us too many colours we can't do transparency */
if (want_trans && quant->mc_count == 256)
want_trans = 0;
if (want_trans && quant->mc_size == 256)
--quant->mc_size;
- quant_makemap(quant, imgs+imgn, 1);
- result = quant_translate(quant, imgs[imgn]);
- if (want_trans)
- quant_transparent(quant, result, imgs[imgn], quant->mc_count);
-
- if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
+ if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
+ result = quant_paletted(quant, imgs[imgn]);
}
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject."));
- return 0;
+ else {
+ i_quant_makemap(quant, imgs+imgn, 1);
+ result = i_quant_translate(quant, imgs[imgn]);
}
- if (imgn < opts->position_count) {
- posx = opts->positions[imgn].x;
- posy = opts->positions[imgn].y;
+ if (!result) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ mm_log((1, "error in i_quant_translate()"));
+ return 0;
}
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
- imgs[imgn]->ysize, opts->interlace,
- map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- myfree(result);
- FreeMapObject(map);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
+ if (want_trans) {
+ i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
+ trans_index = quant->mc_count;
}
- FreeMapObject(map);
-
- if (!do_write(gf, opts, imgs[imgn], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
+
+ if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ myfree(result);
+ EGifCloseFile(gf);
+ mm_log((1, "Error in MakeMapObject."));
+ return 0;
}
- myfree(result);
}
- }
- else {
- int want_trans;
-
- /* get a palette entry for the transparency iff we have an image
- with an alpha channel */
- want_trans = 0;
- for (imgn = 0; imgn < count; ++imgn) {
- if (imgs[imgn]->channels == 4) {
- ++want_trans;
- break;
+ else {
+ quant->mc_colors = glob_colors;
+ quant->mc_count = glob_color_count;
+ if (glob_paletted)
+ result = quant_paletted(quant, imgs[imgn]);
+ else
+ result = i_quant_translate(quant, imgs[imgn]);
+ want_trans = glob_want_trans && imgs[imgn]->channels == 4;
+ if (want_trans) {
+ i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
+ trans_index = quant->mc_count;
}
+ map = NULL;
}
- want_trans = want_trans && quant->transp != tr_none
- && quant->mc_count < 256;
- if (want_trans && quant->mc_size == 256)
- --quant->mc_size;
- /* handle the first image separately - since we allow giflib
- conversion and giflib doesn't give us a separate function to build
- the colormap. */
-
- /* produce a colour map */
- quant_makemap(quant, imgs, count);
- result = quant_translate(quant, imgs[0]);
-
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
+ if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
myfree(result);
EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject"));
return 0;
}
- color_bits = 1;
- while (quant->mc_count > (1 << color_bits))
- ++color_bits;
- if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save screen descriptor");
- FreeMapObject(map);
+ if (!do_comments(gf, imgs[imgn])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
myfree(result);
EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutScreenDesc."));
return 0;
}
- FreeMapObject(map);
-
- if (!do_ns_loop(gf, opts))
- return 0;
- if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (opts->position_count) {
- posx = opts->positions[0].x;
- posy = opts->positions[0].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
+ posx = 0;
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
+ posy = 0;
+
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
+ interlace = 0;
+ if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
+ imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
gif_push_error();
i_push_error(0, "Could not save image descriptor");
+ myfree(result);
+ if (map)
+ FreeMapObject(map);
EGifCloseFile(gf);
mm_log((1, "Error in EGifPutImageDesc."));
return 0;
}
- if (want_trans && imgs[0]->channels == 4)
- quant_transparent(quant, result, imgs[0], quant->mc_count);
-
- if (!do_write(gf, opts, imgs[0], result)) {
+ if (map)
+ FreeMapObject(map);
+
+ if (!do_write(gf, interlace, imgs[imgn], result)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
EGifCloseFile(gf);
myfree(result);
return 0;
}
myfree(result);
-
- for (imgn = 1; imgn < count; ++imgn) {
- int local_trans;
- result = quant_translate(quant, imgs[imgn]);
- local_trans = want_trans && imgs[imgn]->channels == 4;
- if (local_trans)
- quant_transparent(quant, result, imgs[imgn], quant->mc_count);
- if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (imgn < opts->position_count) {
- posx = opts->positions[imgn].x;
- posy = opts->positions[imgn].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy,
- imgs[imgn]->xsize, imgs[imgn]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
- }
- if (!do_write(gf, opts, imgs[imgn], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
- }
- myfree(result);
- }
}
+
if (EGifCloseFile(gf) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
gif_push_error();
i_push_error(0, "Could not close GIF file");
mm_log((1, "Error in EGifCloseFile\n"));
return 0;
}
+ if (glob_colors) {
+ int i;
+ for (i = 0; i < glob_color_count; ++i)
+ orig_colors[i] = glob_colors[i];
+ }
+
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
return 1;
}
*/
undef_int
-i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
- i_gif_opts *opts) {
+i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
GifFileType *gf;
i_clear_error();
- mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
- quant, fd, imgs, count, opts));
+ mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
+ quant, fd, imgs, count));
- gif_set_version(quant, opts);
+ gif_set_version(quant, imgs, count);
if ((gf = EGifOpenFileHandle(fd)) == NULL) {
gif_push_error();
return 0;
}
- return i_writegif_low(quant, gf, imgs, count, opts);
+ return i_writegif_low(quant, gf, imgs, count);
}
#if IM_GIFMAJOR >= 4
{
i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
- return i_gen_writer(gwd, data, size) ? size : 0;
+ return i_gen_writer(gwd, (char*)data, size) ? size : 0;
}
#endif
undef_int
i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
- int maxlength, i_img **imgs, int count, i_gif_opts *opts)
+ int maxlength, i_img **imgs, int count)
{
#if IM_GIFMAJOR >= 4
GifFileType *gf;
i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
- /* giflib declares this incorrectly as EgifOpen */
- extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
int result;
i_clear_error();
- mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
- quant, cb, userdata, maxlength, imgs, count, opts));
+ mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
+ quant, cb, userdata, maxlength, imgs, count));
if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
gif_push_error();
i_push_error(0, "Cannot create GIF file object");
mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
- free_gen_write_data(gwd, 0);
+ i_free_gen_write_data(gwd, 0);
return 0;
}
- result = i_writegif_low(quant, gf, imgs, count, opts);
- return free_gen_write_data(gwd, result);
+ result = i_writegif_low(quant, gf, imgs, count);
+ return i_free_gen_write_data(gwd, result);
#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
return 0;
#endif
}
+#if IM_GIFMAJOR >= 4
+
+static int
+io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
+ io_glue *ig = (io_glue *)gft->UserData;
+
+ return ig->writecb(ig, data, length);
+}
+
+#endif
+
+/*
+=item i_writegif_wiol(ig, quant, opts, imgs, count)
+
+=cut
+*/
+undef_int
+i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
+ int count) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ int fd = dup(ig->source.fdseek.fd);
+ if (fd < 0) {
+ i_push_error(errno, "dup() failed");
+ return 0;
+ }
+ /* giflib opens the fd with fdopen(), which is then closed when fclose()
+ is called - dup it so the caller's fd isn't closed */
+ return i_writegif_gen(quant, fd, imgs, count);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ int result;
+
+ i_clear_error();
+
+ gif_set_version(quant, imgs, count);
+
+ if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
+ return 0;
+ }
+
+ result = i_writegif_low(quant, GifFile, imgs, count);
+
+ ig->closecb(ig);
+
+ return result;
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return 0;
+#endif
+ }
+}
+
/*
=item gif_error_msg(int code)
The returned pointer points to a static buffer, either from a literal
C string or a static buffer.
-=cut */
+=cut
+*/
static char const *gif_error_msg(int code) {
static char msg[80];
=cut
*/
-static void gif_push_error() {
+static void gif_push_error(void) {
int code = GifLastError(); /* clears saved error */
i_push_error(code, gif_error_msg(code));