diff options
Diffstat (limited to 'tkimg/xbm/xbm.c')
-rw-r--r-- | tkimg/xbm/xbm.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/tkimg/xbm/xbm.c b/tkimg/xbm/xbm.c new file mode 100644 index 0000000..c1b674a --- /dev/null +++ b/tkimg/xbm/xbm.c @@ -0,0 +1,666 @@ +/* + * imgXBM.c -- + * + * A photo image file handler for XBM files. + * + * Written by: + * Jan Nijtmans + * email: nijtmans@users.sourceforge.net + * url: http://purl.oclc.org/net/nijtmans/ + * + * <paul@poSoft.de> Paul Obermeier + * Feb 2001: + * - Bugfix in CommonWrite: const char *fileName was overwritten. + */ + +/* + * Generic initialization code, parameterized via CPACKAGE and PACKAGE. + */ + +#include "init.c" + + +/* constants used only in this file */ + +#define MAX_BUFFER 4096 + +/* + * The following data structure is used to describe the state of + * parsing a bitmap file or string. It is used for communication + * between TkGetBitmapData and NextBitmapWord. + */ + +#define MAX_WORD_LENGTH 100 +typedef struct ParseInfo { + tkimg_MFile handle; + char word[MAX_WORD_LENGTH+1]; + /* Current word of bitmap data, NULL + * terminated. */ + int wordLength; /* Number of non-NULL bytes in word. */ +} ParseInfo; + +/* + * Prototypes for local procedures defined in this file: + */ + +static int CommonRead(Tcl_Interp *interp, + ParseInfo *parseInfo, + Tcl_Obj *format, Tk_PhotoHandle imageHandle, + int destX, int destY, int width, int height, + int srcX, int srcY); +static int CommonWrite(Tcl_Interp *interp, + const char *fileName, Tcl_DString *dataPtr, + Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr); + +static int ReadXBMFileHeader(ParseInfo *parseInfo, + int *widthPtr, int *heightPtr); +static int NextBitmapWord(ParseInfo *parseInfoPtr); + +/* + *---------------------------------------------------------------------- + * + * ObjMatch -- + * + * This procedure is invoked by the photo image type to see if + * a datastring contains image data in XBM format. + * + * Results: + * The return value is >0 if the first characters in data look + * like XBM data, and 0 otherwise. + * + * Side effects: + * none + * + *---------------------------------------------------------------------- + */ +static int ObjMatch( + Tcl_Obj *data, /* The data supplied by the image */ + Tcl_Obj *format, /* User-specified format string, or NULL. */ + int *widthPtr, /* The dimensions of the image are */ + int *heightPtr, /* returned here if the file is a valid + * raw XBM file. */ + Tcl_Interp *interp +) { + ParseInfo parseInfo; + + parseInfo.handle.data = (char *)tkimg_GetStringFromObj(data, &parseInfo.handle.length); + parseInfo.handle.state = IMG_STRING; + + return ReadXBMFileHeader(&parseInfo, widthPtr, heightPtr); +} + + +/* + *---------------------------------------------------------------------- + * + * ChnMatch -- + * + * This procedure is invoked by the photo image type to see if + * a channel contains image data in XBM format. + * + * Results: + * The return value is >0 if the first characters in channel "chan" + * look like XBM data, and 0 otherwise. + * + * Side effects: + * The access position in chan may change. + * + *---------------------------------------------------------------------- + */ + +static int ChnMatch( + Tcl_Channel chan, /* The image channel, open for reading. */ + const char *fileName, /* The name of the image file. */ + Tcl_Obj *format, /* User-specified format object, or NULL. */ + int *widthPtr, /* The dimensions of the image are */ + int *heightPtr, /* returned here if the file is a valid + * raw XBM file. */ + Tcl_Interp *interp +) { + ParseInfo parseInfo; + + parseInfo.handle.data = (char *) chan; + parseInfo.handle.state = IMG_CHAN; + + return ReadXBMFileHeader(&parseInfo, widthPtr, heightPtr); +} + +/* + *---------------------------------------------------------------------- + * + * CommonRead -- + * + * This procedure is called by the photo image type to read + * XBM format data from a file or string 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 (if read from file) + * and new data is added to the image given by imageHandle. + * + *---------------------------------------------------------------------- + */ +static int +CommonRead(interp, parseInfo, format, imageHandle, destX, destY, + width, height, srcX, srcY) + Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ + ParseInfo *parseInfo; + Tcl_Obj *format; /* 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. */ +{ + Tk_PhotoImageBlock block; + int fileWidth, fileHeight; + int numBytes, row, col, value, i; + unsigned char *data, *pixelPtr; + char *end; + int result = TCL_OK; + + ReadXBMFileHeader(parseInfo, &fileWidth, &fileHeight); + + 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; + } + + if (tkimg_PhotoExpand(interp, imageHandle, destX + width, destY + height) == TCL_ERROR) { + return TCL_ERROR; + } + + numBytes = ((fileWidth+7)/8)*32; + block.width = fileWidth; + block.height = 1; + block.pixelSize = 4; + block.offset[0] = 0; + block.offset[1] = 1; + block.offset[2] = 2; + block.offset[3] = 3; + + data = (unsigned char *) ckalloc((unsigned) numBytes); + block.pixelPtr = data + srcX*4; + for (row = 0; row < srcY + height; row++) { + pixelPtr = data; + for (col = 0; col<(numBytes/32); col++) { + if (NextBitmapWord(parseInfo) != TCL_OK) { + ckfree((char *) data); + return TCL_ERROR; + } + value = (int) strtol(parseInfo->word, &end, 0); + if (end == parseInfo->word) { + ckfree((char *) data); + return TCL_ERROR; + } + for (i=0; i<8; i++) { + *pixelPtr++ = 0; + *pixelPtr++ = 0; + *pixelPtr++ = 0; + *pixelPtr++ = (value & 0x1)? 255:0; + value >>= 1; + } + } + if (row >= srcY) { + if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, destY++, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { + result = TCL_ERROR; + break; + } + } + } + ckfree((char *) data); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * ChnRead -- + * + * This procedure is called by the photo image type to read + * XBM format data from a channel 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 channel chan is changed, and new data is + * added to the image given by imageHandle. + * + *---------------------------------------------------------------------- + */ + +static int +ChnRead(interp, chan, fileName, format, imageHandle, destX, destY, + width, height, srcX, srcY) + Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ + Tcl_Channel chan; /* The image channel, open for reading. */ + const char *fileName; /* The name of the image file. */ + Tcl_Obj *format; /* User-specified format object, or NULL. */ + Tk_PhotoHandle imageHandle; /* The photo image to write into. */ + int destX, destY; /* Coordinates of top-left pixel in + * photo image to be written to. */ + int width, height; /* Dimensions of block of photo image to + * be written to. */ + int srcX, srcY; /* Coordinates of top-left pixel to be used + * in image being read. */ +{ + ParseInfo parseInfo; + + parseInfo.handle.data = (char *) chan; + parseInfo.handle.state = IMG_CHAN; + + return CommonRead(interp, &parseInfo, format, imageHandle, + destX, destY, width, height, srcX, srcY); +} + +/* + *---------------------------------------------------------------------- + * + * ObjRead -- + * + * This procedure is called by the photo image type to read + * XBM format data from a string 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: + * New data is added to the image given by imageHandle. + * + *---------------------------------------------------------------------- + */ + +static int +ObjRead(interp, data, format, imageHandle, destX, destY, + width, height, srcX, srcY) + Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ + Tcl_Obj *data; + Tcl_Obj *format; /* 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. */ +{ + ParseInfo parseInfo; + parseInfo.handle.data = (char *)tkimg_GetStringFromObj(data, &parseInfo.handle.length); + parseInfo.handle.state = IMG_STRING; + + return CommonRead(interp, &parseInfo, format, imageHandle, + destX, destY, width, height, srcX, srcY); +} + +/* + *---------------------------------------------------------------------- + * + * ReadXBMFileHeader -- + * + * This procedure reads the XBM header from the beginning of a + * XBM file and returns information from the header. + * + * Results: + * The return value is 1 if file "f" appears to start with a valid + * XBM header, and 0 otherwise. If the header is valid, + * then *widthPtr and *heightPtr are modified to hold the + * dimensions of the image and *numColors holds the number of + * colors and byteSize the number of bytes used for 1 pixel. + * + * Side effects: + * The access position in f advances. + * + *---------------------------------------------------------------------- + */ + +#define UCHAR(c) ((unsigned char) (c)) + +/* + *---------------------------------------------------------------------- + * + * NextBitmapWord -- + * + * This procedure retrieves the next word of information (stuff + * between commas or white space) from a bitmap description. + * + * Results: + * Returns TCL_OK if all went well. In this case the next word, + * and its length, will be availble in *parseInfoPtr. If the end + * of the bitmap description was reached then TCL_ERROR is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +NextBitmapWord(parseInfoPtr) + ParseInfo *parseInfoPtr; /* Describes what we're reading + * and where we are in it. */ +{ + char *dst, buf; + int num; + + parseInfoPtr->wordLength = 0; + dst = parseInfoPtr->word; + + for (num=tkimg_Read(&parseInfoPtr->handle,&buf,1); isspace(UCHAR(buf)) || (buf == ','); + num=tkimg_Read(&parseInfoPtr->handle,&buf,1)) { + if (num == 0) { + return TCL_ERROR; + } + } + for ( ; !isspace(UCHAR(buf)) && (buf != ',') && (num != 0); + num=tkimg_Read(&parseInfoPtr->handle,&buf,1)) { + *dst = buf; + dst++; + parseInfoPtr->wordLength++; + if (num == 0 || parseInfoPtr->wordLength > MAX_WORD_LENGTH) { + return TCL_ERROR; + } + } + + if (parseInfoPtr->wordLength == 0) { + return TCL_ERROR; + } + parseInfoPtr->word[parseInfoPtr->wordLength] = 0; + return TCL_OK; +} + +static int +ReadXBMFileHeader(pi, widthPtr, heightPtr) + ParseInfo *pi; + int *widthPtr, *heightPtr; /* The dimensions of the image are + * returned here. */ +{ + int width, height; + char *end; + + /* + * Parse the lines that define the dimensions of the bitmap, + * plus the first line that defines the bitmap data (it declares + * the name of a data variable but doesn't include any actual + * data). These lines look something like the following: + * + * #define foo_width 16 + * #define foo_height 16 + * #define foo_x_hot 3 + * #define foo_y_hot 3 + * static char foo_bits[] = { + * + * The x_hot and y_hot lines may or may not be present. It's + * important to check for "char" in the last line, in order to + * reject old X10-style bitmaps that used shorts. + */ + + width = 0; + height = 0; + while (1) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + if ((pi->wordLength >= 6) && (pi->word[pi->wordLength-6] == '_') + && (strcmp(pi->word+pi->wordLength-6, "_width") == 0)) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + width = strtol(pi->word, &end, 0); + if ((end == pi->word) || (*end != 0)) { + return 0; + } + } else if ((pi->wordLength >= 7) && (pi->word[pi->wordLength-7] == '_') + && (strcmp(pi->word+pi->wordLength-7, "_height") == 0)) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + height = strtol(pi->word, &end, 0); + if ((end == pi->word) || (*end != 0)) { + return 0; + } + } else if ((pi->wordLength >= 6) && (pi->word[pi->wordLength-6] == '_') + && (strcmp(pi->word+pi->wordLength-6, "_x_hot") == 0)) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + strtol(pi->word, &end, 0); + if ((end == pi->word) || (*end != 0)) { + return 0; + } + } else if ((pi->wordLength >= 6) && (pi->word[pi->wordLength-6] == '_') + && (strcmp(pi->word+pi->wordLength-6, "_y_hot") == 0)) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + strtol(pi->word, &end, 0); + if ((end == pi->word) || (*end != 0)) { + return 0; + } + } else if ((pi->word[0] == 'c') && (strcmp(pi->word, "char") == 0)) { + while (1) { + if (NextBitmapWord(pi) != TCL_OK) { + return 0; + } + if ((pi->word[0] == '{') && (pi->word[1] == 0)) { + goto getData; + } + } + } else if ((pi->word[0] == '{') && (pi->word[1] == 0)) { + + return 0; + } + } + +getData: + *widthPtr = width; + *heightPtr = height; + return 1; +} + + +/* + *---------------------------------------------------------------------- + * + * ChnWrite + * + * Writes a XBM image to a file. Just calls CommonWrite + * with appropriate arguments. + * + * Results: + * Returns the return value of CommonWrite + * + * Side effects: + * A file is (hopefully) created on success. + * + *---------------------------------------------------------------------- + */ +static int +ChnWrite(interp, fileName, format, blockPtr) + Tcl_Interp *interp; + const char *fileName; + Tcl_Obj *format; + Tk_PhotoImageBlock *blockPtr; +{ + return CommonWrite(interp, fileName, (Tcl_DString *)NULL, format, blockPtr); +} + + +/* + *---------------------------------------------------------------------- + * + * StringWrite + * + * Writes a XBM image to a string. Just calls CommonWrite + * with appropriate arguments. + * + * Results: + * Returns the return value of CommonWrite + * + * Side effects: + * The Tcl_DString dataPtr is modified on success. + * + *---------------------------------------------------------------------- + */ +static int StringWrite( + Tcl_Interp *interp, + Tcl_Obj *format, + Tk_PhotoImageBlock *blockPtr +) { + int result; + Tcl_DString data; + + Tcl_DStringInit(&data); + result = CommonWrite(interp, "InlineData", &data, format, blockPtr); + + if (result == TCL_OK) { + Tcl_DStringResult(interp, &data); + } else { + Tcl_DStringFree(&data); + } + return result; +} + + +/* + * Yes, I know these macros are dangerous. But it should work fine + */ +#define WRITE(buf) { if (chan) Tcl_Write(chan, buf, -1); else Tcl_DStringAppend(dataPtr, buf, -1);} + +/* + *---------------------------------------------------------------------- + * + * CommonWrite + * + * This procedure writes a XBM image to the file filename + * (if filename != NULL) or to dataPtr. + * + * Results: + * Returns TCL_OK on success, or TCL_ERROR on error. + * + * Side effects: + * varies (see StringWrite and ChnWrite) + * + *---------------------------------------------------------------------- + */ +static int +CommonWrite(interp, fileName, dataPtr, format, blockPtr) + Tcl_Interp *interp; + const char *fileName; + Tcl_DString *dataPtr; + Tcl_Obj *format; + Tk_PhotoImageBlock *blockPtr; +{ + Tcl_Channel chan = (Tcl_Channel) NULL; + char buffer[256]; + unsigned char *pp; + int x, y, value, mask; + int sep = ' '; + int alphaOffset; + char *p = (char *) NULL; + char *imgName; + static const char header[] = +"#define %s_width %d\n\ +#define %s_height %d\n\ +static char %s_bits[] = {\n"; + + alphaOffset = blockPtr->offset[0]; + if (alphaOffset < blockPtr->offset[1]) alphaOffset = blockPtr->offset[1]; + if (alphaOffset < blockPtr->offset[2]) alphaOffset = blockPtr->offset[2]; + if (++alphaOffset < blockPtr->pixelSize) { + alphaOffset -= blockPtr->offset[0]; + } else { + alphaOffset = 0; + } + + + /* open the output file (if needed) */ + if (!dataPtr) { + chan = Tcl_OpenFileChannel(interp, (CONST84 char *) fileName, "w", 0644); + if (!chan) { + return TCL_ERROR; + } + } + + /* compute image name */ + imgName = (char*)ckalloc(strlen(fileName)+1); + memcpy (imgName, fileName, strlen(fileName)+1); + p = strrchr(imgName, '/'); + if (p) { + imgName = p+1; + } + p = strrchr(imgName, '\\'); + if (p) { + imgName = p+1; + } + p = strrchr(imgName, ':'); + if (p) { + imgName = p+1; + } + p = strchr(imgName, '.'); + if (p) { + *p = 0; + } + + sprintf(buffer, header, imgName, blockPtr->width, imgName, + blockPtr->height, imgName); + WRITE(buffer); + + /* write image itself */ + pp = blockPtr->pixelPtr + blockPtr->offset[0]; + sep = ' '; + for (y = 0; y < blockPtr->height; y++) { + value = 0; + mask = 1; + for (x = 0; x < blockPtr->width; x++) { + if (!alphaOffset || pp[alphaOffset]) { + value |= mask; + } else { + /* make transparent pixel */ + } + pp += blockPtr->pixelSize; + mask <<= 1; + if (mask >= 256) + { + sprintf(buffer,"%c 0x%02x",sep,value); + WRITE(buffer); + value = 0; + mask = 1; + sep = ','; + } + } + if (mask != 1) { + sprintf(buffer,"%c 0x%02x",sep, value); + WRITE(buffer); + } + + if (y == blockPtr->height - 1) { + WRITE("};\n"); + } else { + WRITE(",\n"); + sep = ' '; + } + } + + /* close the channel */ + if (chan) { + Tcl_Close(interp, chan); + } + return TCL_OK; +} |