diff options
Diffstat (limited to 'tk8.6/generic/tkImgGIF.c')
-rw-r--r-- | tk8.6/generic/tkImgGIF.c | 2236 |
1 files changed, 0 insertions, 2236 deletions
diff --git a/tk8.6/generic/tkImgGIF.c b/tk8.6/generic/tkImgGIF.c deleted file mode 100644 index 7c4872b..0000000 --- a/tk8.6/generic/tkImgGIF.c +++ /dev/null @@ -1,2236 +0,0 @@ -/* - * 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. - * - * 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-2010 Donal K. Fellows - * - * 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: - * - * +--------------------------------------------------------------------+ - * | Copyright 1990, David Koblas. | - * | 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" without express or implied warranty. | - * +--------------------------------------------------------------------+ - */ - -#include "tkInt.h" - -/* - * 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) -#define GIF_PAD (GIF_SPECIAL+1) -#define GIF_SPACE (GIF_SPECIAL+2) -#define GIF_BAD (GIF_SPECIAL+3) -#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. - */ - -typedef struct mFile { - unsigned char *data; /* mmencoded source string */ - 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; - -/* - * 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. - */ - -static const char GIF87a[] = { /* ASCII GIF87a */ - 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x00 -}; -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 , */ - -/* - * 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. - */ - -#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; - -/* - * Type of a function used to do the writing to a file or buffer when - * serializing in the GIF format. - */ - -typedef int (WriteBytesFunc) (ClientData clientData, const char *bytes, - int byteCount); - -/* - * The format record for the GIF file format: - */ - -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 StringWriteGIF(Tcl_Interp *interp, Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr); -static int CommonWriteGIF(Tcl_Interp *interp, ClientData clientData, - WriteBytesFunc *writeProc, Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr); - -Tk_PhotoImageFormat tkImgFmtGIF = { - "gif", /* name */ - FileMatchGIF, /* fileMatchProc */ - StringMatchGIF, /* stringMatchProc */ - FileReadGIF, /* fileReadProc */ - StringReadGIF, /* stringReadProc */ - FileWriteGIF, /* fileWriteProc */ - StringWriteGIF, /* stringWriteProc */ - NULL -}; - -#define INTERLACE 0x40 -#define LOCALCOLORMAP 0x80 -#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) -#define MAXCOLORMAPSIZE 256 -#define CM_RED 0 -#define CM_GREEN 1 -#define CM_BLUE 2 -#define CM_ALPHA 3 -#define MAX_LWZ_BITS 12 -#define LM_to_uint(a,b) (((b)<<8)|(a)) - -/* - * Prototypes for local functions defined in this file: - */ - -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(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); - -/* - * Types, defines and variables needed to write and compress a GIF. - */ - -#define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF)) -#define MSB(a) ((unsigned char) (((short)(a)) >> 8)) - -#define GIFBITS 12 -#define HSIZE 5003 /* 80% occupancy */ - -#define DEFAULT_BACKGROUND_VALUE 0xD9 - -typedef struct { - int ssize; - int csize; - int rsize; - unsigned char *pixelOffset; - int pixelSize; - int pixelPitch; - int greenOffset; - int blueOffset; - int alphaOffset; - int num; - unsigned char mapa[MAXCOLORMAPSIZE][3]; -} GifWriterState; - -typedef int (* ifunptr) (GifWriterState *statePtr); - -/* - * Support for compression of GIFs. - */ - -#define MAXCODE(numBits) (((long) 1 << (numBits)) - 1) - -#ifdef SIGNED_COMPARE_SLOW -#define U(x) ((unsigned) (x)) -#else -#define U(x) (x) -#endif - -typedef struct { - int numBits; /* Number of bits/code. */ - long maxCode; /* Maximum code, given numBits. */ - int hashTable[HSIZE]; - unsigned int codeTable[HSIZE]; - long hSize; /* For dynamic table sizing. */ - - /* - * To save much memory, we overlay the table used by compress() with those - * used by decompress(). The tab_prefix table is the same size and type as - * the codeTable. The tab_suffix table needs 2**GIFBITS characters. We get - * this from the beginning of hashTable. The output stack uses the rest of - * hashTable, and contains characters. There is plenty of room for any - * possible stack (stack used to be 8000 characters). - */ - - int freeEntry; /* First unused entry. */ - - /* - * Block compression parameters. After all codes are used up, and - * compression rate changes, start over. - */ - - int clearFlag; - - int offset; - unsigned int inCount; /* Length of input */ - unsigned int outCount; /* # of codes output (for debugging) */ - - /* - * Algorithm: use open addressing double hashing (no chaining) on the - * prefix code / next character combination. We do a variant of Knuth's - * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - * secondary probe. Here, the modular division first probe is gives way to - * a faster exclusive-or manipulation. Also do block compression with an - * adaptive reset, whereby the code table is cleared when the compression - * ratio decreases, but after the table fills. The variable-length output - * codes are re-sized at this point, and a special CLEAR code is generated - * for the decompressor. Late addition: construct the table according to - * file size for noticeable speed improvement on small files. Please - * direct questions about this implementation to ames!jaw. - */ - - int initialBits; - ClientData destination; - WriteBytesFunc *writeProc; - - int clearCode; - int eofCode; - - unsigned long currentAccumulated; - int currentBits; - - /* - * Number of characters so far in this 'packet' - */ - - int accumulatedByteCount; - - /* - * Define the storage for the packet accumulator - */ - - unsigned char packetAccumulator[256]; -} GIFState_t; - -/* - * Definition of new functions to write GIFs - */ - -static int ColorNumber(GifWriterState *statePtr, - int red, int green, int blue); -static void Compress(int initBits, ClientData handle, - WriteBytesFunc *writeProc, ifunptr readValue, - GifWriterState *statePtr); -static int IsNewColor(GifWriterState *statePtr, - int red, int green, int blue); -static void SaveMap(GifWriterState *statePtr, - Tk_PhotoImageBlock *blockPtr); -static int ReadValue(GifWriterState *statePtr); -static WriteBytesFunc WriteToChannel; -static WriteBytesFunc WriteToByteArray; -static void Output(GIFState_t *statePtr, long code); -static void ClearForBlock(GIFState_t *statePtr); -static void ClearHashTable(GIFState_t *statePtr, int hSize); -static void CharInit(GIFState_t *statePtr); -static void CharOut(GIFState_t *statePtr, int c); -static void FlushChar(GIFState_t *statePtr); - -/* - *---------------------------------------------------------------------- - * - * FileMatchGIF -- - * - * 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. - * - * Side effects: - * The access position in f may change. - * - *---------------------------------------------------------------------- - */ - -static int -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 */ -{ - GIFImageConfig gifConf; - - memset(&gifConf, 0, sizeof(GIFImageConfig)); - return ReadGIFHeader(&gifConf, chan, widthPtr, heightPtr); -} - -/* - *---------------------------------------------------------------------- - * - * FileReadGIF -- - * - * 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. - * - * Side effects: - * The access position in file f is changed, and new data is added to the - * image given by imageHandle. - * - *---------------------------------------------------------------------- - */ - -static int -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; - unsigned int nBytes; - int index = 0, argc = 0, i, result = TCL_ERROR; - Tcl_Obj **objv; - unsigned char buf[100]; - unsigned char *trashBuffer = NULL; - int bitPixel; - unsigned char colorMap[MAXCOLORMAPSIZE][4]; - int transparent = -1; - static const char *const 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(colorMap, 0, MAXCOLORMAPSIZE*4); - 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++) { - int optionIdx; - if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings, - sizeof(char *), "option name", 0, &optionIdx) != TCL_OK) { - return TCL_ERROR; - } - if (i == (argc-1)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "no value given for \"%s\" option", - Tcl_GetString(objv[i]))); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "OPT_VALUE", NULL); - return TCL_ERROR; - } - if (Tcl_GetIntFromObj(interp, objv[++i], &index) != TCL_OK) { - return TCL_ERROR; - } - } - - /* - * Read the GIF file header and check for some sanity. - */ - - if (!ReadGIFHeader(gifConfPtr, chan, &fileWidth, &fileHeight)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't read GIF header from file \"%s\"", fileName)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "HEADER", NULL); - return TCL_ERROR; - } - if ((fileWidth <= 0) || (fileHeight <= 0)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "GIF image file \"%s\" has dimension(s) <= 0", fileName)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BOGUS_SIZE", NULL); - return TCL_ERROR; - } - - /* - * Get the general colormap information. - */ - - if (Fread(gifConfPtr, buf, 1, 3, chan) != 3) { - return TCL_OK; - } - bitPixel = 2 << (buf[0] & 0x07); - - if (BitSet(buf[0], LOCALCOLORMAP)) { /* Global Colormap */ - if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading color map", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "COLOR_MAP", NULL); - return TCL_ERROR; - } - } - - if ((srcX + width) > fileWidth) { - width = fileWidth - srcX; - } - if ((srcY + height) > fileHeight) { - height = fileHeight - srcY; - } - if ((width <= 0) || (height <= 0) - || (srcX >= fileWidth) || (srcY >= fileHeight)) { - return TCL_OK; - } - - /* - * 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; - } - - /* - * Search for the frame from the GIF to display. - */ - - while (1) { - if (Fread(gifConfPtr, buf, 1, 1, chan) != 1) { - /* - * Premature end of image. - */ - - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "premature end of image data for this index", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "PREMATURE_END", - NULL); - goto error; - } - - switch (buf[0]) { - case GIF_TERMINATOR: - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "no image data for this index", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "NO_DATA", NULL); - goto error; - - case GIF_EXTENSION: - /* - * This is a GIF extension. - */ - - if (Fread(gifConfPtr, buf, 1, 1, chan) != 1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading extension function code in GIF image", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT", - NULL); - goto error; - } - if (DoExtension(gifConfPtr, chan, buf[0], - gifConfPtr->workingBuffer, &transparent) < 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading extension in GIF image", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT", - NULL); - goto error; - } - continue; - case GIF_START: - if (Fread(gifConfPtr, buf, 1, 9, chan) != 9) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "couldn't read left/top/width/height in GIF image", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "DIMENSIONS", - NULL); - goto error; - } - break; - default: - /* - * Not a valid start character; ignore it. - */ - - continue; - } - - /* - * We've read the header for a GIF frame. Work out what we are going - * to do about it. - */ - - 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 GIF frame we want to read: skip it. - */ - - if (BitSet(buf[8], LOCALCOLORMAP)) { - if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading color map", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", - "COLOR_MAP", NULL); - goto error; - } - } - - /* - * If we've not yet allocated a trash buffer, do so now. - */ - - if (trashBuffer == NULL) { - if (fileWidth > (int)((UINT_MAX/3)/fileHeight)) { - goto error; - } - nBytes = fileWidth * fileHeight * 3; - trashBuffer = ckalloc(nBytes); - if (trashBuffer) { - memset(trashBuffer, 0, nBytes); - } - } - - /* - * 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. - */ - - if (ReadImage(gifConfPtr, interp, trashBuffer, chan, imageWidth, - imageHeight, colorMap, 0, 0, 0, -1) != TCL_OK) { - goto error; - } - continue; - } - break; - } - - /* - * Found the frame we want to read. Next, check for a local color map for - * this frame. - */ - - if (BitSet(buf[8], LOCALCOLORMAP)) { - if (!ReadColorMap(gifConfPtr, chan, bitPixel, colorMap)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "error reading color map", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "COLOR_MAP", NULL); - goto error; - } - } - - /* - * 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[0], buf[1]); - srcX -= index; - if (srcX<0) { - destX -= srcX; width += srcX; - srcX = 0; - } - - 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; - if (imageWidth > INT_MAX/block.pixelSize) { - goto error; - } - block.pitch = block.pixelSize * imageWidth; - if (imageHeight > (int)(UINT_MAX/block.pitch)) { - goto error; - } - nBytes = block.pitch * imageHeight; - block.pixelPtr = ckalloc(nBytes); - if (block.pixelPtr) { - memset(block.pixelPtr, 0, nBytes); - } - - if (ReadImage(gifConfPtr, interp, block.pixelPtr, chan, imageWidth, - imageHeight, colorMap, srcX, srcY, BitSet(buf[8], INTERLACE), - transparent) != TCL_OK) { - ckfree(block.pixelPtr); - goto error; - } - if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY, - width, height, TK_PHOTO_COMPOSITE_SET) != TCL_OK) { - ckfree(block.pixelPtr); - goto error; - } - ckfree(block.pixelPtr); - } - - /* - * We've successfully read the GIF frame (or there was nothing to read, - * which suits as well). We're done. - */ - - Tcl_SetObjResult(interp, Tcl_NewStringObj(tkImgFmtGIF.name, -1)); - result = TCL_OK; - - error: - /* - * If a trash buffer has been allocated, free it now. - */ - - if (trashBuffer != NULL) { - ckfree(trashBuffer); - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * StringMatchGIF -- - * - * 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. - * - * Side effects: - * The size of the image is placed in widthPtr and heightPtr. - * - *---------------------------------------------------------------------- - */ - -static int -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; - MFile handle; - - data = Tcl_GetByteArrayFromObj(dataObj, &length); - - /* - * Header is a minimum of 10 bytes. - */ - - if (length < 10) { - return 0; - } - - /* - * Check whether the data is Base64 encoded. - */ - - if ((strncmp(GIF87a, (char *) data, 6) != 0) && - (strncmp(GIF89a, (char *) data, 6) != 0)) { - /* - * Try interpreting the data as Base64 encoded - */ - - mInit((unsigned char *) data, &handle, length); - got = Mread(header, 10, 1, &handle); - if (got != 10 || - ((strncmp(GIF87a, (char *) header, 6) != 0) - && (strncmp(GIF89a, (char *) header, 6) != 0))) { - return 0; - } - } else { - memcpy(header, data, 10); - } - *widthPtr = LM_to_uint(header[6], header[7]); - *heightPtr = LM_to_uint(header[8], header[9]); - return 1; -} - -/* - *---------------------------------------------------------------------- - * - * StringReadGIF -- - * - * 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. - * - * Side effects: - * New data is added to the image given by imageHandle. This function - * calls FileReadGIF by redefining the operation of fprintf temporarily. - * - *---------------------------------------------------------------------- - */ - -static int -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) -{ - 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 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). - */ - - if (strncmp(GIF87a, (char *) data, 6) - && strncmp(GIF89a, (char *) data, 6)) { - xferFormat = INLINE_DATA_BASE64; - } else { - xferFormat = INLINE_DATA_BINARY; - } - - /* - * 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 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. - * - * Side effects: - * The access position in f advances. - * - *---------------------------------------------------------------------- - */ - -static int -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(gifConfPtr, buf, 1, 6, chan) != 6) - || ((strncmp(GIF87a, (char *) buf, 6) != 0) - && (strncmp(GIF89a, (char *) buf, 6) != 0))) { - return 0; - } - - 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]); - return 1; -} - -/* - *----------------------------------------------------------------- - * The code below is copied from the giftoppm program and modified just - * slightly. - *----------------------------------------------------------------- - */ - -static int -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 (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; - } - } - return 1; -} - -static int -DoExtension( - GIFImageConfig *gifConfPtr, - Tcl_Channel chan, - int label, - unsigned char *buf, - int *transparent) -{ - int count; - - switch (label) { - case 0x01: /* Plain Text Extension */ - break; - - case 0xff: /* Application Extension */ - break; - - case 0xfe: /* Comment Extension */ - do { - count = GetDataBlock(gifConfPtr, chan, buf); - } while (count > 0); - return count; - - case 0xf9: /* Graphic Control Extension */ - count = GetDataBlock(gifConfPtr, chan, buf); - if (count < 0) { - return 1; - } - if ((buf[0] & 0x1) != 0) { - *transparent = buf[3]; - } - - do { - count = GetDataBlock(gifConfPtr, chan, buf); - } while (count > 0); - return count; - } - - do { - count = GetDataBlock(gifConfPtr, chan, buf); - } while (count > 0); - return count; -} - -static int -GetDataBlock( - GIFImageConfig *gifConfPtr, - Tcl_Channel chan, - unsigned char *buf) -{ - unsigned char count; - - if (Fread(gifConfPtr, &count, 1, 1, chan) <= 0) { - return -1; - } - - 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. - * - * 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 - * that if we were smarter, we could avoid doing that. - * - * Possible further optimizations: we could pull the GetCode function - * directly into ReadImage, which would improve our speed. - * - * Results: - * Processes a GIF image and loads the pixel data into a memory array. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -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) -{ - unsigned char initialCodeSize; - int xpos = 0, ypos = 0, pass = 0, i; - 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]; - register unsigned char *top; - int codeSize, clearCode, inCode, endCode, oldCode, maxCode; - int code, firstCode, v; - - /* - * Initialize the decoder - */ - - if (Fread(gifConfPtr, &initialCodeSize, 1, 1, chan) <= 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "error reading GIF image: %s", Tcl_PosixError(interp))); - return TCL_ERROR; - } - - if (initialCodeSize > MAX_LWZ_BITS) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("malformed image", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "MALFORMED", NULL); - return TCL_ERROR; - } - - if (transparent != -1) { - cmap[transparent][CM_RED] = 0; - cmap[transparent][CM_GREEN] = 0; - cmap[transparent][CM_BLUE] = 0; - cmap[transparent][CM_ALPHA] = 0; - } - - pixelPtr = imagePtr; - - /* - * Initialize the decoder. - * - * Set values for "special" numbers: - * clear code reset the decoder - * end code stop decoding - * 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(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, 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! - */ - - 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 the code is the magic endCode value, quit. - */ - - return TCL_OK; - } - - if (code == clearCode) { - /* - * 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 - * codes 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 < (1 << MAX_LWZ_BITS))) { - /* - * 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 entry to the codes table. - */ - - *top++ = firstCode; - code = oldCode; - } - - while (code > clearCode) { - /* - * Populate the stack by tracing the code in the codes - * table from its tail to its head - */ - - *top++ = append[code]; - code = prefix[code]; - } - firstCode = append[code]; - - /* - * Push the head of the code onto the stack. - */ - - *top++ = firstCode; - - if (maxCode < (1 << MAX_LWZ_BITS)) { - /* - * If there's still room in our codes table, add a new entry. - * Otherwise don't, and keep using the current table. - * See DEFERRED CLEAR CODE IN LZW COMPRESSION in the GIF89a - * specification. - */ - - 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. - */ - - if ((maxCode >= (1 << codeSize)) - && (maxCode < (1<<MAX_LWZ_BITS))) { - codeSize++; - } - oldCode = inCode; - } - - /* - * 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) - */ - - *pixelPtr++ = cmap[v][CM_RED]; - *pixelPtr++ = cmap[v][CM_GREEN]; - *pixelPtr++ = cmap[v][CM_BLUE]; - if (transparent >= 0) { - *pixelPtr++ = cmap[v][CM_ALPHA]; - } - xpos++; - - } - - /* - * If interlacing, the next ypos is not just +1. - */ - - if (interlace) { - ypos += interlaceStep[pass]; - while (ypos >= rows) { - pass++; - if (pass > 3) { - return TCL_OK; - } - ypos = interlaceStart[pass]; - } - } else { - ypos++; - } - pixelPtr = imagePtr + (ypos) * len * ((transparent>=0)?4:3); - } - 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: - * 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. - * 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 - * - * Results: - * code the next compression code - * - * Side effects: - * May consume more input from chan. - * - *---------------------------------------------------------------------- - */ - -static int -GetCode( - Tcl_Channel chan, - int code_size, - int flag, - GIFImageConfig *gifConfPtr) -{ - int ret; - - if (flag) { - /* - * Initialize the decoder. - */ - - gifConfPtr->reader.bitsInWindow = 0; - gifConfPtr->reader.bytes = 0; - gifConfPtr->reader.window = 0; - gifConfPtr->reader.done = 0; - gifConfPtr->reader.c = NULL; - return 0; - } - - while (gifConfPtr->reader.bitsInWindow < code_size) { - /* - * Not enough bits in our window to cover the request. - */ - - if (gifConfPtr->reader.done) { - return -1; - } - if (gifConfPtr->reader.bytes == 0) { - /* - * Not enough bytes in our buffer to add to the window. - */ - - 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. - */ - - 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 = gifConfPtr->reader.window & ((1 << code_size) - 1); - - /* - * Shift data in the window to put the next code at the end. - */ - - gifConfPtr->reader.window >>= code_size; - gifConfPtr->reader.bitsInWindow -= code_size; - return ret; -} - -/* - *---------------------------------------------------------------------- - * - * Minit -- -- - * - * This function initializes a base64 decoder handle - * - * Results: - * None - * - * Side effects: - * The base64 handle is initialized - * - *---------------------------------------------------------------------- - */ - -static void -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->state = 0; - handle->c = 0; - handle->length = length; -} - -/* - *---------------------------------------------------------------------- - * - * Mread -- - * - * 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" - * - * Side effects: - * The base64 handle will change state. - * - *---------------------------------------------------------------------- - */ - -static int -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++) { - *dst++ = c; - } - return i; -} - -/* - *---------------------------------------------------------------------- - * - * Mgetc -- - * - * 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. - * - * Side effects: - * The base64 handle will change state. - * - *---------------------------------------------------------------------- - */ - -static int -Mgetc( - MFile *handle) /* Handle containing decoder data and state */ -{ - int c; - int result = 0; /* Initialization needed only to prevent gcc - * compiler warning. */ - - if (handle->state == GIF_DONE) { - return GIF_DONE; - } - - do { - if (handle->length-- <= 0) { - return GIF_DONE; - } - c = char64(*handle->data); - handle->data++; - } while (c == GIF_SPACE); - - if (c > GIF_SPECIAL) { - handle->state = GIF_DONE; - return handle->c; - } - - switch (handle->state++) { - case 0: - handle->c = c<<2; - result = Mgetc(handle); - break; - case 1: - result = handle->c | (c>>4); - handle->c = (c&0xF)<<4; - break; - case 2: - result = handle->c | (c>>2); - handle->c = (c&0x3) << 6; - break; - case 3: - result = handle->c | c; - handle->state = 0; - break; - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * char64 -- - * - * 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( - int c) -{ - switch(c) { - case 'A': return 0; case 'B': return 1; case 'C': return 2; - case 'D': return 3; case 'E': return 4; case 'F': return 5; - case 'G': return 6; case 'H': return 7; case 'I': return 8; - case 'J': return 9; case 'K': return 10; case 'L': return 11; - case 'M': return 12; case 'N': return 13; case 'O': return 14; - case 'P': return 15; case 'Q': return 16; case 'R': return 17; - case 'S': return 18; case 'T': return 19; case 'U': return 20; - case 'V': return 21; case 'W': return 22; case 'X': return 23; - case 'Y': return 24; case 'Z': return 25; case 'a': return 26; - case 'b': return 27; case 'c': return 28; case 'd': return 29; - case 'e': return 30; case 'f': return 31; case 'g': return 32; - case 'h': return 33; case 'i': return 34; case 'j': return 35; - case 'k': return 36; case 'l': return 37; case 'm': return 38; - case 'n': return 39; case 'o': return 40; case 'p': return 41; - case 'q': return 42; case 'r': return 43; case 's': return 44; - case 't': return 45; case 'u': return 46; case 'v': return 47; - case 'w': return 48; case 'x': return 49; case 'y': return 50; - case 'z': return 51; case '0': return 52; case '1': return 53; - case '2': return 54; case '3': return 55; case '4': return 56; - case '5': return 57; case '6': return 58; case '7': return 59; - case '8': return 60; case '9': return 61; case '+': return 62; - case '/': return 63; - - case ' ': case '\t': case '\n': case '\r': case '\f': - return GIF_SPACE; - case '=': - return GIF_PAD; - case '\0': - return GIF_DONE; - default: - return GIF_BAD; - } -} - -/* - *---------------------------------------------------------------------- - * - * Fread -- - * - * This function calls either fread or Mread to read data from a file or - * a base64 encoded string. - * - * Results: - same as POSIX fread() or Tcl Tcl_Read() - * - *---------------------------------------------------------------------- - */ - -static int -Fread( - GIFImageConfig *gifConfPtr, - unsigned char *dst, /* where to put the result */ - size_t hunk, size_t count, /* how many */ - Tcl_Channel chan) -{ - if (gifConfPtr->fromData == INLINE_DATA_BASE64) { - return Mread(dst, hunk, count, (MFile *) chan); - } - - if (gifConfPtr->fromData == INLINE_DATA_BINARY) { - MFile *handle = (MFile *) chan; - - if (handle->length <= 0 || (size_t) handle->length < hunk*count) { - return -1; - } - memcpy(dst, handle->data, (size_t) (hunk * count)); - handle->data += hunk * count; - handle->length -= hunk * count; - return (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 - * - * Modified for transparency handling (gif89a) - * by Jan Nijtmans <nijtmans@users.sourceforge.net> - * - *---------------------------------------------------------------------- - * FileWriteGIF- - * - * 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 the interp's result. - * - *---------------------------------------------------------------------- - */ - - -static int -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; - - chan = Tcl_OpenFileChannel(interp, (char *) filename, "w", 0644); - if (!chan) { - return TCL_ERROR; - } - if (Tcl_SetChannelOption(interp, chan, "-translation", - "binary") != TCL_OK) { - Tcl_Close(NULL, chan); - return TCL_ERROR; - } - - result = CommonWriteGIF(interp, chan, WriteToChannel, format, blockPtr); - - if (Tcl_Close(interp, chan) == TCL_ERROR) { - return TCL_ERROR; - } - return result; -} - -static int -StringWriteGIF( - Tcl_Interp *interp, /* Interpreter to use for reporting errors and - * returning the GIF data. */ - Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr) -{ - int result; - Tcl_Obj *objPtr = Tcl_NewObj(); - - Tcl_IncrRefCount(objPtr); - result = CommonWriteGIF(interp, objPtr, WriteToByteArray, format, - blockPtr); - if (result == TCL_OK) { - Tcl_SetObjResult(interp, objPtr); - } - Tcl_DecrRefCount(objPtr); - return result; -} - -static int -WriteToChannel( - ClientData clientData, - const char *bytes, - int byteCount) -{ - Tcl_Channel handle = clientData; - - return Tcl_Write(handle, bytes, byteCount); -} - -static int -WriteToByteArray( - ClientData clientData, - const char *bytes, - int byteCount) -{ - Tcl_Obj *objPtr = clientData; - Tcl_Obj *tmpObj = Tcl_NewByteArrayObj((unsigned char *) bytes, byteCount); - - Tcl_IncrRefCount(tmpObj); - Tcl_AppendObjToObj(objPtr, tmpObj); - Tcl_DecrRefCount(tmpObj); - return byteCount; -} - -static int -CommonWriteGIF( - Tcl_Interp *interp, - ClientData handle, - WriteBytesFunc *writeProc, - Tcl_Obj *format, - Tk_PhotoImageBlock *blockPtr) -{ - GifWriterState state; - int resolution; - long width, height, x; - unsigned char c; - unsigned int top, left; - - top = 0; - left = 0; - - memset(&state, 0, sizeof(state)); - - state.pixelSize = blockPtr->pixelSize; - state.greenOffset = blockPtr->offset[1]-blockPtr->offset[0]; - state.blueOffset = blockPtr->offset[2]-blockPtr->offset[0]; - state.alphaOffset = blockPtr->offset[0]; - if (state.alphaOffset < blockPtr->offset[2]) { - state.alphaOffset = blockPtr->offset[2]; - } - if (++state.alphaOffset < state.pixelSize) { - state.alphaOffset -= blockPtr->offset[0]; - } else { - state.alphaOffset = 0; - } - - writeProc(handle, (char *) (state.alphaOffset ? GIF89a : GIF87a), 6); - - for (x = 0; x < MAXCOLORMAPSIZE ;x++) { - state.mapa[x][CM_RED] = 255; - state.mapa[x][CM_GREEN] = 255; - state.mapa[x][CM_BLUE] = 255; - } - - width = blockPtr->width; - height = blockPtr->height; - state.pixelOffset = blockPtr->pixelPtr + blockPtr->offset[0]; - state.pixelPitch = blockPtr->pitch; - SaveMap(&state, blockPtr); - if (state.num >= MAXCOLORMAPSIZE) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("too many colors", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "COLORFUL", NULL); - return TCL_ERROR; - } - if (state.num<2) { - state.num = 2; - } - c = LSB(width); - writeProc(handle, (char *) &c, 1); - c = MSB(width); - writeProc(handle, (char *) &c, 1); - c = LSB(height); - writeProc(handle, (char *) &c, 1); - c = MSB(height); - writeProc(handle, (char *) &c, 1); - - resolution = 0; - while (state.num >> resolution) { - resolution++; - } - c = 111 + resolution * 17; - writeProc(handle, (char *) &c, 1); - - state.num = 1 << resolution; - - /* - * Background color - */ - - c = 0; - writeProc(handle, (char *) &c, 1); - - /* - * Zero for future expansion. - */ - - writeProc(handle, (char *) &c, 1); - - for (x = 0; x < state.num; x++) { - c = state.mapa[x][CM_RED]; - writeProc(handle, (char *) &c, 1); - c = state.mapa[x][CM_GREEN]; - writeProc(handle, (char *) &c, 1); - c = state.mapa[x][CM_BLUE]; - writeProc(handle, (char *) &c, 1); - } - - /* - * Write out extension for transparent colour index, if necessary. - */ - - if (state.alphaOffset) { - c = GIF_EXTENSION; - writeProc(handle, (char *) &c, 1); - writeProc(handle, "\371\4\1\0\0\0", 7); - } - - c = GIF_START; - writeProc(handle, (char *) &c, 1); - c = LSB(top); - writeProc(handle, (char *) &c, 1); - c = MSB(top); - writeProc(handle, (char *) &c, 1); - c = LSB(left); - writeProc(handle, (char *) &c, 1); - c = MSB(left); - writeProc(handle, (char *) &c, 1); - - c = LSB(width); - writeProc(handle, (char *) &c, 1); - c = MSB(width); - writeProc(handle, (char *) &c, 1); - - c = LSB(height); - writeProc(handle, (char *) &c, 1); - c = MSB(height); - writeProc(handle, (char *) &c, 1); - - c = 0; - writeProc(handle, (char *) &c, 1); - c = resolution; - writeProc(handle, (char *) &c, 1); - - state.ssize = state.rsize = blockPtr->width; - state.csize = blockPtr->height; - Compress(resolution+1, handle, writeProc, ReadValue, &state); - - c = 0; - writeProc(handle, (char *) &c, 1); - c = GIF_TERMINATOR; - writeProc(handle, (char *) &c, 1); - - return TCL_OK; -} - -static int -ColorNumber( - GifWriterState *statePtr, - int red, int green, int blue) -{ - int x = (statePtr->alphaOffset != 0); - - for (; x <= MAXCOLORMAPSIZE; x++) { - if ((statePtr->mapa[x][CM_RED] == red) && - (statePtr->mapa[x][CM_GREEN] == green) && - (statePtr->mapa[x][CM_BLUE] == blue)) { - return x; - } - } - return -1; -} - -static int -IsNewColor( - GifWriterState *statePtr, - int red, int green, int blue) -{ - int x = (statePtr->alphaOffset != 0); - - for (; x<=statePtr->num ; x++) { - if ((statePtr->mapa[x][CM_RED] == red) && - (statePtr->mapa[x][CM_GREEN] == green) && - (statePtr->mapa[x][CM_BLUE] == blue)) { - return 0; - } - } - return 1; -} - -static void -SaveMap( - GifWriterState *statePtr, - Tk_PhotoImageBlock *blockPtr) -{ - unsigned char *colores; - int x, y; - unsigned char red, green, blue; - - if (statePtr->alphaOffset) { - statePtr->num = 0; - statePtr->mapa[0][CM_RED] = DEFAULT_BACKGROUND_VALUE; - statePtr->mapa[0][CM_GREEN] = DEFAULT_BACKGROUND_VALUE; - statePtr->mapa[0][CM_BLUE] = DEFAULT_BACKGROUND_VALUE; - } else { - 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 (!statePtr->alphaOffset || colores[statePtr->alphaOffset]!=0) { - red = colores[0]; - green = colores[statePtr->greenOffset]; - blue = colores[statePtr->blueOffset]; - if (IsNewColor(statePtr, red, green, blue)) { - statePtr->num++; - if (statePtr->num >= MAXCOLORMAPSIZE) { - return; - } - statePtr->mapa[statePtr->num][CM_RED] = red; - statePtr->mapa[statePtr->num][CM_GREEN] = green; - statePtr->mapa[statePtr->num][CM_BLUE] = blue; - } - } - colores += statePtr->pixelSize; - } - } -} - -static int -ReadValue( - GifWriterState *statePtr) -{ - unsigned int col; - - if (statePtr->csize == 0) { - return EOF; - } - if (statePtr->alphaOffset - && (statePtr->pixelOffset[statePtr->alphaOffset]==0)) { - col = 0; - } else { - col = ColorNumber(statePtr, statePtr->pixelOffset[0], - statePtr->pixelOffset[statePtr->greenOffset], - statePtr->pixelOffset[statePtr->blueOffset]); - } - statePtr->pixelOffset += statePtr->pixelSize; - if (--statePtr->ssize <= 0) { - statePtr->ssize = statePtr->rsize; - statePtr->csize--; - statePtr->pixelOffset += statePtr->pixelPitch - - (statePtr->rsize * statePtr->pixelSize); - } - - return col; -} - -/* - * GIF Image compression - modified 'Compress' - * - * Based on: compress.c - File compression ala IEEE Computer, June 1984. - * - * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) - * Jim McKie (decvax!mcvax!jim) - * Steve Davies (decvax!vax135!petsd!peora!srd) - * Ken Turkowski (decvax!decwrl!turtlevax!ken) - * James A. Woods (decvax!ihnp4!ames!jaw) - * Joe Orost (decvax!vax135!petsd!joe) - */ - -static void -Compress( - int initialBits, - ClientData handle, - WriteBytesFunc *writeProc, - ifunptr readValue, - GifWriterState *statePtr) -{ - long fcode, ent, disp, hSize, i = 0; - int c, hshift; - GIFState_t state; - - memset(&state, 0, sizeof(state)); - - /* - * Set up the globals: initialBits - initial number of bits - * outChannel - pointer to output file - */ - - state.initialBits = initialBits; - state.destination = handle; - state.writeProc = writeProc; - - /* - * Set up the necessary values. - */ - - state.offset = 0; - state.hSize = HSIZE; - state.outCount = 0; - state.clearFlag = 0; - state.inCount = 1; - state.maxCode = MAXCODE(state.numBits = state.initialBits); - state.clearCode = 1 << (initialBits - 1); - state.eofCode = state.clearCode + 1; - state.freeEntry = state.clearCode + 2; - CharInit(&state); - - ent = readValue(statePtr); - - hshift = 0; - for (fcode = (long) state.hSize; fcode < 65536L; fcode *= 2L) { - hshift++; - } - hshift = 8 - hshift; /* Set hash code range bound */ - - hSize = state.hSize; - ClearHashTable(&state, (int) hSize); /* Clear hash table */ - - Output(&state, (long) state.clearCode); - - while (U(c = readValue(statePtr)) != U(EOF)) { - state.inCount++; - - fcode = (long) (((long) c << GIFBITS) + ent); - i = ((long)c << hshift) ^ ent; /* XOR hashing */ - - if (state.hashTable[i] == fcode) { - ent = state.codeTable[i]; - continue; - } else if ((long) state.hashTable[i] < 0) { /* Empty slot */ - goto nomatch; - } - - disp = hSize - i; /* Secondary hash (after G. Knott) */ - if (i == 0) { - disp = 1; - } - - probe: - if ((i -= disp) < 0) { - i += hSize; - } - - if (state.hashTable[i] == fcode) { - ent = state.codeTable[i]; - continue; - } - if ((long) state.hashTable[i] > 0) { - goto probe; - } - - nomatch: - Output(&state, (long) ent); - state.outCount++; - ent = c; - if (U(state.freeEntry) < U((long)1 << GIFBITS)) { - state.codeTable[i] = state.freeEntry++; /* code -> hashtable */ - state.hashTable[i] = fcode; - } else { - ClearForBlock(&state); - } - } - - /* - * Put out the final code. - */ - - Output(&state, (long) ent); - state.outCount++; - Output(&state, (long) state.eofCode); -} - -/***************************************************************** - * Output -- - * Output the given code. - * - * Inputs: - * code: A numBits-bit integer. If == -1, then EOF. This assumes that - * numBits =< (long) wordsize - 1. - * Outputs: - * Outputs code to the file. - * Assumptions: - * Chars are 8 bits long. - * Algorithm: - * Maintain a GIFBITS character long buffer (so that 8 codes will fit in - * it exactly). Use the VAX insv instruction to insert each code in turn. - * When the buffer fills up empty it and start over. - */ - -static void -Output( - GIFState_t *statePtr, - long code) -{ - static const unsigned long masks[] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000F, - 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, - 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF - }; - - statePtr->currentAccumulated &= masks[statePtr->currentBits]; - if (statePtr->currentBits > 0) { - statePtr->currentAccumulated |= ((long) code << statePtr->currentBits); - } else { - statePtr->currentAccumulated = code; - } - statePtr->currentBits += statePtr->numBits; - - while (statePtr->currentBits >= 8) { - CharOut(statePtr, (unsigned) (statePtr->currentAccumulated & 0xff)); - statePtr->currentAccumulated >>= 8; - statePtr->currentBits -= 8; - } - - /* - * If the next entry is going to be too big for the code size, then - * increase it, if possible. - */ - - if ((statePtr->freeEntry > statePtr->maxCode) || statePtr->clearFlag) { - if (statePtr->clearFlag) { - statePtr->maxCode = MAXCODE( - statePtr->numBits = statePtr->initialBits); - statePtr->clearFlag = 0; - } else { - statePtr->numBits++; - if (statePtr->numBits == GIFBITS) { - statePtr->maxCode = (long)1 << GIFBITS; - } else { - statePtr->maxCode = MAXCODE(statePtr->numBits); - } - } - } - - if (code == statePtr->eofCode) { - /* - * At EOF, write the rest of the buffer. - */ - - while (statePtr->currentBits > 0) { - CharOut(statePtr, - (unsigned) (statePtr->currentAccumulated & 0xff)); - statePtr->currentAccumulated >>= 8; - statePtr->currentBits -= 8; - } - FlushChar(statePtr); - } -} - -/* - * Clear out the hash table - */ - -static void -ClearForBlock( /* Table clear for block compress. */ - GIFState_t *statePtr) -{ - ClearHashTable(statePtr, (int) statePtr->hSize); - statePtr->freeEntry = statePtr->clearCode + 2; - statePtr->clearFlag = 1; - - Output(statePtr, (long) statePtr->clearCode); -} - -static void -ClearHashTable( /* Reset code table. */ - GIFState_t *statePtr, - int hSize) -{ - register int *hashTablePtr = statePtr->hashTable + hSize; - register long i; - register long m1 = -1; - - i = hSize - 16; - do { /* might use Sys V memset(3) here */ - *(hashTablePtr-16) = m1; - *(hashTablePtr-15) = m1; - *(hashTablePtr-14) = m1; - *(hashTablePtr-13) = m1; - *(hashTablePtr-12) = m1; - *(hashTablePtr-11) = m1; - *(hashTablePtr-10) = m1; - *(hashTablePtr-9) = m1; - *(hashTablePtr-8) = m1; - *(hashTablePtr-7) = m1; - *(hashTablePtr-6) = m1; - *(hashTablePtr-5) = m1; - *(hashTablePtr-4) = m1; - *(hashTablePtr-3) = m1; - *(hashTablePtr-2) = m1; - *(hashTablePtr-1) = m1; - hashTablePtr -= 16; - } while ((i -= 16) >= 0); - - for (i += 16; i > 0; i--) { - *--hashTablePtr = m1; - } -} - -/* - ***************************************************************************** - * - * GIF Specific routines - * - ***************************************************************************** - */ - -/* - * Set up the 'byte output' routine - */ - -static void -CharInit( - GIFState_t *statePtr) -{ - statePtr->accumulatedByteCount = 0; - statePtr->currentAccumulated = 0; - statePtr->currentBits = 0; -} - -/* - * Add a character to the end of the current packet, and if it is 254 - * characters, flush the packet to disk. - */ - -static void -CharOut( - GIFState_t *statePtr, - int c) -{ - statePtr->packetAccumulator[statePtr->accumulatedByteCount++] = c; - if (statePtr->accumulatedByteCount >= 254) { - FlushChar(statePtr); - } -} - -/* - * Flush the packet to disk, and reset the accumulator - */ - -static void -FlushChar( - GIFState_t *statePtr) -{ - unsigned char c; - - if (statePtr->accumulatedByteCount > 0) { - c = statePtr->accumulatedByteCount; - statePtr->writeProc(statePtr->destination, (const char *) &c, 1); - statePtr->writeProc(statePtr->destination, - (const char *) statePtr->packetAccumulator, - statePtr->accumulatedByteCount); - statePtr->accumulatedByteCount = 0; - } -} - -/* The End */ - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |