stk/Tk/generic/tkImgGIF.c

1060 lines
27 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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));
}
}