diff options
Diffstat (limited to 'generic/tkImgGIF.c')
-rw-r--r-- | generic/tkImgGIF.c | 1954 |
1 files changed, 1033 insertions, 921 deletions
diff --git a/generic/tkImgGIF.c b/generic/tkImgGIF.c index 9c2004a..e576559 100644 --- a/generic/tkImgGIF.c +++ b/generic/tkImgGIF.c @@ -1,20 +1,20 @@ /* * tkImgGIF.c -- * - * A photo image file handler for GIF files. Reads 87a and 89a GIF - * files. At present, there only is a file write function. GIF images - * may be read using the -data option of the photo image. The data may be - * given as a binary string in a Tcl_Obj or by representing - * the data as BASE64 encoded ascii. Derived from the giftoppm code - * found in the pbmplus package and tkImgFmtPPM.c in the tk4.0b2 - * distribution. + * A photo image file handler for GIF files. Reads 87a and 89a GIF files. + * At present, there only is a file write function. GIF images may be + * read using the -data option of the photo image. The data may be given + * as a binary string in a Tcl_Obj or by representing the data as BASE64 + * encoded ascii. Derived from the giftoppm code found in the pbmplus + * package and tkImgFmtPPM.c in the tk4.0b2 distribution. * * Copyright (c) Reed Wade (wade@cs.utk.edu), University of Tennessee * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Copyright (c) 1997 Australian National University + * Copyright (c) 2005 Donal K. Fellows * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. * * This file also contains code from the giftoppm program, which is * copyrighted as follows: @@ -25,18 +25,22 @@ * | and its documentation for any purpose and without fee is hereby | * | granted, provided that the above copyright notice appear in all | * | copies and that both that copyright notice and this permission | - * | notice appear in supporting documentation. This software is | + * | notice appear in supporting documentation. This software is | * | provided "as is" without express or implied warranty. | - * +-------------------------------------------------------------------+ + * +--------------------------------------------------------------------+ + * + * This file also contains code from miGIF. See lower down in file for the + * applicable copyright notice for that portion. */ +#include "tkInt.h" + /* - * GIF's are represented as data in base64 format. - * base64 strings consist of 4 6-bit characters -> 3 8 bit bytes. - * A-Z, a-z, 0-9, + and / represent the 64 values (in order). - * '=' is a trailing padding char when the un-encoded data is not a - * multiple of 3 bytes. We'll ignore white space when encountered. - * Any other invalid character is treated as an EOF + * GIF's are represented as data in either binary or base64 format. base64 + * strings consist of 4 6-bit characters -> 3 8 bit bytes. A-Z, a-z, 0-9, + + * and / represent the 64 values (in order). '=' is a trailing padding char + * when the un-encoded data is not a multiple of 3 bytes. We'll ignore white + * space when encountered. Any other invalid character is treated as an EOF */ #define GIF_SPECIAL (256) @@ -46,77 +50,86 @@ #define GIF_DONE (GIF_SPECIAL+4) /* - * structure to "mimic" FILE for Mread, so we can look like fread. - * The decoder state keeps track of which byte we are about to read, - * or EOF. + * structure to "mimic" FILE for Mread, so we can look like fread. The decoder + * state keeps track of which byte we are about to read, or EOF. */ typedef struct mFile { unsigned char *data; /* mmencoded source string */ - int length; /* Length of string in bytes */ int c; /* bits left over from previous character */ int state; /* decoder state (0-4 or GIF_DONE) */ + int length; /* Total amount of bytes in data */ } MFile; -#include "tkInt.h" -#include "tkPort.h" - /* * Non-ASCII encoding support: - * Most data in a GIF image is binary and is treated as such. However, - * a few key bits are stashed in ASCII. If we try to compare those pieces - * to the char they represent, it will fail on any non-ASCII (eg, EBCDIC) - * system. To accomodate these systems, we test against the numeric value - * of the ASCII characters instead of the characters themselves. This is - * encoding independant. + * Most data in a GIF image is binary and is treated as such. However, a few + * key bits are stashed in ASCII. If we try to compare those pieces to the + * char they represent, it will fail on any non-ASCII (eg, EBCDIC) system. To + * accomodate these systems, we test against the numeric value of the ASCII + * characters instead of the characters themselves. This is encoding + * independant. */ -static CONST char GIF87a[] = { /* ASCII GIF87a */ +static const char GIF87a[] = { /* ASCII GIF87a */ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x00 }; -static CONST char GIF89a[] = { /* ASCII GIF89a */ +static const char GIF89a[] = { /* ASCII GIF89a */ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x00 }; -# define GIF_TERMINATOR 0x3b /* ASCII ; */ -# define GIF_EXTENSION 0x21 /* ASCII ! */ -# define GIF_START 0x2c /* ASCII , */ +#define GIF_TERMINATOR 0x3b /* ASCII ; */ +#define GIF_EXTENSION 0x21 /* ASCII ! */ +#define GIF_START 0x2c /* ASCII , */ /* - * HACK ALERT!! HACK ALERT!! HACK ALERT!! - * This code is hard-wired for reading from files. In order to read - * from a data stream, we'll trick fread so we can reuse the same code. - * 0==from file; 1==from base64 encoded data; 2==from binary data + * Flags used to notify that we've got inline data instead of a file to read + * from. Note that we need to figure out which type of inline data we've got + * before handing off to the GIF reading code; this is done in StringReadGIF. */ -typedef struct ThreadSpecificData { - int fromData; -} ThreadSpecificData; -static Tcl_ThreadDataKey dataKey; +#define INLINE_DATA_BINARY ((const char *) 0x01) +#define INLINE_DATA_BASE64 ((const char *) 0x02) + +/* + * HACK ALERT!! HACK ALERT!! HACK ALERT!! + * This code is hard-wired for reading from files. In order to read from a + * data stream, we'll trick fread so we can reuse the same code. 0==from file; + * 1==from base64 encoded data; 2==from binary data + */ + +typedef struct { + const char *fromData; + unsigned char workingBuffer[280]; + struct { + int bytes; + int done; + unsigned int window; + int bitsInWindow; + unsigned char *c; + } reader; +} GIFImageConfig; /* * The format record for the GIF file format: */ -static int FileMatchGIF _ANSI_ARGS_((Tcl_Channel chan, CONST char *fileName, - Tcl_Obj *format, int *widthPtr, int *heightPtr, - Tcl_Interp *interp)); -static int FileReadGIF _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Channel chan, CONST char *fileName, Tcl_Obj *format, - Tk_PhotoHandle imageHandle, int destX, int destY, - int width, int height, int srcX, int srcY)); -static int StringMatchGIF _ANSI_ARGS_(( Tcl_Obj *dataObj, - Tcl_Obj *format, int *widthPtr, int *heightPtr, - Tcl_Interp *interp)); -static int StringReadGIF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj, - Tcl_Obj *format, Tk_PhotoHandle imageHandle, - int destX, int destY, int width, int height, - int srcX, int srcY)); -static int FileWriteGIF _ANSI_ARGS_((Tcl_Interp *interp, - CONST char *filename, Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr)); -static int CommonWriteGIF _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Channel handle, Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr)); +static int FileMatchGIF(Tcl_Channel chan, const char *fileName, + Tcl_Obj *format, int *widthPtr, int *heightPtr, + Tcl_Interp *interp); +static int FileReadGIF(Tcl_Interp *interp, Tcl_Channel chan, + const char *fileName, Tcl_Obj *format, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY); +static int StringMatchGIF(Tcl_Obj *dataObj, Tcl_Obj *format, + int *widthPtr, int *heightPtr, Tcl_Interp *interp); +static int StringReadGIF(Tcl_Interp *interp, Tcl_Obj *dataObj, + Tcl_Obj *format, Tk_PhotoHandle imageHandle, + int destX, int destY, int width, int height, + int srcX, int srcY); +static int FileWriteGIF(Tcl_Interp *interp, const char *filename, + Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr); +static int CommonWriteGIF(Tcl_Interp *interp, Tcl_Channel handle, + Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr); Tk_PhotoImageFormat tkImgFmtGIF = { "gif", /* name */ @@ -138,54 +151,53 @@ Tk_PhotoImageFormat tkImgFmtGIF = { #define CM_ALPHA 3 #define MAX_LWZ_BITS 12 #define LM_to_uint(a,b) (((b)<<8)|(a)) -#define ReadOK(file,buffer,len) (Fread(buffer, len, 1, file) != 0) /* - * Prototypes for local procedures defined in this file: + * Prototypes for local functions defined in this file: */ -static int DoExtension _ANSI_ARGS_((Tcl_Channel chan, int label, - int *transparent)); -static int GetCode _ANSI_ARGS_((Tcl_Channel chan, int code_size, - int flag)); -static int GetDataBlock _ANSI_ARGS_((Tcl_Channel chan, - unsigned char *buf)); -static int ReadColorMap _ANSI_ARGS_((Tcl_Channel chan, int number, - unsigned char buffer[MAXCOLORMAPSIZE][4])); -static int ReadGIFHeader _ANSI_ARGS_((Tcl_Channel chan, - int *widthPtr, int *heightPtr)); -static int ReadImage _ANSI_ARGS_((Tcl_Interp *interp, - char *imagePtr, Tcl_Channel chan, - int len, int rows, - unsigned char cmap[MAXCOLORMAPSIZE][4], - int width, int height, int srcX, int srcY, - int interlace, int transparent)); +static int DoExtension(GIFImageConfig *gifConfPtr, + Tcl_Channel chan, int label, unsigned char *buffer, + int *transparent); +static int GetCode(Tcl_Channel chan, int code_size, int flag, + GIFImageConfig *gifConfPtr); +static int GetDataBlock(GIFImageConfig *gifConfPtr, + Tcl_Channel chan, unsigned char *buf); +static int ReadColorMap(GIFImageConfig *gifConfPtr, + Tcl_Channel chan, int number, + unsigned char buffer[MAXCOLORMAPSIZE][4]); +static int ReadGIFHeader(GIFImageConfig *gifConfPtr, + Tcl_Channel chan, int *widthPtr, int *heightPtr); +static int ReadImage(GIFImageConfig *gifConfPtr, + Tcl_Interp *interp, unsigned char *imagePtr, + Tcl_Channel chan, int len, int rows, + unsigned char cmap[MAXCOLORMAPSIZE][4], int srcX, + int srcY, int interlace, int transparent); /* * these are for the BASE64 image reader code only */ -static int Fread _ANSI_ARGS_((unsigned char *dst, size_t size, - size_t count, Tcl_Channel chan)); -static int Mread _ANSI_ARGS_((unsigned char *dst, size_t size, - size_t count, MFile *handle)); -static int Mgetc _ANSI_ARGS_((MFile *handle)); -static int char64 _ANSI_ARGS_((int c)); -static void mInit _ANSI_ARGS_((unsigned char *string, - int length, MFile *handle)); - +static int Fread(GIFImageConfig *gifConfPtr, unsigned char *dst, + size_t size, size_t count, Tcl_Channel chan); +static int Mread(unsigned char *dst, size_t size, size_t count, + MFile *handle); +static int Mgetc(MFile *handle); +static int char64(int c); +static void mInit(unsigned char *string, MFile *handle, + int length); /* *---------------------------------------------------------------------- * * FileMatchGIF -- * - * This procedure is invoked by the photo image type to see if - * a file contains image data in GIF format. + * This function is invoked by the photo image type to see if a file + * contains image data in GIF format. * * Results: - * The return value is 1 if the first characters in file f look - * like GIF data, and 0 otherwise. + * The return value is 1 if the first characters in file f look like GIF + * data, and 0 otherwise. * * Side effects: * The access position in f may change. @@ -194,16 +206,19 @@ static void mInit _ANSI_ARGS_((unsigned char *string, */ static int -FileMatchGIF(chan, fileName, format, widthPtr, heightPtr, interp) - Tcl_Channel chan; /* The image file, open for reading. */ - CONST char *fileName; /* The name of the image file. */ - Tcl_Obj *format; /* User-specified format object, or NULL. */ - int *widthPtr, *heightPtr; /* The dimensions of the image are - * returned here if the file is a valid - * raw GIF file. */ - Tcl_Interp *interp; /* not used */ +FileMatchGIF( + Tcl_Channel chan, /* The image file, open for reading. */ + const char *fileName, /* The name of the image file. */ + Tcl_Obj *format, /* User-specified format object, or NULL. */ + int *widthPtr, int *heightPtr, + /* The dimensions of the image are returned + * here if the file is a valid raw GIF file. */ + Tcl_Interp *interp) /* not used */ { - return ReadGIFHeader(chan, widthPtr, heightPtr); + GIFImageConfig gifConf; + + memset(&gifConf, 0, sizeof(GIFImageConfig)); + return ReadGIFHeader(&gifConf, chan, widthPtr, heightPtr); } /* @@ -211,88 +226,108 @@ FileMatchGIF(chan, fileName, format, widthPtr, heightPtr, interp) * * FileReadGIF -- * - * This procedure is called by the photo image type to read - * GIF format data from a file and write it into a given - * photo image. + * This function is called by the photo image type to read GIF format + * data from a file and write it into a given photo image. * * Results: - * A standard TCL completion code. If TCL_ERROR is returned - * then an error message is left in the interp's result. + * A standard TCL completion code. If TCL_ERROR is returned then an error + * message is left in the interp's result. * * Side effects: - * The access position in file f is changed, and new data is - * added to the image given by imageHandle. + * The access position in file f is changed, and new data is added to the + * image given by imageHandle. * *---------------------------------------------------------------------- */ static int -FileReadGIF(interp, chan, fileName, format, imageHandle, destX, destY, - width, height, srcX, srcY) - Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ - Tcl_Channel chan; /* The image file, open for reading. */ - CONST char *fileName; /* The name of the image file. */ - Tcl_Obj *format; /* User-specified format object, or NULL. */ - Tk_PhotoHandle imageHandle; /* The photo image to write into. */ - int destX, destY; /* Coordinates of top-left pixel in - * photo image to be written to. */ - int width, height; /* Dimensions of block of photo image to - * be written to. */ - int srcX, srcY; /* Coordinates of top-left pixel to be used - * in image being read. */ +FileReadGIF( + Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ + Tcl_Channel chan, /* The image file, open for reading. */ + const char *fileName, /* The name of the image file. */ + Tcl_Obj *format, /* User-specified format object, or NULL. */ + Tk_PhotoHandle imageHandle, /* The photo image to write into. */ + int destX, int destY, /* Coordinates of top-left pixel in photo + * image to be written to. */ + int width, int height, /* Dimensions of block of photo image to be + * written to. */ + int srcX, int srcY) /* Coordinates of top-left pixel to be used in + * image being read. */ { int fileWidth, fileHeight, imageWidth, imageHeight; - int nBytes, index = 0, argc = 0, i; + int nBytes, index = 0, argc = 0, i, result = TCL_ERROR; Tcl_Obj **objv; - Tk_PhotoImageBlock block; unsigned char buf[100]; unsigned char *trashBuffer = NULL; int bitPixel; unsigned char colorMap[MAXCOLORMAPSIZE][4]; int transparent = -1; - static CONST char *optionStrings[] = { - "-index", NULL + static const char *optionStrings[] = { + "-index", NULL }; + GIFImageConfig gifConf, *gifConfPtr = &gifConf; + + /* + * Decode the magic used to convey when we're sourcing data from a string + * source and not a file. + */ + + memset(gifConfPtr, 0, sizeof(GIFImageConfig)); + if (fileName == INLINE_DATA_BINARY || fileName == INLINE_DATA_BASE64) { + gifConfPtr->fromData = fileName; + fileName = "inline data"; + } + + /* + * Parse the format string to get options. + */ if (format && Tcl_ListObjGetElements(interp, format, &argc, &objv) != TCL_OK) { return TCL_ERROR; } for (i = 1; i < argc; i++) { - if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option name", 0, - &nBytes) != TCL_OK) { + if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option name", + 0, &nBytes) != TCL_OK) { return TCL_ERROR; } if (i == (argc-1)) { Tcl_AppendResult(interp, "no value given for \"", - Tcl_GetStringFromObj(objv[i], NULL), - "\" option", (char *) NULL); + Tcl_GetString(objv[i]), "\" option", NULL); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[++i], &index) != TCL_OK) { return TCL_ERROR; } } - if (!ReadGIFHeader(chan, &fileWidth, &fileHeight)) { - Tcl_AppendResult(interp, "couldn't read GIF header from file \"", + + /* + * Read the GIF file header and check for some sanity. + */ + + if (!ReadGIFHeader(gifConfPtr, chan, &fileWidth, &fileHeight)) { + Tcl_AppendResult(interp, "couldn't read GIF header from file \"", fileName, "\"", NULL); return TCL_ERROR; } if ((fileWidth <= 0) || (fileHeight <= 0)) { Tcl_AppendResult(interp, "GIF image file \"", fileName, - "\" has dimension(s) <= 0", (char *) NULL); + "\" has dimension(s) <= 0", NULL); return TCL_ERROR; } - if (Fread(buf, 1, 3, chan) != 3) { + /* + * Get the general colormap information. + */ + + if (Fread(gifConfPtr, buf, 1, 3, chan) != 3) { return TCL_OK; } - bitPixel = 2<<(buf[0]&0x07); + bitPixel = 2 << (buf[0] & 0x07); if (BitSet(buf[0], LOCALCOLORMAP)) { /* Global Colormap */ - if (!ReadColorMap(chan, bitPixel, colorMap)) { - Tcl_AppendResult(interp, "error reading color map", - (char *) NULL); + if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { + Tcl_AppendResult(interp, "error reading color map", NULL); return TCL_ERROR; } } @@ -308,85 +343,87 @@ FileReadGIF(interp, chan, fileName, format, imageHandle, destX, destY, return TCL_OK; } - Tk_PhotoExpand(imageHandle, destX + width, destY + height); + /* + * Make sure we have enough space in the photo image to hold the data from + * the GIF. + */ + + if (Tk_PhotoExpand(interp, imageHandle, + destX + width, destY + height) != TCL_OK) { + return TCL_ERROR; + } - block.width = width; - block.height = height; - block.pixelSize = 4; - block.pitch = block.pixelSize * block.width; - block.offset[0] = 0; - block.offset[1] = 1; - block.offset[2] = 2; - block.offset[3] = 3; - block.pixelPtr = NULL; + /* + * Search for the frame from the GIF to display. + */ while (1) { - if (Fread(buf, 1, 1, chan) != 1) { + if (Fread(gifConfPtr, buf, 1, 1, chan) != 1) { /* * Premature end of image. */ - Tcl_AppendResult(interp,"premature end of image data for this index", - (char *) NULL); + Tcl_AppendResult(interp, + "premature end of image data for this index", NULL); goto error; } - if (buf[0] == GIF_TERMINATOR) { - /* - * GIF terminator. - */ - - Tcl_AppendResult(interp,"no image data for this index", - (char *) NULL); + switch (buf[0]) { + case GIF_TERMINATOR: + Tcl_AppendResult(interp, "no image data for this index", NULL); goto error; - } - if (buf[0] == GIF_EXTENSION) { + case GIF_EXTENSION: /* * This is a GIF extension. */ - if (Fread(buf, 1, 1, chan) != 1) { + if (Fread(gifConfPtr, buf, 1, 1, chan) != 1) { Tcl_SetResult(interp, "error reading extension function code in GIF image", TCL_STATIC); goto error; } - if (DoExtension(chan, buf[0], &transparent) < 0) { + if (DoExtension(gifConfPtr, chan, buf[0], + gifConfPtr->workingBuffer, &transparent) < 0) { Tcl_SetResult(interp, "error reading extension in GIF image", TCL_STATIC); goto error; } continue; - } - - if (buf[0] != GIF_START) { + case GIF_START: + if (Fread(gifConfPtr, buf, 1, 9, chan) != 9) { + Tcl_SetResult(interp, + "couldn't read left/top/width/height in GIF image", + TCL_STATIC); + goto error; + } + break; + default: /* * Not a valid start character; ignore it. */ - continue; - } - if (Fread(buf, 1, 9, chan) != 9) { - Tcl_SetResult(interp, - "couldn't read left/top/width/height in GIF image", - TCL_STATIC); - goto error; + continue; } - imageWidth = LM_to_uint(buf[4],buf[5]); - imageHeight = LM_to_uint(buf[6],buf[7]); + /* + * We've read the header for a GIF frame. Work out what we are going + * to do about it. + */ - bitPixel = 1<<((buf[8]&0x07)+1); + imageWidth = LM_to_uint(buf[4], buf[5]); + imageHeight = LM_to_uint(buf[6], buf[7]); + bitPixel = 1 << ((buf[8] & 0x07) + 1); if (index--) { /* - * This is not the image we want to read: skip it. + * This is not the GIF frame we want to read: skip it. */ + if (BitSet(buf[8], LOCALCOLORMAP)) { - if (!ReadColorMap(chan, bitPixel, colorMap)) { - Tcl_AppendResult(interp, - "error reading color map", (char *) NULL); + if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { + Tcl_AppendResult(interp, "error reading color map", NULL); goto error; } } @@ -394,113 +431,126 @@ FileReadGIF(interp, chan, fileName, format, imageHandle, destX, destY, /* * If we've not yet allocated a trash buffer, do so now. */ + if (trashBuffer == NULL) { nBytes = fileWidth * fileHeight * 3; - trashBuffer = - (unsigned char *) ckalloc((unsigned int) nBytes); + trashBuffer = (unsigned char *) ckalloc((unsigned) nBytes); } /* - * Slurp! Process the data for this image and stuff it in - * a trash buffer. + * Slurp! Process the data for this image and stuff it in a trash + * buffer. * - * Yes, it might be more efficient here to *not* store the - * data (we're just going to throw it away later). - * However, I elected to implement it this way for good - * reasons. First, I wanted to avoid duplicating the - * (fairly complex) LWZ decoder in ReadImage. Fine, you - * say, why didn't you just modify it to allow the use of - * a NULL specifier for the output buffer? I tried that, - * but it negatively impacted the performance of what I - * think will be the common case: reading the first image - * in the file. Rather than marginally improve the speed - * of the less frequent case, I chose to maintain high - * performance for the common case. + * Yes, it might be more efficient here to *not* store the data + * (we're just going to throw it away later). However, I elected + * to implement it this way for good reasons. First, I wanted to + * avoid duplicating the (fairly complex) LWZ decoder in + * ReadImage. Fine, you say, why didn't you just modify it to + * allow the use of a NULL specifier for the output buffer? I + * tried that, but it negatively impacted the performance of what + * I think will be the common case: reading the first image in the + * file. Rather than marginally improve the speed of the less + * frequent case, I chose to maintain high performance for the + * common case. */ - if (ReadImage(interp, (char *) trashBuffer, chan, imageWidth, - imageHeight, colorMap, 0, 0, 0, 0, 0, -1) != TCL_OK) { + + if (ReadImage(gifConfPtr, interp, trashBuffer, chan, imageWidth, + imageHeight, colorMap, 0, 0, 0, -1) != TCL_OK) { goto error; } continue; } + break; + } - if (BitSet(buf[8], LOCALCOLORMAP)) { - if (!ReadColorMap(chan, bitPixel, colorMap)) { - Tcl_AppendResult(interp, "error reading color map", - (char *) NULL); - goto error; - } - } + /* + * Found the frame we want to read. Next, check for a local color map for + * this frame. + */ - index = LM_to_uint(buf[0],buf[1]); - srcX -= index; - if (srcX<0) { - destX -= srcX; width += srcX; - srcX = 0; + if (BitSet(buf[8], LOCALCOLORMAP)) { + if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { + Tcl_AppendResult(interp, "error reading color map", NULL); + goto error; } + } - if (width > imageWidth) { - width = imageWidth; - } + /* + * Extract the location within the overall visible image to put the data + * in this frame, together with the size of this frame. + */ - index = LM_to_uint(buf[2],buf[3]); - srcY -= index; - if (index > srcY) { - destY -= srcY; height += srcY; - srcY = 0; - } - if (height > imageHeight) { - height = imageHeight; - } + index = LM_to_uint(buf[0], buf[1]); + srcX -= index; + if (srcX<0) { + destX -= srcX; width += srcX; + srcX = 0; + } - if ((width <= 0) || (height <= 0)) { - block.pixelPtr = 0; - goto noerror; - } + if (width > imageWidth) { + width = imageWidth; + } + + index = LM_to_uint(buf[2], buf[3]); + srcY -= index; + if (index > srcY) { + destY -= srcY; height += srcY; + srcY = 0; + } + if (height > imageHeight) { + height = imageHeight; + } + + if ((width > 0) && (height > 0)) { + Tk_PhotoImageBlock block; + + /* + * Read the data and put it into the photo buffer for display by the + * general image machinery. + */ block.width = width; block.height = height; block.pixelSize = (transparent>=0) ? 4 : 3; + block.offset[0] = 0; + block.offset[1] = 1; + block.offset[2] = 2; block.offset[3] = (transparent>=0) ? 3 : 0; block.pitch = block.pixelSize * imageWidth; nBytes = block.pitch * imageHeight; block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); - if (ReadImage(interp, (char *) block.pixelPtr, chan, imageWidth, - imageHeight, colorMap, fileWidth, fileHeight, srcX, srcY, - BitSet(buf[8], INTERLACE), transparent) != TCL_OK) { + if (ReadImage(gifConfPtr, interp, block.pixelPtr, chan, imageWidth, + imageHeight, colorMap, srcX, srcY, BitSet(buf[8],INTERLACE), + transparent) != TCL_OK) { + ckfree((char *) block.pixelPtr); goto error; } - break; + if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY, + width, height, TK_PHOTO_COMPOSITE_SET) != TCL_OK) { + ckfree((char *) block.pixelPtr); + goto error; + } + ckfree((char *) block.pixelPtr); } - Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height, - TK_PHOTO_COMPOSITE_SET); - - noerror: /* - * If a trash buffer has been allocated, free it now. + * We've successfully read the GIF frame (or there was nothing to read, + * which suits as well). We're done. */ - if (trashBuffer != NULL) { - ckfree((char *)trashBuffer); - } - if (block.pixelPtr) { - ckfree((char *) block.pixelPtr); - } - Tcl_AppendResult(interp, tkImgFmtGIF.name, (char *) NULL); - return TCL_OK; - error: + Tcl_AppendResult(interp, tkImgFmtGIF.name, NULL); + result = TCL_OK; + + error: /* * If a trash buffer has been allocated, free it now. */ + if (trashBuffer != NULL) { - ckfree((char *)trashBuffer); - } - if (block.pixelPtr) { - ckfree((char *) block.pixelPtr); + ckfree((char *) trashBuffer); } - return TCL_ERROR; + return result; } /* @@ -508,26 +558,26 @@ FileReadGIF(interp, chan, fileName, format, imageHandle, destX, destY, * * StringMatchGIF -- * - * This procedure is invoked by the photo image type to see if - * an object contains image data in GIF format. + * This function is invoked by the photo image type to see if an object + * contains image data in GIF format. * * Results: - * The return value is 1 if the first characters in the data are - * like GIF data, and 0 otherwise. + * The return value is 1 if the first characters in the data are like GIF + * data, and 0 otherwise. * * Side effects: - * the size of the image is placed in widthPre and heightPtr. + * The size of the image is placed in widthPtr and heightPtr. * *---------------------------------------------------------------------- */ static int -StringMatchGIF(dataObj, format, widthPtr, heightPtr, interp) - Tcl_Obj *dataObj; /* the object containing the image data */ - Tcl_Obj *format; /* the image format object, or NULL */ - int *widthPtr; /* where to put the string width */ - int *heightPtr; /* where to put the string height */ - Tcl_Interp *interp; /* not used */ +StringMatchGIF( + Tcl_Obj *dataObj, /* the object containing the image data */ + Tcl_Obj *format, /* the image format object, or NULL */ + int *widthPtr, /* where to put the string width */ + int *heightPtr, /* where to put the string height */ + Tcl_Interp *interp) /* not used */ { unsigned char *data, header[10]; int got, length; @@ -538,6 +588,7 @@ StringMatchGIF(dataObj, format, widthPtr, heightPtr, interp) /* * Header is a minimum of 10 bytes. */ + if (length < 10) { return 0; } @@ -546,97 +597,99 @@ StringMatchGIF(dataObj, format, widthPtr, heightPtr, interp) * Check whether the data is Base64 encoded. */ - if ((strncmp(GIF87a, (char *) data, 6) != 0) && + if ((strncmp(GIF87a, (char *) data, 6) != 0) && (strncmp(GIF89a, (char *) data, 6) != 0)) { /* * Try interpreting the data as Base64 encoded */ - mInit((unsigned char *) data, length, &handle); + + mInit((unsigned char *) data, &handle, length); got = Mread(header, 10, 1, &handle); - if (got != 10 - || ((strncmp(GIF87a, (char *) header, 6) != 0) + if (got != 10 || + ((strncmp(GIF87a, (char *) header, 6) != 0) && (strncmp(GIF89a, (char *) header, 6) != 0))) { return 0; } } else { - memcpy((VOID *) header, (VOID *) data, 10); + memcpy(header, data, 10); } - *widthPtr = LM_to_uint(header[6],header[7]); - *heightPtr = LM_to_uint(header[8],header[9]); + *widthPtr = LM_to_uint(header[6], header[7]); + *heightPtr = LM_to_uint(header[8], header[9]); return 1; } /* *---------------------------------------------------------------------- * - * StringReadGif -- -- + * StringReadGIF -- * - * This procedure is called by the photo image type to read - * GIF format data from an object, optionally base64 encoded, - * and give it to the photo image. + * This function is called by the photo image type to read GIF format + * data from an object, optionally base64 encoded, and give it to the + * photo image. * * Results: - * A standard TCL completion code. If TCL_ERROR is returned - * then an error message is left in the interp's result. + * A standard TCL completion code. If TCL_ERROR is returned then an error + * message is left in the interp's result. * * Side effects: - * new data is added to the image given by imageHandle. This - * procedure calls FileReadGif by redefining the operation of - * fprintf temporarily. + * New data is added to the image given by imageHandle. This function + * calls FileReadGIF by redefining the operation of fprintf temporarily. * *---------------------------------------------------------------------- */ static int -StringReadGIF(interp, dataObj, format, imageHandle, - destX, destY, width, height, srcX, srcY) - Tcl_Interp *interp; /* interpreter for reporting errors in */ - Tcl_Obj *dataObj; /* object containing the image */ - Tcl_Obj *format; /* format object, or NULL */ - Tk_PhotoHandle imageHandle; /* the image to write this data into */ - int destX, destY; /* The rectangular region of the */ - int width, height; /* image to copy */ - int srcX, srcY; +StringReadGIF( + Tcl_Interp *interp, /* interpreter for reporting errors in */ + Tcl_Obj *dataObj, /* object containing the image */ + Tcl_Obj *format, /* format object, or NULL */ + Tk_PhotoHandle imageHandle, /* the image to write this data into */ + int destX, int destY, /* The rectangular region of the */ + int width, int height, /* image to copy */ + int srcX, int srcY) { - int result, length; - MFile handle; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - Tcl_Channel dataSrc; - char *data; + MFile handle, *hdlPtr = &handle; + int length; + const char *xferFormat; + unsigned char *data = Tcl_GetByteArrayFromObj(dataObj, &length); + + mInit(data, hdlPtr, length); /* - * Check whether the data is Base64 encoded + * Check whether the data is Base64 encoded by doing a character-by- + * charcter comparison with the binary-format headers; BASE64-encoded + * never matches (matching the other way is harder because of potential + * padding of the BASE64 data). */ - data = (char *) Tcl_GetByteArrayFromObj(dataObj, &length); - if ((strncmp(GIF87a, data, 6) != 0) && (strncmp(GIF89a, data, 6) != 0)) { - mInit((unsigned char *)data, length, &handle); - tsdPtr->fromData = 1; - dataSrc = (Tcl_Channel) &handle; + + if (strncmp(GIF87a, (char *) data, 6) + && strncmp(GIF89a, (char *) data, 6)) { + xferFormat = INLINE_DATA_BASE64; } else { - tsdPtr->fromData = 2; - mInit((unsigned char *)data, length, &handle); - dataSrc = (Tcl_Channel) &handle; + xferFormat = INLINE_DATA_BINARY; } - result = FileReadGIF(interp, dataSrc, "inline data", - format, imageHandle, destX, destY, width, height, srcX, srcY); - tsdPtr->fromData = 0; - return result; -} + /* + * Fall through to the file reader now that we have a correctly-configured + * pseudo-channel to pull the data from. + */ + + return FileReadGIF(interp, (Tcl_Channel) hdlPtr, xferFormat, format, + imageHandle, destX, destY, width, height, srcX, srcY); +} + /* *---------------------------------------------------------------------- * * ReadGIFHeader -- * - * This procedure reads the GIF header from the beginning of a - * GIF file and returns the dimensions of the image. + * This function reads the GIF header from the beginning of a GIF file + * and returns the dimensions of the image. * * Results: - * The return value is 1 if file "f" appears to start with - * a valid GIF header, 0 otherwise. If the header is valid, - * then *widthPtr and *heightPtr are modified to hold the - * dimensions of the image. + * The return value is 1 if file "f" appears to start with a valid GIF + * header, 0 otherwise. If the header is valid, then *widthPtr and + * *heightPtr are modified to hold the dimensions of the image. * * Side effects: * The access position in f advances. @@ -645,68 +698,70 @@ StringReadGIF(interp, dataObj, format, imageHandle, */ static int -ReadGIFHeader(chan, widthPtr, heightPtr) - Tcl_Channel chan; /* Image file to read the header from */ - int *widthPtr, *heightPtr; /* The dimensions of the image are - * returned here. */ +ReadGIFHeader( + GIFImageConfig *gifConfPtr, + Tcl_Channel chan, /* Image file to read the header from */ + int *widthPtr, int *heightPtr) + /* The dimensions of the image are returned + * here. */ { unsigned char buf[7]; - if ((Fread(buf, 1, 6, chan) != 6) + if ((Fread(gifConfPtr, buf, 1, 6, chan) != 6) || ((strncmp(GIF87a, (char *) buf, 6) != 0) && (strncmp(GIF89a, (char *) buf, 6) != 0))) { return 0; } - if (Fread(buf, 1, 4, chan) != 4) { + if (Fread(gifConfPtr, buf, 1, 4, chan) != 4) { return 0; } - *widthPtr = LM_to_uint(buf[0],buf[1]); - *heightPtr = LM_to_uint(buf[2],buf[3]); + *widthPtr = LM_to_uint(buf[0], buf[1]); + *heightPtr = LM_to_uint(buf[2], buf[3]); return 1; } - + /* *----------------------------------------------------------------- - * The code below is copied from the giftoppm program and modified - * just slightly. + * The code below is copied from the giftoppm program and modified just + * slightly. *----------------------------------------------------------------- */ static int -ReadColorMap(chan, number, buffer) - Tcl_Channel chan; - int number; - unsigned char buffer[MAXCOLORMAPSIZE][4]; +ReadColorMap( + GIFImageConfig *gifConfPtr, + Tcl_Channel chan, + int number, + unsigned char buffer[MAXCOLORMAPSIZE][4]) { int i; unsigned char rgb[3]; for (i = 0; i < number; ++i) { - if (! ReadOK(chan, rgb, sizeof(rgb))) { + if (Fread(gifConfPtr, rgb, sizeof(rgb), 1, chan) <= 0) { return 0; } if (buffer) { - buffer[i][CM_RED] = rgb[0] ; - buffer[i][CM_GREEN] = rgb[1] ; - buffer[i][CM_BLUE] = rgb[2] ; - buffer[i][CM_ALPHA] = 255 ; + buffer[i][CM_RED] = rgb[0]; + buffer[i][CM_GREEN] = rgb[1]; + buffer[i][CM_BLUE] = rgb[2]; + buffer[i][CM_ALPHA] = 255; } } return 1; } - - - + static int -DoExtension(chan, label, transparent) - Tcl_Channel chan; - int label; - int *transparent; +DoExtension( + GIFImageConfig *gifConfPtr, + Tcl_Channel chan, + int label, + unsigned char *buf, + int *transparent) { - static unsigned char buf[256]; int count; switch (label) { @@ -718,12 +773,12 @@ DoExtension(chan, label, transparent) case 0xfe: /* Comment Extension */ do { - count = GetDataBlock(chan, (unsigned char*) buf); + count = GetDataBlock(gifConfPtr, chan, buf); } while (count > 0); return count; case 0xf9: /* Graphic Control Extension */ - count = GetDataBlock(chan, (unsigned char*) buf); + count = GetDataBlock(gifConfPtr, chan, buf); if (count < 0) { return 1; } @@ -732,53 +787,52 @@ DoExtension(chan, label, transparent) } do { - count = GetDataBlock(chan, (unsigned char*) buf); + count = GetDataBlock(gifConfPtr, chan, buf); } while (count > 0); return count; } do { - count = GetDataBlock(chan, (unsigned char*) buf); + count = GetDataBlock(gifConfPtr, chan, buf); } while (count > 0); return count; } - + static int -GetDataBlock(chan, buf) - Tcl_Channel chan; - unsigned char *buf; +GetDataBlock( + GIFImageConfig *gifConfPtr, + Tcl_Channel chan, + unsigned char *buf) { unsigned char count; - if (! ReadOK(chan, &count,1)) { + if (Fread(gifConfPtr, &count, 1, 1, chan) <= 0) { return -1; } - if ((count != 0) && (! ReadOK(chan, buf, count))) { + if ((count != 0) && (Fread(gifConfPtr, buf, count, 1, chan) <= 0)) { return -1; } return count; } - - /* *---------------------------------------------------------------------- * * ReadImage -- * - * Process a GIF image from a given source, with a given height, - * width, transparency, etc. + * Process a GIF image from a given source, with a given height, width, + * transparency, etc. * * This code is based on the code found in the ImageMagick GIF decoder, * which is (c) 2000 ImageMagick Studio. * * Some thoughts on our implementation: - * It sure would be nice if ReadImage didn't take 11 parameters! I think + * It sure would be nice if ReadImage didn't take 11 parameters! I think * that if we were smarter, we could avoid doing that. * - * Possible further optimizations: we could pull the GetCode function + * Possible further optimizations: we could pull the GetCode function * directly into ReadImage, which would improve our speed. * * Results: @@ -791,37 +845,36 @@ GetDataBlock(chan, buf) */ static int -ReadImage(interp, imagePtr, chan, len, rows, cmap, - width, height, srcX, srcY, interlace, transparent) - Tcl_Interp *interp; - char *imagePtr; - Tcl_Channel chan; - int len, rows; - unsigned char cmap[MAXCOLORMAPSIZE][4]; - int width, height; - int srcX, srcY; - int interlace; - int transparent; +ReadImage( + GIFImageConfig *gifConfPtr, + Tcl_Interp *interp, + unsigned char *imagePtr, + Tcl_Channel chan, + int len, int rows, + unsigned char cmap[MAXCOLORMAPSIZE][4], + int srcX, int srcY, + int interlace, + int transparent) { unsigned char initialCodeSize; - int v; int xpos = 0, ypos = 0, pass = 0, i; - register char *pixelPtr; - CONST static int interlaceStep[] = { 8, 8, 4, 2 }; - CONST static int interlaceStart[] = { 0, 4, 2, 1 }; + register unsigned char *pixelPtr; + static const int interlaceStep[] = { 8, 8, 4, 2 }; + static const int interlaceStart[] = { 0, 4, 2, 1 }; unsigned short prefix[(1 << MAX_LWZ_BITS)]; - unsigned char append[(1 << MAX_LWZ_BITS)]; - unsigned char stack[(1 << MAX_LWZ_BITS)*2]; + unsigned char append[(1 << MAX_LWZ_BITS)]; + unsigned char stack[(1 << MAX_LWZ_BITS)*2]; register unsigned char *top; int codeSize, clearCode, inCode, endCode, oldCode, maxCode; - int code, firstCode; + int code, firstCode, v; /* - * Initialize the decoder + * Initialize the decoder */ - if (! ReadOK(chan, &initialCodeSize, 1)) { + + if (Fread(gifConfPtr, &initialCodeSize, 1, 1, chan) <= 0) { Tcl_AppendResult(interp, "error reading GIF image: ", - Tcl_PosixError(interp), (char *) NULL); + Tcl_PosixError(interp), NULL); return TCL_ERROR; } @@ -848,46 +901,49 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, * code size size of the next code to retrieve * max code next available table position */ + clearCode = 1 << (int) initialCodeSize; endCode = clearCode + 1; codeSize = (int) initialCodeSize + 1; maxCode = clearCode + 2; oldCode = -1; firstCode = -1; - - memset((void *)prefix, 0, (1 << MAX_LWZ_BITS) * sizeof(short)); - memset((void *)append, 0, (1 << MAX_LWZ_BITS) * sizeof(char)); + + memset(prefix, 0, (1 << MAX_LWZ_BITS) * sizeof(short)); + memset(append, 0, (1 << MAX_LWZ_BITS) * sizeof(char)); for (i = 0; i < clearCode; i++) { append[i] = i; } top = stack; - GetCode(chan, 0, 1); + GetCode(chan, 0, 1, gifConfPtr); /* * Read until we finish the image */ + for (i = 0, ypos = 0; i < rows; i++) { for (xpos = 0; xpos < len; ) { - if (top == stack) { /* - * Bummer -- our stack is empty. Now we have to work! + * Bummer - our stack is empty. Now we have to work! */ - code = GetCode(chan, codeSize, 0); + + code = GetCode(chan, codeSize, 0, gifConfPtr); if (code < 0) { return TCL_OK; } if (code > maxCode || code == endCode) { /* - * If we're doing things right, we should never - * receive a code that is greater than our current - * maximum code. If we do, bail, because our decoder - * does not yet have that code set up. + * If we're doing things right, we should never receive a + * code that is greater than our current maximum code. If + * we do, bail, because our decoder does not yet have that + * code set up. * * If the code is the magic endCode value, quit. */ + return TCL_OK; } @@ -895,44 +951,48 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, /* * Reset the decoder. */ + codeSize = initialCodeSize + 1; maxCode = clearCode + 2; oldCode = -1; continue; } - + if (oldCode == -1) { /* - * Last pass reset the decoder, so the first code we - * see must be a singleton. Seed the stack with it, - * and set up the old/first code pointers for - * insertion into the string table. We can't just - * roll this into the clearCode test above, because - * at that point we have not yet read the next code. + * Last pass reset the decoder, so the first code we see + * must be a singleton. Seed the stack with it, and set up + * the old/first code pointers for insertion into the + * string table. We can't just roll this into the + * clearCode test above, because at that point we have not + * yet read the next code. */ + *top++ = append[code]; oldCode = code; firstCode = code; continue; } - + inCode = code; if (code == maxCode) { /* * maxCode is always one bigger than our highest assigned - * code. If the code we see is equal to maxCode, then - * we are about to add a new string to the table. ??? + * code. If the code we see is equal to maxCode, then we + * are about to add a new string to the table. ??? */ + *top++ = firstCode; code = oldCode; } while (code > clearCode) { /* - * Populate the stack by tracing the string in the - * string table from its tail to its head + * Populate the stack by tracing the string in the string + * table from its tail to its head */ + *top++ = append[code]; code = prefix[code]; } @@ -942,6 +1002,7 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, * If there's no more room in our string table, quit. * Otherwise, add a new string to the table */ + if (maxCode >= (1 << MAX_LWZ_BITS)) { return TCL_OK; } @@ -949,21 +1010,24 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, /* * Push the head of the string onto the stack. */ + *top++ = firstCode; /* * Add a new string to the string table */ + prefix[maxCode] = oldCode; append[maxCode] = firstCode; maxCode++; /* - * maxCode tells us the maximum code value we can accept. - * If we see that we need more bits to represent it than - * we are requesting from the unpacker, we need to increase - * the number we ask for. + * maxCode tells us the maximum code value we can accept. If + * we see that we need more bits to represent it than we are + * requesting from the unpacker, we need to increase the + * number we ask for. */ + if ((maxCode >= (1 << codeSize)) && (maxCode < (1<<MAX_LWZ_BITS))) { codeSize++; @@ -974,16 +1038,18 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, /* * Pop the next color index off the stack. */ + v = *(--top); if (v < 0) { return TCL_OK; } - /* + /* * If pixelPtr is null, we're skipping this image (presumably - * there are more in the file and we will be called to read - * one of them later) + * there are more in the file and we will be called to read one of + * them later) */ + *pixelPtr++ = cmap[v][CM_RED]; *pixelPtr++ = cmap[v][CM_GREEN]; *pixelPtr++ = cmap[v][CM_BLUE]; @@ -995,8 +1061,9 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, } /* - * If interlacing, the next ypos is not just +1 + * If interlacing, the next ypos is not just +1. */ + if (interlace) { ypos += interlaceStep[pass]; while (ypos >= rows) { @@ -1013,27 +1080,25 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, } return TCL_OK; } - /* *---------------------------------------------------------------------- * * GetCode -- * - * Extract the next compression code from the file. In GIF's, the - * compression codes are between 3 and 12 bits long and are then - * packed into 8 bit bytes, left to right, for example: + * Extract the next compression code from the file. In GIF's, the + * compression codes are between 3 and 12 bits long and are then packed + * into 8 bit bytes, left to right, for example: * bbbaaaaa * dcccccbb * eeeedddd * ... - * We use a byte buffer read from the file and a sliding window - * to unpack the bytes. Thanks to ImageMagick for the sliding window - * idea. + * We use a byte buffer read from the file and a sliding window to unpack + * the bytes. Thanks to ImageMagick for the sliding window idea. * args: chan the channel to read from * code_size size of the code to extract - * flag boolean indicating whether the extractor - * should be reset or not + * flag boolean indicating whether the extractor should be + * reset or not * * Results: * code the next compression code @@ -1045,69 +1110,72 @@ ReadImage(interp, imagePtr, chan, len, rows, cmap, */ static int -GetCode(chan, code_size, flag) - Tcl_Channel chan; - int code_size; - int flag; +GetCode( + Tcl_Channel chan, + int code_size, + int flag, + GIFImageConfig *gifConfPtr) { - static unsigned char buf[280]; - static int bytes = 0, done; - static unsigned char *c; - - static unsigned int window; - static int bitsInWindow = 0; int ret; - + if (flag) { /* * Initialize the decoder. */ - bitsInWindow = 0; - bytes = 0; - window = 0; - done = 0; - c = NULL; + + gifConfPtr->reader.bitsInWindow = 0; + gifConfPtr->reader.bytes = 0; + gifConfPtr->reader.window = 0; + gifConfPtr->reader.done = 0; + gifConfPtr->reader.c = NULL; return 0; } - while (bitsInWindow < code_size) { + while (gifConfPtr->reader.bitsInWindow < code_size) { /* * Not enough bits in our window to cover the request. */ - if (done) { + + if (gifConfPtr->reader.done) { return -1; } - if (bytes == 0) { + if (gifConfPtr->reader.bytes == 0) { /* * Not enough bytes in our buffer to add to the window. */ - bytes = GetDataBlock(chan, buf); - c = buf; - if (bytes <= 0) { - done = 1; + + gifConfPtr->reader.bytes = + GetDataBlock(gifConfPtr, chan, gifConfPtr->workingBuffer); + gifConfPtr->reader.c = gifConfPtr->workingBuffer; + if (gifConfPtr->reader.bytes <= 0) { + gifConfPtr->reader.done = 1; break; } } + /* * Tack another byte onto the window, see if that's enough. */ - window += (*c) << bitsInWindow; - c++; - bitsInWindow += 8; - bytes--; - } + gifConfPtr->reader.window += + (*gifConfPtr->reader.c) << gifConfPtr->reader.bitsInWindow; + gifConfPtr->reader.c++; + gifConfPtr->reader.bitsInWindow += 8; + gifConfPtr->reader.bytes--; + } /* * The next code will always be the last code_size bits of the window. */ - ret = window & ((1 << code_size) - 1); - + + ret = gifConfPtr->reader.window & ((1 << code_size) - 1); + /* * Shift data in the window to put the next code at the end. */ - window >>= code_size; - bitsInWindow -= code_size; + + gifConfPtr->reader.window >>= code_size; + gifConfPtr->reader.bitsInWindow -= code_size; return ret; } @@ -1116,27 +1184,27 @@ GetCode(chan, code_size, flag) * * Minit -- -- * - * This procedure initializes a base64 decoder handle + * This function initializes a base64 decoder handle * * Results: - * none + * None * * Side effects: - * the base64 handle is initialized + * The base64 handle is initialized * *---------------------------------------------------------------------- */ static void -mInit(string, length, handle) - unsigned char *string; /* string containing initial mmencoded data */ - int length; /* Length of string */ - MFile *handle; /* mmdecode "file" handle */ +mInit( + unsigned char *string, /* string containing initial mmencoded data */ + MFile *handle, /* mmdecode "file" handle */ + int length) /* Number of bytes in string */ { handle->data = string; - handle->length = length; handle->state = 0; handle->c = 0; + handle->length = length; } /* @@ -1144,9 +1212,9 @@ mInit(string, length, handle) * * Mread -- * - * This procedure is invoked by the GIF file reader as a - * temporary replacement for "fread", to get GIF data out - * of a string (using Mgetc). + * This function is invoked by the GIF file reader as a temporary + * replacement for "fread", to get GIF data out of a string (using + * Mgetc). * * Results: * The return value is the number of characters "read" @@ -1158,50 +1226,45 @@ mInit(string, length, handle) */ static int -Mread(dst, chunkSize, numChunks, handle) - unsigned char *dst; /* where to put the result */ - size_t chunkSize; /* size of each transfer */ - size_t numChunks; /* number of chunks */ - MFile *handle; /* mmdecode "file" handle */ +Mread( + unsigned char *dst, /* where to put the result */ + size_t chunkSize, /* size of each transfer */ + size_t numChunks, /* number of chunks */ + MFile *handle) /* mmdecode "file" handle */ { register int i, c; int count = chunkSize * numChunks; - for(i=0; i<count && (c=Mgetc(handle)) != GIF_DONE; i++) { + for (i=0; i<count && (c=Mgetc(handle)) != GIF_DONE; i++) { *dst++ = c; } return i; } - -/* - * get the next decoded character from an mmencode handle - * This causes at least 1 character to be "read" from the encoded string - */ /* *---------------------------------------------------------------------- * * Mgetc -- * - * This procedure decodes and returns the next byte from a base64 - * encoded string. + * This function gets the next decoded character from an mmencode handle. + * This causes at least 1 character to be "read" from the encoded string. * * Results: - * The next byte (or GIF_DONE) is returned. + * The next byte (or GIF_DONE) is returned. * * Side effects: - * The base64 handle will change state. + * The base64 handle will change state. * *---------------------------------------------------------------------- */ static int -Mgetc(handle) - MFile *handle; /* Handle containing decoder data and state */ +Mgetc( + MFile *handle) /* Handle containing decoder data and state */ { int c; - int result = 0; /* Initialization needed only to prevent - * gcc compiler warning. */ + int result = 0; /* Initialization needed only to prevent gcc + * compiler warning. */ if (handle->state == GIF_DONE) { return GIF_DONE; @@ -1209,7 +1272,6 @@ Mgetc(handle) do { if (handle->length-- <= 0) { - handle->state = GIF_DONE; return GIF_DONE; } c = char64(*handle->data); @@ -1247,21 +1309,22 @@ Mgetc(handle) * * char64 -- * - * This procedure converts a base64 ascii character into its binary - * equivalent. This code is a slightly modified version of the - * char64 proc in N. Borenstein's metamail decoder. + * This function converts a base64 ascii character into its binary + * equivalent. This code is a slightly modified version of the char64 + * function in N. Borenstein's metamail decoder. * * Results: * The binary value, or an error code. * * Side effects: * None. + * *---------------------------------------------------------------------- */ static int -char64(c) -int c; +char64( + int c) { switch(c) { case 'A': return 0; case 'B': return 1; case 'C': return 2; @@ -1303,52 +1366,53 @@ int c; * * Fread -- * - * This procedure calls either fread or Mread to read data - * from a file or a base64 encoded string. + * This function calls either fread or Mread to read data from a file or + * a base64 encoded string. * - * Results: - same as fread + * Results: - same as POSIX fread() or Tcl Tcl_Read() * *---------------------------------------------------------------------- */ static int -Fread(dst, hunk, count, chan) - unsigned char *dst; /* where to put the result */ - size_t hunk,count; /* how many */ - Tcl_Channel chan; +Fread( + GIFImageConfig *gifConfPtr, + unsigned char *dst, /* where to put the result */ + size_t hunk, size_t count, /* how many */ + Tcl_Channel chan) { - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - MFile *handle; - - switch (tsdPtr->fromData) { - case 1: + if (gifConfPtr->fromData == INLINE_DATA_BASE64) { return Mread(dst, hunk, count, (MFile *) chan); - case 2: - handle = (MFile *) chan; - if (handle->length <= 0 || (size_t)handle->length < (size_t) (hunk * count)) { + } + + if (gifConfPtr->fromData == INLINE_DATA_BINARY) { + MFile *handle = (MFile *) chan; + + if (handle->length <= 0 || (size_t) handle->length < hunk*count) { return -1; } - memcpy((VOID *)dst, (VOID *) handle->data, (size_t) (hunk * count)); + memcpy(dst, handle->data, (size_t) (hunk * count)); handle->data += hunk * count; - handle->length -= hunk * count; return (int)(hunk * count); - default: - return Tcl_Read(chan, (char *) dst, (int) (hunk * count)); } -} + /* + * Otherwise we've got a real file to read. + */ + + return Tcl_Read(chan, (char *) dst, (int) (hunk * count)); +} /* * ChanWriteGIF - writes a image in GIF format. *------------------------------------------------------------------------- - * Author: Lolo - * Engeneering Projects Area - * Department of Mining - * University of Oviedo - * e-mail zz11425958@zeus.etsimo.uniovi.es - * lolo@pcsig22.etsimo.uniovi.es - * Date: Fri September 20 1996 + * Author: Lolo + * Engeneering Projects Area + * Department of Mining + * University of Oviedo + * e-mail zz11425958@zeus.etsimo.uniovi.es + * lolo@pcsig22.etsimo.uniovi.es + * Date: Fri September 20 1996 * * Modified for transparency handling (gif89a) and miGIF compression * by Jan Nijtmans <j.nijtmans@chello.nl> @@ -1356,60 +1420,65 @@ Fread(dst, hunk, count, chan) *---------------------------------------------------------------------- * FileWriteGIF- * - * This procedure is called by the photo image type to write - * GIF format data from a photo image into a given file + * This function is called by the photo image type to write GIF format + * data from a photo image into a given file * * Results: - * A standard TCL completion code. If TCL_ERROR is returned - * then an error message is left in interp->result. + * A standard TCL completion code. If TCL_ERROR is returned then an error + * message is left in interp->result. * *---------------------------------------------------------------------- */ - /* - * Types, defines and variables needed to write and compress a GIF. - */ - -typedef int (* ifunptr) _ANSI_ARGS_((void)); - -#define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF)) -#define MSB(a) ((unsigned char) (((short)(a)) >> 8)) - -#define GIFBITS 12 -#define HSIZE 5003 /* 80% occupancy */ +/* + * Types, defines and variables needed to write and compress a GIF. + */ -static int ssize; -static int csize; -static int rsize; -static unsigned char *pixelo; -static int pixelSize; -static int pixelPitch; -static int greenOffset; -static int blueOffset; -static int alphaOffset; -static int num; -static unsigned char mapa[MAXCOLORMAPSIZE][3]; +typedef int (* ifunptr) (ClientData clientData); + +#define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF)) +#define MSB(a) ((unsigned char) (((short)(a)) >> 8)) + +#define GIFBITS 12 +#define HSIZE 5003 /* 80% occupancy */ + +typedef struct { + int ssize; + int csize; + int rsize; + unsigned char *pixelo; + int pixelSize; + int pixelPitch; + int greenOffset; + int blueOffset; + int alphaOffset; + int num; + unsigned char mapa[MAXCOLORMAPSIZE][3]; +} GifWriterState; /* - * Definition of new functions to write GIFs + * Definition of new functions to write GIFs */ -static int color _ANSI_ARGS_((int red,int green, int blue, - unsigned char mapa[MAXCOLORMAPSIZE][3])); -static void compress _ANSI_ARGS_((int init_bits, Tcl_Channel handle, - ifunptr readValue)); -static int nuevo _ANSI_ARGS_((int red, int green ,int blue, - unsigned char mapa[MAXCOLORMAPSIZE][3])); -static void savemap _ANSI_ARGS_((Tk_PhotoImageBlock *blockPtr, - unsigned char mapa[MAXCOLORMAPSIZE][3])); -static int ReadValue _ANSI_ARGS_((void)); - +static int color(GifWriterState *statePtr, + int red, int green, int blue, + unsigned char mapa[MAXCOLORMAPSIZE][3]); +static void compress(int initBits, Tcl_Channel handle, + ifunptr readValue, ClientData clientData); +static int nuevo(GifWriterState *statePtr, + int red, int green, int blue, + unsigned char mapa[MAXCOLORMAPSIZE][3]); +static void savemap(GifWriterState *statePtr, + Tk_PhotoImageBlock *blockPtr, + unsigned char mapa[MAXCOLORMAPSIZE][3]); +static int ReadValue(ClientData clientData); + static int -FileWriteGIF(interp, filename, format, blockPtr) - Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ - CONST char *filename; - Tcl_Obj *format; - Tk_PhotoImageBlock *blockPtr; +FileWriteGIF( + Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ + const char *filename, + Tcl_Obj *format, + Tk_PhotoImageBlock *blockPtr) { Tcl_Channel chan = NULL; int result; @@ -1418,167 +1487,168 @@ FileWriteGIF(interp, filename, format, blockPtr) if (!chan) { return TCL_ERROR; } - if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK) { + if (Tcl_SetChannelOption(interp, chan, "-translation", + "binary") != TCL_OK) { Tcl_Close(NULL, chan); return TCL_ERROR; } result = CommonWriteGIF(interp, chan, format, blockPtr); + if (Tcl_Close(interp, chan) == TCL_ERROR) { return TCL_ERROR; } return result; } - -#define Mputc(c,handle) Tcl_Write(handle,(char *) &c,1) - + static int -CommonWriteGIF(interp, handle, format, blockPtr) - Tcl_Interp *interp; - Tcl_Channel handle; - Tcl_Obj *format; - Tk_PhotoImageBlock *blockPtr; +CommonWriteGIF( + Tcl_Interp *interp, + Tcl_Channel handle, + Tcl_Obj *format, + Tk_PhotoImageBlock *blockPtr) { - int resolution; - - long width,height,x; + GifWriterState state, *statePtr = &state; + int resolution; + long width, height, x; unsigned char c; - unsigned int top,left; + unsigned int top, left; top = 0; left = 0; - pixelSize = blockPtr->pixelSize; - greenOffset = blockPtr->offset[1]-blockPtr->offset[0]; - blueOffset = blockPtr->offset[2]-blockPtr->offset[0]; - alphaOffset = blockPtr->offset[0]; - if (alphaOffset < blockPtr->offset[2]) { - alphaOffset = blockPtr->offset[2]; + memset(statePtr, 0, sizeof(state)); + + statePtr->pixelSize = blockPtr->pixelSize; + statePtr->greenOffset = blockPtr->offset[1]-blockPtr->offset[0]; + statePtr->blueOffset = blockPtr->offset[2]-blockPtr->offset[0]; + statePtr->alphaOffset = blockPtr->offset[0]; + if (statePtr->alphaOffset < blockPtr->offset[2]) { + statePtr->alphaOffset = blockPtr->offset[2]; } - if (++alphaOffset < pixelSize) { - alphaOffset -= blockPtr->offset[0]; + if (++statePtr->alphaOffset < statePtr->pixelSize) { + statePtr->alphaOffset -= blockPtr->offset[0]; } else { - alphaOffset = 0; + statePtr->alphaOffset = 0; } - Tcl_Write(handle, (char *) (alphaOffset ? GIF89a : GIF87a), 6); + Tcl_Write(handle, (char *) (statePtr->alphaOffset ? GIF89a : GIF87a), 6); for (x=0 ; x<MAXCOLORMAPSIZE ; x++) { - mapa[x][CM_RED] = 255; - mapa[x][CM_GREEN] = 255; - mapa[x][CM_BLUE] = 255; + statePtr->mapa[x][CM_RED] = 255; + statePtr->mapa[x][CM_GREEN] = 255; + statePtr->mapa[x][CM_BLUE] = 255; } - width = blockPtr->width; height = blockPtr->height; - pixelo = blockPtr->pixelPtr + blockPtr->offset[0]; - pixelPitch = blockPtr->pitch; - savemap(blockPtr,mapa); - if (num >= MAXCOLORMAPSIZE) { - Tcl_AppendResult(interp, "too many colors", (char *) NULL); + statePtr->pixelo = blockPtr->pixelPtr + blockPtr->offset[0]; + statePtr->pixelPitch = blockPtr->pitch; + savemap(statePtr, blockPtr, statePtr->mapa); + if (statePtr->num >= MAXCOLORMAPSIZE) { + Tcl_AppendResult(interp, "too many colors", NULL); return TCL_ERROR; } - if (num<2) { - num = 2; + if (statePtr->num<2) { + statePtr->num = 2; } c = LSB(width); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(width); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = LSB(height); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(height); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); resolution = 0; - while (num >> resolution) { + while (statePtr->num >> resolution) { resolution++; } c = 111 + resolution * 17; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); - num = 1 << resolution; + statePtr->num = 1 << resolution; /* - * background color + * Background color */ c = 0; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); /* - * zero for future expansion. + * Zero for future expansion. */ - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); - for (x=0 ; x<num ; x++) { - c = mapa[x][CM_RED]; - Mputc(c,handle); - c = mapa[x][CM_GREEN]; - Mputc(c,handle); - c = mapa[x][CM_BLUE]; - Mputc(c,handle); + for (x=0 ; x<statePtr->num ; x++) { + c = statePtr->mapa[x][CM_RED]; + Tcl_Write(handle, (char *) &c, 1); + c = statePtr->mapa[x][CM_GREEN]; + Tcl_Write(handle, (char *) &c, 1); + c = statePtr->mapa[x][CM_BLUE]; + Tcl_Write(handle, (char *) &c, 1); } /* * Write out extension for transparent colour index, if necessary. */ - if (alphaOffset) { + if (statePtr->alphaOffset) { c = GIF_EXTENSION; - Mputc(c, handle); + Tcl_Write(handle, (char *) &c, 1); Tcl_Write(handle, "\371\4\1\0\0\0", 7); } c = GIF_START; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = LSB(top); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(top); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = LSB(left); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(left); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = LSB(width); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(width); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = LSB(height); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = MSB(height); - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = 0; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); c = resolution; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); - ssize = rsize = blockPtr->width; - csize = blockPtr->height; - compress(resolution+1, handle, ReadValue); + statePtr->ssize = statePtr->rsize = blockPtr->width; + statePtr->csize = blockPtr->height; + compress(resolution+1, handle, ReadValue, (ClientData) statePtr); - c = 0; - Mputc(c,handle); + c = 0; + Tcl_Write(handle, (char *) &c, 1); c = GIF_TERMINATOR; - Mputc(c,handle); + Tcl_Write(handle, (char *) &c, 1); - return TCL_OK; + return TCL_OK; } - + static int -color(red, green, blue, mapa) - int red; - int green; - int blue; - unsigned char mapa[MAXCOLORMAPSIZE][3]; +color( + GifWriterState *statePtr, + int red, int green, int blue, + unsigned char mapa[MAXCOLORMAPSIZE][3]) { - int x; - for (x=(alphaOffset != 0) ; x<=MAXCOLORMAPSIZE ; x++) { + int x = (statePtr->alphaOffset != 0); + + for (; x<=MAXCOLORMAPSIZE ; x++) { if ((mapa[x][CM_RED] == red) && (mapa[x][CM_GREEN] == green) && (mapa[x][CM_BLUE] == blue)) { return x; @@ -1586,15 +1656,16 @@ color(red, green, blue, mapa) } return -1; } - static int -nuevo(red, green, blue, mapa) - int red,green,blue; - unsigned char mapa[MAXCOLORMAPSIZE][3]; +nuevo( + GifWriterState *statePtr, + int red, int green, int blue, + unsigned char mapa[MAXCOLORMAPSIZE][3]) { - int x = (alphaOffset != 0); - for (; x<=num ; x++) { + int x = (statePtr->alphaOffset != 0); + + for (; x<=statePtr->num ; x++) { if ((mapa[x][CM_RED] == red) && (mapa[x][CM_GREEN] == green) && (mapa[x][CM_BLUE] == blue)) { return 0; @@ -1602,74 +1673,76 @@ nuevo(red, green, blue, mapa) } return 1; } - + static void -savemap(blockPtr,mapa) - Tk_PhotoImageBlock *blockPtr; - unsigned char mapa[MAXCOLORMAPSIZE][3]; +savemap( + GifWriterState *statePtr, + Tk_PhotoImageBlock *blockPtr, + unsigned char mapa[MAXCOLORMAPSIZE][3]) { unsigned char *colores; - int x,y; - unsigned char red,green,blue; + int x, y; + unsigned char red, green, blue; - if (alphaOffset) { - num = 0; + if (statePtr->alphaOffset) { + statePtr->num = 0; mapa[0][CM_RED] = 0xd9; mapa[0][CM_GREEN] = 0xd9; mapa[0][CM_BLUE] = 0xd9; } else { - num = -1; + statePtr->num = -1; } - for(y=0 ; y<blockPtr->height ; y++) { - colores = blockPtr->pixelPtr + blockPtr->offset[0] - + y * blockPtr->pitch; - for(x=0 ; x<blockPtr->width ; x++) { - if (!alphaOffset || (colores[alphaOffset] != 0)) { + for (y=0 ; y<blockPtr->height ; y++) { + colores = blockPtr->pixelPtr + blockPtr->offset[0] + y*blockPtr->pitch; + for (x=0 ; x<blockPtr->width ; x++) { + if (!statePtr->alphaOffset || colores[statePtr->alphaOffset]!=0) { red = colores[0]; - green = colores[greenOffset]; - blue = colores[blueOffset]; - if (nuevo(red,green,blue,mapa)) { - num++; - if (num >= MAXCOLORMAPSIZE) { + green = colores[statePtr->greenOffset]; + blue = colores[statePtr->blueOffset]; + if (nuevo(statePtr, red, green, blue, mapa)) { + statePtr->num++; + if (statePtr->num >= MAXCOLORMAPSIZE) { return; } - mapa[num][CM_RED] = red; - mapa[num][CM_GREEN] = green; - mapa[num][CM_BLUE] = blue; + mapa[statePtr->num][CM_RED] = red; + mapa[statePtr->num][CM_GREEN] = green; + mapa[statePtr->num][CM_BLUE] = blue; } } - colores += pixelSize; + colores += statePtr->pixelSize; } } - return; } - + static int -ReadValue() +ReadValue( + ClientData clientData) { + GifWriterState *statePtr = (GifWriterState *) clientData; unsigned int col; - if (csize == 0) { + if (statePtr->csize == 0) { return EOF; } - if (alphaOffset && (pixelo[alphaOffset] == 0)) { + if (statePtr->alphaOffset && statePtr->pixelo[statePtr->alphaOffset]==0) { col = 0; } else { - col = color(pixelo[0], pixelo[greenOffset], pixelo[blueOffset], mapa); + col = color(statePtr, statePtr->pixelo[0], + statePtr->pixelo[statePtr->greenOffset], + statePtr->pixelo[statePtr->blueOffset], statePtr->mapa); } - pixelo += pixelSize; - if (--ssize <= 0) { - ssize = rsize; - csize--; - pixelo += pixelPitch - (rsize * pixelSize); + statePtr->pixelo += statePtr->pixelSize; + if (--statePtr->ssize <= 0) { + statePtr->ssize = statePtr->rsize; + statePtr->csize--; + statePtr->pixelo += statePtr->pixelPitch + - (statePtr->rsize * statePtr->pixelSize); } return col; } - - /* *----------------------------------------------------------------------- * @@ -1681,30 +1754,27 @@ ReadValue() * http://www.hasc.com * info@hasc.com * - * Permission to use, copy, modify, and distribute this software and - * its documentation for any purpose and without fee is hereby - * granted, provided that the above copyright notice appear in all - * copies and that both that copyright notice and this permission - * notice appear in supporting documentation. This software is - * provided "AS IS." The Hutchison Avenue Software Corporation - * disclaims all warranties, either express or implied, including but - * not limited to implied warranties of merchantability and fitness - * for a particular purpose, with respect to this code and + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "AS IS." The Hutchison Avenue + * Software Corporation disclaims all warranties, either express or implied, + * including but not limited to implied warranties of merchantability and + * fitness for a particular purpose, with respect to this code and * accompanying documentation. - * - * The miGIF compression routines do not, strictly speaking, generate - * files conforming to the GIF spec, since the image data is not - * LZW-compressed (this is the point: in order to avoid transgression - * of the Unisys patent on the LZW algorithm.) However, miGIF - * generates data streams that any reasonably sane LZW decompresser - * will decompress to what we want. - * - * miGIF compression uses run length encoding. It compresses - * horizontal runs of pixels of the same color. This type of - * compression gives good results on images with many runs, for - * example images with lines, text and solid shapes on a solid-colored - * background. It gives little or no compression on images with few - * runs, for example digital or scanned photos. + * + * The miGIF compression routines do not, strictly speaking, generate files + * conforming to the GIF spec, since the image data is not LZW-compressed + * (this is the point: in order to avoid transgression of the Unisys patent on + * the LZW algorithm.) However, miGIF generates data streams that any + * reasonably sane LZW decompresser will decompress to what we want. + * + * miGIF compression uses run length encoding. It compresses horizontal runs + * of pixels of the same color. This type of compression gives good results on + * images with many runs, for example images with lines, text and solid shapes + * on a solid-colored background. It gives little or no compression on images + * with few runs, for example digital or scanned photos. * * der Mouse * mouse@rodents.montreal.qc.ca @@ -1712,35 +1782,36 @@ ReadValue() * * ivo@hasc.com * - * The Graphics Interchange Format(c) is the Copyright property of - * CompuServe Incorporated. GIF(sm) is a Service Mark property of - * CompuServe Incorporated. + * The Graphics Interchange Format(c) is the Copyright property of CompuServe + * Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated. * *----------------------------------------------------------------------- */ -static int rl_pixel; -static int rl_basecode; -static int rl_count; -static int rl_table_pixel; -static int rl_table_max; -static int just_cleared; -static int out_bits; -static int out_bits_init; -static int out_count; -static int out_bump; -static int out_bump_init; -static int out_clear; -static int out_clear_init; -static int max_ocodes; -static int code_clear; -static int code_eof; -static unsigned int obuf; -static int obits; -static Tcl_Channel ofile; -static unsigned char oblock[256]; -static int oblen; - +typedef struct { + int runlengthPixel; + int runlengthBaseCode; + int runlengthCount; + int runlengthTablePixel; + int runlengthTableMax; + int justCleared; + int outputBits; + int outputBitsInit; + int outputCount; + int outputBump; + int outputBumpInit; + int outputClear; + int outputClearInit; + int maxOcodes; + int codeClear; + int codeEOF; + unsigned int obuf; + int obits; + Tcl_Channel ofile; + unsigned char oblock[256]; + int oblen; +} miGIFState_t; + /* * Used only when debugging GIF compression code */ @@ -1748,23 +1819,28 @@ static int oblen; #ifdef MIGIF_DEBUGGING_ENVARS -static int verbose_set = 0; +/* + * This debugging code is _absolutely_ not thread-safe. It's also not normally + * enabled either. + */ + +static int verboseSet = 0; static int verbose; -#define MIGIF_VERBOSE (verbose_set?verbose:set_verbose()) -#define DEBUGMSG(printf_args) if (MIGIF_VERBOSE) { printf printf_args; } +#define MIGIF_VERBOSE (verboseSet?verbose:setVerbose()) +#define DEBUGMSG(printfArgs) if (MIGIF_VERBOSE) { printf printfArgs; } static int -set_verbose(void) +setVerbose(void) { verbose = !!getenv("MIGIF_VERBOSE"); - verbose_set = 1; + verboseSet = 1; return verbose; } -static CONST char * -binformat(v, nbits) - unsigned int v; - int nbits; +static const char * +binformat( + unsigned int v, + int nbits) { static char bufs[8][64]; static int bhand = 0; @@ -1786,112 +1862,119 @@ binformat(v, nbits) *bp = '\0'; return &bufs[bhand][0]; } - -#else - -#define MIGIF_VERBOSE 0 -#define DEBUGMSG(printf_args) /* do nothing */ - +#else /* !MIGIF_DEBUGGING_ENVARS */ +#define DEBUGMSG(printfArgs) /* do nothing */ #endif - + static void -write_block() +writeBlock( + miGIFState_t *statePtr) { - int i; unsigned char c; +#ifdef MIGIF_DEBUGGING_ENVARS if (MIGIF_VERBOSE) { - printf("write_block %d:", oblen); - for (i=0 ; i<oblen ; i++) { - printf(" %02x", oblock[i]); + int i; + printf("writeBlock %d:", statePtr->oblen); + for (i=0 ; i<statePtr->oblen ; i++) { + printf(" %02x", statePtr->oblock[i]); } printf("\n"); } - c = oblen; - Tcl_Write(ofile, (char *) &c, 1); - Tcl_Write(ofile, (char *) &oblock[0], oblen); - oblen = 0; +#endif + c = statePtr->oblen; + Tcl_Write(statePtr->ofile, (char *) &c, 1); + Tcl_Write(statePtr->ofile, (char *) &statePtr->oblock[0], statePtr->oblen); + statePtr->oblen = 0; } - + static void -block_out(c) - unsigned char c; +blockOut( + miGIFState_t *statePtr, + unsigned c) { - DEBUGMSG(("block_out %s\n", binformat(c, 8))); - oblock[oblen++] = c; - if (oblen >= 255) { - write_block(); + DEBUGMSG(("blockOut %s\n", binformat(c, 8))); + statePtr->oblock[statePtr->oblen++] = (unsigned char) c; + if (statePtr->oblen >= 255) { + writeBlock(statePtr); } } - + static void -block_flush() +blockFlush( + miGIFState_t *statePtr) { - DEBUGMSG(("block_flush\n")); - if (oblen > 0) { - write_block(); + DEBUGMSG(("blockFlush\n")); + if (statePtr->oblen > 0) { + writeBlock(statePtr); } } - + static void -output(val) - int val; +output( + miGIFState_t *statePtr, + int val) { - DEBUGMSG(("output %s [%s %d %d]\n", binformat(val, out_bits), - binformat(obuf, obits), obits, out_bits)); - obuf |= val << obits; - obits += out_bits; - while (obits >= 8) { - block_out(UCHAR(obuf&0xff)); - obuf >>= 8; - obits -= 8; - } - DEBUGMSG(("output leaving [%s %d]\n", binformat(obuf, obits), obits)); + DEBUGMSG(("output %s [%s %d %d]\n", binformat(val, statePtr->outputBits), + binformat(statePtr->obuf, statePtr->obits), statePtr->obits, + statePtr->outputBits)); + statePtr->obuf |= val << statePtr->obits; + statePtr->obits += statePtr->outputBits; + while (statePtr->obits >= 8) { + blockOut(statePtr, statePtr->obuf & 0xff); + statePtr->obuf >>= 8; + statePtr->obits -= 8; + } + DEBUGMSG(("output leaving [%s %d]\n", + binformat(statePtr->obuf, statePtr->obits), statePtr->obits)); } - + static void -output_flush() +outputFlush( + miGIFState_t *statePtr) { - DEBUGMSG(("output_flush\n")); - if (obits > 0) { - block_out(UCHAR(obuf)); + DEBUGMSG(("outputFlush\n")); + if (statePtr->obits > 0) { + blockOut(statePtr, statePtr->obuf); } - block_flush(); + blockFlush(statePtr); } - + static void -did_clear() +didClear( + miGIFState_t *statePtr) { - DEBUGMSG(("did_clear\n")); - out_bits = out_bits_init; - out_bump = out_bump_init; - out_clear = out_clear_init; - out_count = 0; - rl_table_max = 0; - just_cleared = 1; + DEBUGMSG(("didClear\n")); + statePtr->outputBits = statePtr->outputBitsInit; + statePtr->outputBump = statePtr->outputBumpInit; + statePtr->outputClear = statePtr->outputClearInit; + statePtr->outputCount = 0; + statePtr->runlengthTableMax = 0; + statePtr->justCleared = 1; } - + static void -output_plain(c) - int c; +outputPlain( + miGIFState_t *statePtr, + int c) { - DEBUGMSG(("output_plain %s\n", binformat(c, out_bits))); - just_cleared = 0; - output(c); - out_count++; - if (out_count >= out_bump) { - out_bits++; - out_bump += 1 << (out_bits - 1); - } - if (out_count >= out_clear) { - output(code_clear); - did_clear(); + DEBUGMSG(("outputPlain %s\n", binformat(c, statePtr->outputBits))); + statePtr->justCleared = 0; + output(statePtr, c); + statePtr->outputCount++; + if (statePtr->outputCount >= statePtr->outputBump) { + statePtr->outputBits++; + statePtr->outputBump += 1 << (statePtr->outputBits - 1); + } + if (statePtr->outputCount >= statePtr->outputClear) { + output(statePtr, statePtr->codeClear); + didClear(statePtr); } } - + static unsigned int -isqrt(x) - unsigned int x; +isqrt( + unsigned int x) { unsigned int r; unsigned int v; @@ -1908,11 +1991,11 @@ isqrt(x) r = v; } } - -static unsigned int -compute_triangle_count(count, nrepcodes) - unsigned int count; - unsigned int nrepcodes; + +static int +computeTriangleCount( + unsigned int count, + unsigned int nrepcodes) { unsigned int perrep; unsigned int cost; @@ -1924,8 +2007,8 @@ compute_triangle_count(count, nrepcodes) count -= perrep; } if (count > 0) { - unsigned int n; - n = isqrt(count); + unsigned int n = isqrt(count); + while (n*(n+1) >= 2*count) { n--; } @@ -1934,204 +2017,233 @@ compute_triangle_count(count, nrepcodes) } cost += n; } - return cost; + return (int) cost + 1; } - + static void -max_out_clear() +maxOutputClear( + miGIFState_t *statePtr) { - out_clear = max_ocodes; + statePtr->outputClear = statePtr->maxOcodes; } - + static void -reset_out_clear() +resetOutputClear( + miGIFState_t *statePtr) { - out_clear = out_clear_init; - if (out_count >= out_clear) { - output(code_clear); - did_clear(); + statePtr->outputClear = statePtr->outputClearInit; + if (statePtr->outputCount >= statePtr->outputClear) { + output(statePtr, statePtr->codeClear); + didClear(statePtr); } } - + static void -rl_flush_fromclear(count) - int count; +runlengthFlushFromClear( + miGIFState_t *statePtr, + int count) { int n; - DEBUGMSG(("rl_flush_fromclear %d\n", count)); - max_out_clear(); - rl_table_pixel = rl_pixel; + DEBUGMSG(("runlengthFlushFromClear %d\n", count)); + maxOutputClear(statePtr); + statePtr->runlengthTablePixel = statePtr->runlengthPixel; n = 1; while (count > 0) { if (n == 1) { - rl_table_max = 1; - output_plain(rl_pixel); + statePtr->runlengthTableMax = 1; + outputPlain(statePtr, statePtr->runlengthPixel); count--; } else if (count >= n) { - rl_table_max = n; - output_plain(rl_basecode+n-2); + statePtr->runlengthTableMax = n; + outputPlain(statePtr, statePtr->runlengthBaseCode+n-2); count -= n; } else if (count == 1) { - rl_table_max++; - output_plain(rl_pixel); + statePtr->runlengthTableMax++; + outputPlain(statePtr, statePtr->runlengthPixel); count = 0; } else { - rl_table_max++; - output_plain(rl_basecode+count-2); + statePtr->runlengthTableMax++; + outputPlain(statePtr, statePtr->runlengthBaseCode+count-2); count = 0; } - if (out_count == 0) { + if (statePtr->outputCount == 0) { n = 1; } else { n++; } } - reset_out_clear(); - DEBUGMSG(("rl_flush_fromclear leaving table_max=%d\n", rl_table_max)); + resetOutputClear(statePtr); + DEBUGMSG(("runlengthFlushFromClear leaving tableMax=%d\n", + statePtr->runlengthTableMax)); } - + static void -rl_flush_clearorrep(count) - int count; +runlengthFlushClearOrRep( + miGIFState_t *statePtr, + int count) { int withclr; - DEBUGMSG(("rl_flush_clearorrep %d\n", count)); - withclr = 1 + compute_triangle_count(count, max_ocodes); + DEBUGMSG(("runlengthFlushClearOrRep %d\n", count)); + withclr = computeTriangleCount((unsigned) count, + (unsigned) statePtr->maxOcodes); if (withclr < count) { - output(code_clear); - did_clear(); - rl_flush_fromclear(count); + output(statePtr, statePtr->codeClear); + didClear(statePtr); + runlengthFlushFromClear(statePtr, count); } else { for (; count>0 ; count--) { - output_plain(rl_pixel); + outputPlain(statePtr, statePtr->runlengthPixel); } } } - + static void -rl_flush_withtable(count) - int count; +runlengthFlushWithTable( + miGIFState_t *statePtr, + int count) { int repmax; int repleft; int leftover; - DEBUGMSG(("rl_flush_withtable %d\n", count)); - repmax = count / rl_table_max; - leftover = count % rl_table_max; + DEBUGMSG(("runlengthFlushWithTable %d\n", count)); + repmax = count / statePtr->runlengthTableMax; + leftover = count % statePtr->runlengthTableMax; repleft = (leftover ? 1 : 0); - if (out_count+repmax+repleft > max_ocodes) { - repmax = max_ocodes - out_count; - leftover = count - (repmax * rl_table_max); - repleft = 1 + compute_triangle_count(leftover, max_ocodes); + if (statePtr->outputCount+repmax+repleft > statePtr->maxOcodes) { + repmax = statePtr->maxOcodes - statePtr->outputCount; + leftover = count - (repmax * statePtr->runlengthTableMax); + repleft = computeTriangleCount((unsigned) leftover, + (unsigned) statePtr->maxOcodes); } - DEBUGMSG(("rl_flush_withtable repmax=%d leftover=%d repleft=%d\n", + DEBUGMSG(("runlengthFlushWithTable repmax=%d leftover=%d repleft=%d\n", repmax, leftover, repleft)); - if (1+(int)compute_triangle_count(count, max_ocodes) < repmax+repleft) { - output(code_clear); - did_clear(); - rl_flush_fromclear(count); + if (computeTriangleCount((unsigned) count, (unsigned) statePtr->maxOcodes) + < repmax+repleft) { + output(statePtr, statePtr->codeClear); + didClear(statePtr); + runlengthFlushFromClear(statePtr, count); return; } - max_out_clear(); + maxOutputClear(statePtr); for (; repmax>0 ; repmax--) { - output_plain(rl_basecode + rl_table_max - 2); + outputPlain(statePtr, + statePtr->runlengthBaseCode + statePtr->runlengthTableMax - 2); } if (leftover) { - if (just_cleared) { - rl_flush_fromclear(leftover); + if (statePtr->justCleared) { + runlengthFlushFromClear(statePtr, leftover); } else if (leftover == 1) { - output_plain(rl_pixel); + outputPlain(statePtr, statePtr->runlengthPixel); } else { - output_plain(rl_basecode + leftover - 2); + outputPlain(statePtr, statePtr->runlengthBaseCode + leftover - 2); } } - reset_out_clear(); + resetOutputClear(statePtr); } - + static void -rl_flush() +runlengthFlush( + miGIFState_t *statePtr) { - DEBUGMSG(("rl_flush [ %d %d\n", rl_count, rl_pixel)); - if (rl_count == 1) { - output_plain(rl_pixel); - rl_count = 0; - DEBUGMSG(("rl_flush ]\n")); + DEBUGMSG(("runlengthFlush [ %d %d\n", statePtr->runlengthCount, + statePtr->runlengthPixel)); + if (statePtr->runlengthCount == 1) { + outputPlain(statePtr, statePtr->runlengthPixel); + statePtr->runlengthCount = 0; + DEBUGMSG(("runlengthFlush ]\n")); return; } - if (just_cleared) { - rl_flush_fromclear(rl_count); - } else if ((rl_table_max < 2) || (rl_table_pixel != rl_pixel)) { - rl_flush_clearorrep(rl_count); + if (statePtr->justCleared) { + runlengthFlushFromClear(statePtr, statePtr->runlengthCount); + } else if ((statePtr->runlengthTableMax < 2) + || (statePtr->runlengthTablePixel != statePtr->runlengthPixel)) { + runlengthFlushClearOrRep(statePtr, statePtr->runlengthCount); } else { - rl_flush_withtable(rl_count); + runlengthFlushWithTable(statePtr, statePtr->runlengthCount); } - DEBUGMSG(("rl_flush ]\n")); - rl_count = 0; + DEBUGMSG(("runlengthFlush ]\n")); + statePtr->runlengthCount = 0; } - - + static void -compress(init_bits, handle, readValue) - int init_bits; - Tcl_Channel handle; - ifunptr readValue; +compress( + int initBits, + Tcl_Channel handle, + ifunptr readValue, + ClientData clientData) { int c; + miGIFState_t state, *statePtr = &state; + + memset(statePtr, 0, sizeof(state)); + + statePtr->ofile = handle; + statePtr->obuf = 0; + statePtr->obits = 0; + statePtr->oblen = 0; + statePtr->codeClear = 1 << (initBits - 1); + statePtr->codeEOF = statePtr->codeClear + 1; + statePtr->runlengthBaseCode = statePtr->codeEOF + 1; + statePtr->outputBumpInit = (1 << (initBits - 1)) - 1; - ofile = handle; - obuf = 0; - obits = 0; - oblen = 0; - code_clear = 1 << (init_bits - 1); - code_eof = code_clear + 1; - rl_basecode = code_eof + 1; - out_bump_init = (1 << (init_bits - 1)) - 1; /* - * For images with a lot of runs, making out_clear_init larger - * will give better compression. + * For images with a lot of runs, making outputClearInit larger will give + * better compression. */ - out_clear_init = (init_bits <= 3) ? 9 : (out_bump_init-1); + + statePtr->outputClearInit = + (initBits <= 3) ? 9 : (statePtr->outputBumpInit-1); #ifdef MIGIF_DEBUGGING_ENVARS { - const char *ocienv; - ocienv = getenv("MIGIF_OUT_CLEAR_INIT"); + const char *ocienv = getenv("MIGIF_OUT_CLEAR_INIT"); + if (ocienv) { - out_clear_init = atoi(ocienv); - DEBUGMSG(("[overriding out_clear_init to %d]\n", out_clear_init)); + statePtr->outputClearInit = atoi(ocienv); + DEBUGMSG(("[overriding outputClearInit to %d]\n", + statePtr->outputClearInit)); } } #endif - out_bits_init = init_bits; - max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3); - did_clear(); - output(code_clear); - rl_count = 0; + statePtr->outputBitsInit = initBits; + statePtr->maxOcodes = + (1 << GIFBITS) - ((1 << (statePtr->outputBitsInit - 1)) + 3); + didClear(statePtr); + output(statePtr, statePtr->codeClear); + statePtr->runlengthCount = 0; while (1) { - c = readValue(); - if ((rl_count > 0) && (c != rl_pixel)) { - rl_flush(); + c = readValue(clientData); + if (statePtr->runlengthCount>0 && statePtr->runlengthPixel!=c) { + runlengthFlush(statePtr); } if (c == EOF) { break; } - if (rl_pixel == c) { - rl_count++; + if (statePtr->runlengthPixel == c) { + statePtr->runlengthCount++; } else { - rl_pixel = c; - rl_count = 1; + statePtr->runlengthPixel = c; + statePtr->runlengthCount = 1; } } - output(code_eof); - output_flush(); + output(statePtr, statePtr->codeEOF); + outputFlush(statePtr); } /* *----------------------------------------------------------------------- * - * End of miGIF section - See copyright notice at start of section. + * End of miGIF section - See copyright notice at start of section. * *----------------------------------------------------------------------- */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |