summaryrefslogtreecommitdiffstats
path: root/tkimg/xbm/xbm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkimg/xbm/xbm.c')
-rw-r--r--tkimg/xbm/xbm.c666
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;
+}