diff options
Diffstat (limited to 'generic/tkImgGIF.c')
-rw-r--r-- | generic/tkImgGIF.c | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/generic/tkImgGIF.c b/generic/tkImgGIF.c new file mode 100644 index 0000000..a2ad081 --- /dev/null +++ b/generic/tkImgGIF.c @@ -0,0 +1,1059 @@ +/* + * tkImgGIF.c -- + * + * A photo image file handler for GIF files. Reads 87a and 89a GIF + * files. At present there is no write function. GIF images may be + * read using the -data option of the photo image 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. + * + * 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. | + * +-------------------------------------------------------------------+ + * + * SCCS: @(#) tkImgGIF.c 1.19 97/08/13 15:23:45 + */ + +/* + * 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 + */ + +#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) */ +} MFile; + +#include "tkInt.h" +#include "tkPort.h" + +/* + * The format record for the GIF file format: + */ + +static int FileMatchGIF _ANSI_ARGS_((Tcl_Channel chan, char *fileName, + char *formatString, int *widthPtr, int *heightPtr)); +static int FileReadGIF _ANSI_ARGS_((Tcl_Interp *interp, + Tcl_Channel chan, char *fileName, char *formatString, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY)); +static int StringMatchGIF _ANSI_ARGS_(( char *string, + char *formatString, int *widthPtr, int *heightPtr)); +static int StringReadGIF _ANSI_ARGS_((Tcl_Interp *interp, char *string, + char *formatString, Tk_PhotoHandle imageHandle, + int destX, int destY, int width, int height, + int srcX, int srcY)); + +Tk_PhotoImageFormat tkImgFmtGIF = { + "GIF", /* name */ + FileMatchGIF, /* fileMatchProc */ + StringMatchGIF, /* stringMatchProc */ + FileReadGIF, /* fileReadProc */ + StringReadGIF, /* stringReadProc */ + NULL, /* fileWriteProc */ + NULL, /* stringWriteProc */ +}; + +#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)) +#define ReadOK(file,buffer,len) (Fread(buffer, len, 1, file) != 0) + +/* + * 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 + */ + +static int fromData=0; + +/* + * Prototypes for local procedures 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 LWZReadByte _ANSI_ARGS_((Tcl_Channel chan, int flag, + int input_code_size)); +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)); + +/* + * 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, + MFile *handle)); + +/* + *---------------------------------------------------------------------- + * + * FileMatchGIF -- + * + * This procedure 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(chan, fileName, formatString, widthPtr, heightPtr) + Tcl_Channel chan; /* The image file, open for reading. */ + char *fileName; /* The name of the image file. */ + char *formatString; /* User-specified format string, or NULL. */ + int *widthPtr, *heightPtr; /* The dimensions of the image are + * returned here if the file is a valid + * raw GIF file. */ +{ + return ReadGIFHeader(chan, widthPtr, heightPtr); +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + * Results: + * A standard TCL completion code. If TCL_ERROR is returned + * then an error message is left in interp->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(interp, chan, fileName, formatString, 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. */ + char *fileName; /* The name of the image file. */ + char *formatString; /* User-specified format string, 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. */ +{ + int fileWidth, fileHeight; + int nBytes; + Tk_PhotoImageBlock block; + unsigned char buf[100]; + int bitPixel; + unsigned char colorMap[MAXCOLORMAPSIZE][4]; + int transparent = -1; + + if (!ReadGIFHeader(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); + return TCL_ERROR; + } + + if (Fread(buf, 1, 3, chan) != 3) { + return TCL_OK; + } + 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); + 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; + } + + Tk_PhotoExpand(imageHandle, destX + width, destY + height); + + 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; + nBytes = height * block.pitch; + block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); + + while (1) { + if (Fread(buf, 1, 1, chan) != 1) { + /* + * Premature end of image. We should really notify + * the user, but for now just show garbage. + */ + + break; + } + + if (buf[0] == ';') { + /* + * GIF terminator. + */ + + break; + } + + if (buf[0] == '!') { + /* + * This is a GIF extension. + */ + + if (Fread(buf, 1, 1, chan) != 1) { + interp->result = + "error reading extension function code in GIF image"; + goto error; + } + if (DoExtension(chan, buf[0], &transparent) < 0) { + interp->result = "error reading extension in GIF image"; + goto error; + } + continue; + } + + if (buf[0] != ',') { + /* + * Not a valid start character; ignore it. + */ + continue; + } + + if (Fread(buf, 1, 9, chan) != 9) { + interp->result = "couldn't read left/top/width/height in GIF image"; + goto error; + } + + bitPixel = 1<<((buf[8]&0x07)+1); + + if (BitSet(buf[8], LOCALCOLORMAP)) { + if (!ReadColorMap(chan, bitPixel, colorMap)) { + Tcl_AppendResult(interp, "error reading color map", + (char *) NULL); + goto error; + } + } + if (ReadImage(interp, (char *) block.pixelPtr, chan, width, + height, colorMap, fileWidth, fileHeight, srcX, srcY, + BitSet(buf[8], INTERLACE), transparent) != TCL_OK) { + goto error; + } + break; + } + + if (transparent == -1) { + Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height); + } else { + int x, y, end; + unsigned char *imagePtr, *rowPtr, *pixelPtr; + + imagePtr = rowPtr = block.pixelPtr; + for (y = 0; y < height; y++) { + x = 0; + pixelPtr = rowPtr; + while(x < width) { + /* search for first non-transparent pixel */ + while ((x < width) && !(pixelPtr[CM_ALPHA])) { + x++; pixelPtr += 4; + } + end = x; + /* search for first transparent pixel */ + while ((end < width) && pixelPtr[CM_ALPHA]) { + end++; pixelPtr += 4; + } + if (end > x) { + block.pixelPtr = rowPtr + 4 * x; + Tk_PhotoPutBlock(imageHandle, &block, destX+x, + destY+y, end-x, 1); + } + x = end; + } + rowPtr += block.pitch; + } + block.pixelPtr = imagePtr; + } + ckfree((char *) block.pixelPtr); + return TCL_OK; + + error: + ckfree((char *) block.pixelPtr); + return TCL_ERROR; + +} + +/* + *---------------------------------------------------------------------- + * + * StringMatchGIF -- + * + * This procedure is invoked by the photo image type to see if + * a string contains image data in GIF format. + * + * Results: + * The return value is 1 if the first characters in the string + * like GIF data, and 0 otherwise. + * + * Side effects: + * the size of the image is placed in widthPre and heightPtr. + * + *---------------------------------------------------------------------- + */ + +static int +StringMatchGIF(string, formatString, widthPtr, heightPtr) + char *string; /* the string containing the image data */ + char *formatString; /* the image format string */ + int *widthPtr; /* where to put the string width */ + int *heightPtr; /* where to put the string height */ +{ + unsigned char header[10]; + int got; + MFile handle; + mInit((unsigned char *) string, &handle); + got = Mread(header, 10, 1, &handle); + if (got != 10 + || ((strncmp("GIF87a", (char *) header, 6) != 0) + && (strncmp("GIF89a", (char *) header, 6) != 0))) { + return 0; + } + *widthPtr = LM_to_uint(header[6],header[7]); + *heightPtr = LM_to_uint(header[8],header[9]); + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * StringReadGif -- -- + * + * This procedure is called by the photo image type to read + * GIF format data from a base64 encoded string, 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 interp->result. + * + * Side effects: + * new data is added to the image given by imageHandle. This + * procedure calls FileReadGif by redefining the operation of + * fprintf temporarily. + * + *---------------------------------------------------------------------- + */ + +static int +StringReadGIF(interp,string,formatString,imageHandle, + destX, destY, width, height, srcX, srcY) + Tcl_Interp *interp; /* interpreter for reporting errors in */ + char *string; /* string containing the image */ + char *formatString; /* format string if any */ + 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; +{ + int result; + MFile handle; + mInit((unsigned char *)string,&handle); + fromData = 1; + result = FileReadGIF(interp, (Tcl_Channel) &handle, "inline data", + formatString, imageHandle, destX, destY, width, height, + srcX, srcY); + fromData = 0; + return(result); +} + +/* + *---------------------------------------------------------------------- + * + * ReadGIFHeader -- + * + * This procedure 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(chan, widthPtr, heightPtr) + Tcl_Channel chan; /* Image file to read the header from */ + int *widthPtr, *heightPtr; /* The dimensions of the image are + * returned here. */ +{ + unsigned char buf[7]; + + if ((Fread(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) { + 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(chan, number, buffer) + 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))) { + return 0; + } + + 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; +{ + static unsigned char buf[256]; + int count; + + switch (label) { + case 0x01: /* Plain Text Extension */ + break; + + case 0xff: /* Application Extension */ + break; + + case 0xfe: /* Comment Extension */ + do { + count = GetDataBlock(chan, (unsigned char*) buf); + } while (count > 0); + return count; + + case 0xf9: /* Graphic Control Extension */ + count = GetDataBlock(chan, (unsigned char*) buf); + if (count < 0) { + return 1; + } + if ((buf[0] & 0x1) != 0) { + *transparent = buf[3]; + } + + do { + count = GetDataBlock(chan, (unsigned char*) buf); + } while (count > 0); + return count; + } + + do { + count = GetDataBlock(chan, (unsigned char*) buf); + } while (count > 0); + return count; +} + +static int ZeroDataBlock = 0; + +static int +GetDataBlock(chan, buf) + Tcl_Channel chan; + unsigned char *buf; +{ + unsigned char count; + + if (! ReadOK(chan, &count,1)) { + return -1; + } + + ZeroDataBlock = count == 0; + + if ((count != 0) && (! ReadOK(chan, buf, count))) { + return -1; + } + + return count; +} + + +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; +{ + unsigned char c; + int v; + int xpos = 0, ypos = 0, pass = 0; + char *pixelPtr; + + + /* + * Initialize the Compression routines + */ + if (! ReadOK(chan, &c, 1)) { + Tcl_AppendResult(interp, "error reading GIF image: ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + + if (LWZReadByte(chan, 1, c) < 0) { + interp->result = "format error in GIF image"; + 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; + while ((v = LWZReadByte(chan, 0, c)) >= 0 ) { + + if ((xpos>=srcX) && (xpos<srcX+len) && + (ypos>=srcY) && (ypos<srcY+rows)) { + *pixelPtr++ = cmap[v][CM_RED]; + *pixelPtr++ = cmap[v][CM_GREEN]; + *pixelPtr++ = cmap[v][CM_BLUE]; + *pixelPtr++ = cmap[v][CM_ALPHA]; + } + + ++xpos; + if (xpos == width) { + xpos = 0; + if (interlace) { + switch (pass) { + case 0: + case 1: + ypos += 8; break; + case 2: + ypos += 4; break; + case 3: + ypos += 2; break; + } + + while (ypos >= height) { + ++pass; + switch (pass) { + case 1: + ypos = 4; break; + case 2: + ypos = 2; break; + case 3: + ypos = 1; break; + default: + return TCL_OK; + } + } + } else { + ++ypos; + } + pixelPtr = imagePtr + (ypos-srcY) * len * 4; + } + if (ypos >= height) + break; + } + return TCL_OK; +} + +static int +LWZReadByte(chan, flag, input_code_size) + Tcl_Channel chan; + int flag; + int input_code_size; +{ + static int fresh = 0; + int code, incode; + static int code_size, set_code_size; + static int max_code, max_code_size; + static int firstcode, oldcode; + static int clear_code, end_code; + static int table[2][(1<< MAX_LWZ_BITS)]; + static int stack[(1<<(MAX_LWZ_BITS))*2], *sp; + register int i; + + if (flag) { + set_code_size = input_code_size; + code_size = set_code_size+1; + clear_code = 1 << set_code_size ; + end_code = clear_code + 1; + max_code_size = 2*clear_code; + max_code = clear_code+2; + + GetCode(chan, 0, 1); + + fresh = 1; + + for (i = 0; i < clear_code; ++i) { + table[0][i] = 0; + table[1][i] = i; + } + for (; i < (1<<MAX_LWZ_BITS); ++i) { + table[0][i] = table[1][0] = 0; + } + + sp = stack; + + return 0; + } else if (fresh) { + fresh = 0; + do { + firstcode = oldcode = GetCode(chan, code_size, 0); + } while (firstcode == clear_code); + return firstcode; + } + + if (sp > stack) { + return *--sp; + } + + while ((code = GetCode(chan, code_size, 0)) >= 0) { + if (code == clear_code) { + for (i = 0; i < clear_code; ++i) { + table[0][i] = 0; + table[1][i] = i; + } + + for (; i < (1<<MAX_LWZ_BITS); ++i) { + table[0][i] = table[1][i] = 0; + } + + code_size = set_code_size+1; + max_code_size = 2*clear_code; + max_code = clear_code+2; + sp = stack; + firstcode = oldcode = GetCode(chan, code_size, 0); + return firstcode; + + } else if (code == end_code) { + int count; + unsigned char buf[260]; + + if (ZeroDataBlock) { + return -2; + } + + while ((count = GetDataBlock(chan, buf)) > 0) + /* Empty body */; + + if (count != 0) { + return -2; + } + } + + incode = code; + + if (code >= max_code) { + *sp++ = firstcode; + code = oldcode; + } + + while (code >= clear_code) { + *sp++ = table[1][code]; + if (code == table[0][code]) { + return -2; + + /* + * Used to be this instead, Steve Ball suggested + * the change to just return. + printf("circular table entry BIG ERROR\n"); + */ + } + code = table[0][code]; + } + + *sp++ = firstcode = table[1][code]; + + if ((code = max_code) <(1<<MAX_LWZ_BITS)) { + table[0][code] = oldcode; + table[1][code] = firstcode; + ++max_code; + if ((max_code>=max_code_size) && (max_code_size < (1<<MAX_LWZ_BITS))) { + max_code_size *= 2; + ++code_size; + } + } + + oldcode = incode; + + if (sp > stack) + return *--sp; + } + return code; +} + + +static int +GetCode(chan, code_size, flag) + Tcl_Channel chan; + int code_size; + int flag; +{ + static unsigned char buf[280]; + static int curbit, lastbit, done, last_byte; + int i, j, ret; + unsigned char count; + + if (flag) { + curbit = 0; + lastbit = 0; + done = 0; + return 0; + } + + + if ( (curbit+code_size) >= lastbit) { + if (done) { + /* ran off the end of my bits */ + return -1; + } + if (last_byte >= 2) { + buf[0] = buf[last_byte-2]; + } + if (last_byte >= 1) { + buf[1] = buf[last_byte-1]; + } + + if ((count = GetDataBlock(chan, &buf[2])) == 0) { + done = 1; + } + + last_byte = 2 + count; + curbit = (curbit - lastbit) + 16; + lastbit = (2+count)*8 ; + } + + ret = 0; + for (i = curbit, j = 0; j < code_size; ++i, ++j) { + ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j; + } + + curbit += code_size; + + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * Minit -- -- + * + * This procedure initializes a base64 decoder handle + * + * Results: + * none + * + * Side effects: + * the base64 handle is initialized + * + *---------------------------------------------------------------------- + */ + +static void +mInit(string, handle) + unsigned char *string; /* string containing initial mmencoded data */ + MFile *handle; /* mmdecode "file" handle */ +{ + handle->data = string; + handle->state = 0; +} + +/* + *---------------------------------------------------------------------- + * + * 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). + * + * Results: + * The return value is the number of characters "read" + * + * Side effects: + * The base64 handle will change state. + * + *---------------------------------------------------------------------- + */ + +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 */ +{ + register int i, c; + int count = chunkSize * numChunks; + + 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. + * + * Results: + * The next byte (or GIF_DONE) is returned. + * + * Side effects: + * The base64 handle will change state. + * + *---------------------------------------------------------------------- + */ + +static int +Mgetc(handle) + 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 { + c = char64(*handle->data); + handle->data++; + } while (c==GIF_SPACE); + + if (c>GIF_SPECIAL) { + handle->state = GIF_DONE; + return(handle->state ? handle->c : GIF_DONE); + } + + 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 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. + * + * Results: + * The binary value, or an error code. + * + * Side effects: + * None. + *---------------------------------------------------------------------- + */ + +static int +char64(c) +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 procedure calls either fread or Mread to read data + * from a file or a base64 encoded string. + * + * Results: - same as fread + * + *---------------------------------------------------------------------- + */ + +static int +Fread(dst, hunk, count, chan) + unsigned char *dst; /* where to put the result */ + size_t hunk,count; /* how many */ + Tcl_Channel chan; +{ + if (fromData) { + return(Mread(dst, hunk, count, (MFile *) chan)); + } else { + return Tcl_Read(chan, (char *) dst, (int) (hunk * count)); + } +} |