summaryrefslogtreecommitdiffstats
path: root/tkimg/xpm/xpm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkimg/xpm/xpm.c')
-rw-r--r--tkimg/xpm/xpm.c1032
1 files changed, 1032 insertions, 0 deletions
diff --git a/tkimg/xpm/xpm.c b/tkimg/xpm/xpm.c
new file mode 100644
index 0000000..3c5f030
--- /dev/null
+++ b/tkimg/xpm/xpm.c
@@ -0,0 +1,1032 @@
+/*
+ * xpm.c --
+ *
+ * A photo image file handler for XPM files.
+ *
+ * Written by:
+ * Jan Nijtmans
+ * email: nijtmans@users.sourceforge.net
+ * url: http://purl.oclc.org/net/nijtmans/
+ *
+ * (with some code stolen from the XPM image type and the GIF handler)
+ *
+ * <mweilguni@sime.com> Mario Weilguni
+ * Jan 1997:
+ * - fixed a bug when reading images with invalid color tables
+ * (if a pixelchar is not in the colormap)
+ * - added FileWriteXPM, StringWriteXPM and WriteXPM.
+ * - modified FileReadXPM, ReadXPM and added StringReadXPM
+ * This makes the XPM reader complete :-)
+ *
+ * <paul@poSoft.de> Paul Obermeier
+ * Jan 2001:
+ * - Bugfix in CommonWriteXPM: const char *fileName was overwritten.
+ * - Bugfix in CommonWriteXPM: If used with "-from" option, dumped core.
+ * - Improve in CommonWriteXPM: Greater channel buffersize for better
+ * Win98 (i.e. FAT32) performance.
+ * - Improve in ChnRead: Better performance by using ReadBuffer.
+ *
+ * $Id: xpm.c,v 1.1.1.1 2016/01/25 21:20:47 joye Exp $
+ */
+
+/*
+ * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
+ */
+
+#include "init.c"
+
+#if defined(__WIN32__) && !defined(__GNUC__)
+#define strncasecmp strnicmp
+#endif
+
+#ifndef MAC_TCL
+#include <sys/types.h>
+#endif
+
+/* constants used only in this file */
+
+#define XPM_MONO 1
+#define XPM_GRAY_4 2
+#define XPM_GRAY 3
+#define XPM_COLOR 4
+#define XPM_SYMBOLIC 5
+#define XPM_UNKNOWN 6
+
+#define MAX_BUFFER 4096
+
+/*
+ * Prototypes for local procedures defined in this file:
+ */
+
+static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
+ 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 ReadXPMFileHeader(tkimg_MFile *handle,
+ int *widthPtr, int *heightPtr, int *numColors, int *byteSize);
+static char *GetType(char *colorDefn, int *type_ret);
+static char *GetColor(char *colorDefn, char *colorName, int *type_ret);
+static char *Gets(tkimg_MFile *handle, char *buffer, int size);
+
+/*
+ *----------------------------------------------------------------------
+ * Gets
+ *
+ * Allows other routines to get their data from channels, files as well
+ * as Tcl_DStrings.
+ *
+ * Results:
+ * same as fgets: NULL pointer on any error, otherwise
+ * it returns buffer
+ *
+ * Side effects:
+ * The access position of the file changes OR
+ * The access pointer dataPtr->p is changed
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+Gets(handle, buffer, size)
+ tkimg_MFile *handle;
+ char *buffer;
+ int size;
+{
+ char *p;
+
+ /* read data from tkimg_MFile */
+ p = buffer;
+ while ((tkimg_Read(handle, p, 1) == 1)) {
+ if (--size <= 0) {
+ *p = 0; return buffer;
+ }
+ if (*p++ == '\n') {
+ *p = 0;
+ return buffer;
+ }
+ }
+ *p = 0;
+ return (p != buffer) ? buffer :(char *) NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjMatch --
+ *
+ * This procedure is invoked by the photo image type to see if
+ * a datastring contains image data in XPM format.
+ *
+ * Results:
+ * The return value is >0 if the first characters in data look
+ * like XPM 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 object, or NULL. */
+ int *widthPtr, /* The dimensions of the image are */
+ int *heightPtr, /* returned here if the file is a valid
+ * raw XPM file. */
+ Tcl_Interp *interp
+) {
+ int numColors, byteSize;
+ tkimg_MFile handle;
+
+ handle.data = (char *)tkimg_GetStringFromObj(data, &handle.length);
+ handle.state = IMG_STRING;
+
+ return ReadXPMFileHeader(&handle, widthPtr, heightPtr, &numColors, &byteSize);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChnMatch --
+ *
+ * This procedure is invoked by the photo image type to see if
+ * a file contains image data in XPM format.
+ *
+ * Results:
+ * The return value is >0 if the first characters in channel "chan"
+ * look like XPM 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 XPM file. */
+ Tcl_Interp *interp
+) {
+ int numColors, byteSize;
+ tkimg_MFile handle;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ return ReadXPMFileHeader(&handle, widthPtr, heightPtr, &numColors, &byteSize);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CommonRead --
+ *
+ * This procedure is called by the photo image type to read
+ * XPM 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, handle, format, imageHandle, destX, destY,
+ width, height, srcX, srcY)
+ Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
+ tkimg_MFile *handle; /* The image channel, open for reading. */
+ 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. */
+{
+ int fileWidth = 0, fileHeight = 0, numColors = 0, byteSize = 0;
+ int h, type;
+ int nchan, matte = 1;
+ unsigned char *pixelPtr;
+ Tk_PhotoImageBlock block;
+ Tcl_HashTable colorTable;
+ Tk_Window tkwin = Tk_MainWindow(interp);
+ Display *display = Tk_Display(tkwin);
+ Colormap colormap = Tk_Colormap(tkwin);
+ int depth = Tk_Depth(tkwin);
+ char *p;
+ char buffer[MAX_BUFFER];
+ int i, isMono;
+ int color1;
+ unsigned int data;
+ Tcl_HashEntry *hPtr;
+ int result = TCL_OK;
+
+ Tcl_InitHashTable(&colorTable, TCL_ONE_WORD_KEYS);
+
+ switch ((Tk_Visual(tkwin))->class) {
+ case StaticGray:
+ case GrayScale:
+ isMono = 1;
+ break;
+ default:
+ isMono = 0;
+ }
+
+ type = ReadXPMFileHeader(handle, &fileWidth, &fileHeight, &numColors, &byteSize);
+ if (type == 0) {
+ Tcl_AppendResult(interp, "couldn't read raw XPM header", NULL);
+ return TCL_ERROR;
+ }
+ if ((fileWidth <= 0) || (fileHeight <= 0)) {
+ Tcl_AppendResult(interp, "XPM image file has dimension(s) <= 0",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if ((byteSize < 1) || (byteSize > 4)) {
+ Tcl_AppendResult(interp, "XPM image file has invalid byte size ",
+ "(should be 1, 2, 3 or 4)", (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;
+ }
+
+ for (i=0; i<numColors; i++) {
+ char * colorDefn; /* the color definition line */
+ char * colorName; /* temp place to hold the color name
+ * defined for one type of visual */
+ char * useName; /* the color name used for this
+ * color. If there are many names
+ * defined, choose the name that is
+ * "best" for the target visual
+ */
+ XColor color;
+ int found;
+
+ p = Gets(handle, buffer,MAX_BUFFER);
+ while (((p = strchr(p,'\"')) == NULL)) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return TCL_ERROR;
+ }
+ p = buffer;
+ }
+ colorDefn = p + byteSize + 1;
+ colorName = (char*)ckalloc(strlen(colorDefn)+1);
+ useName = (char*)ckalloc(strlen(colorDefn)+1);
+ found = 0;
+ color1 = 0;
+ data = 0;
+
+ while (colorDefn && *colorDefn) {
+ int type;
+
+ if ((colorDefn=GetColor(colorDefn, colorName, &type)) == NULL) {
+ break;
+ }
+ if (colorName[0] == '\0') {
+ continue;
+ }
+
+ switch (type) {
+ case XPM_MONO:
+ if (isMono && depth == 1) {
+ strcpy(useName, colorName);
+ found = 1; goto gotcolor;
+ }
+ break;
+ case XPM_GRAY_4:
+ if (isMono && depth == 4) {
+ strcpy(useName, colorName);
+ found = 1; goto gotcolor;
+ }
+ break;
+ case XPM_GRAY:
+ if (isMono && depth > 4) {
+ strcpy(useName, colorName);
+ found = 1; goto gotcolor;
+ }
+ break;
+ case XPM_COLOR:
+ if (!isMono) {
+ strcpy(useName, colorName);
+ found = 1; goto gotcolor;
+ }
+ break;
+ }
+ if (type != XPM_SYMBOLIC && type != XPM_UNKNOWN) {
+ if (!found) { /* use this color as default */
+ strcpy(useName, colorName);
+ found = 1;
+ }
+ }
+ }
+
+ gotcolor:
+
+ memcpy(&color1, p+1, byteSize);
+ p = useName;
+ while ((*p != 0) && (*p != '"') && (*p != '\t')) {
+ p++;
+ }
+ *p = 0;
+
+ data = 0;
+ if (strncasecmp(useName, "none",4)) {
+ if (XParseColor(display, colormap, useName, &color) == 0) {
+ color.red = color.green = color.blue = 0;
+ }
+ ((unsigned char *) &data)[0] = color.red>>8;
+ ((unsigned char *) &data)[1] = color.green>>8;
+ ((unsigned char *) &data)[2] = color.blue>>8;
+ ((unsigned char *) &data)[3] = 255;
+ }
+
+ hPtr = Tcl_CreateHashEntry(&colorTable, (char *) (size_t) color1, &found);
+ Tcl_SetHashValue(hPtr, (char *) (size_t) data);
+
+ ckfree(colorName);
+ ckfree(useName);
+ }
+
+ Tk_PhotoGetImage(imageHandle, &block);
+
+ if (tkimg_PhotoExpand(interp, imageHandle, destX + width, destY + height) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ nchan = block.pixelSize;
+ block.pitch = nchan * fileWidth;
+ block.width = width;
+ block.height = 1;
+ block.offset[0] = 0;
+ block.offset[1] = 1;
+ block.offset[2] = 2;
+ block.offset[3] = (nchan == 4 && matte? 3: 0);
+ block.pixelPtr = (unsigned char *) ckalloc((unsigned) nchan * width);
+
+ i = srcY;
+ while (i-- > 0) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ while (((p = strchr(p,'\"')) == NULL)) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return TCL_ERROR;
+ }
+ p = buffer;
+ }
+ }
+
+
+ for (h = height; h > 0; h--) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ while (((p = strchr(p,'\"')) == NULL)) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return TCL_ERROR;
+ }
+ p = buffer;
+ }
+ p += byteSize * srcX + 1;
+ pixelPtr = block.pixelPtr;
+
+ for (i = 0; i < width; ) {
+ unsigned int col;
+
+ memcpy((char *) &color1, p, byteSize);
+ hPtr = Tcl_FindHashEntry(&colorTable, (char *) (size_t) color1);
+
+ /*
+ * if hPtr == NULL, we have an invalid color entry in the XPM
+ * file. We use transparant as default color
+ */
+ if (hPtr != (Tcl_HashEntry *)NULL)
+ col = (unsigned int) (size_t) Tcl_GetHashValue(hPtr);
+ else
+ col = (unsigned int) 0;
+
+ /*
+ * we've found a non-transparent pixel, let's search the next
+ * transparent pixel and copy this block to the image
+ */
+ if (col) {
+ int len = 0, j;
+
+ j = i;
+ pixelPtr = block.pixelPtr;
+ do {
+ memcpy(pixelPtr, &col, block.pixelSize);
+ pixelPtr += block.pixelSize;
+ i++;
+ len++;
+ p += byteSize;
+
+ if (i < width) {
+ memcpy((char *) &color1, p, byteSize);
+ hPtr = Tcl_FindHashEntry(&colorTable, (char *) (size_t) color1);
+ if (hPtr != (Tcl_HashEntry *)NULL)
+ col = (unsigned int) (size_t) Tcl_GetHashValue(hPtr);
+ else
+ col = (unsigned int) 0;
+ }
+ } while ((i < width) && col);
+ if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX+j, destY, len, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
+ result = TCL_ERROR;
+ break;
+ }
+ } else {
+ p += byteSize;
+ i++;
+ }
+ }
+ destY++;
+ }
+
+ Tcl_DeleteHashTable(&colorTable);
+
+ ckfree((char *) block.pixelPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChnRead --
+ *
+ * This procedure is called by the photo image type to read
+ * XPM 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. */
+{
+ tkimg_MFile handle;
+ int retVal;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ tkimg_ReadBuffer (1);
+ retVal = CommonRead (interp, &handle, format, imageHandle,
+ destX, destY, width, height, srcX, srcY);
+ tkimg_ReadBuffer (0);
+ return retVal;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjRead --
+ *
+ * This procedure is called by the photo image type to read
+ * XPM 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 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. */
+{
+ tkimg_MFile handle;
+
+ handle.data = (char *)tkimg_GetStringFromObj(data, &handle.length);
+ handle.state = IMG_STRING;
+
+ return CommonRead(interp, &handle, format, imageHandle,
+ destX, destY, width, height, srcX, srcY);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadXPMFileHeader --
+ *
+ * This procedure reads the XPM header from the beginning of a
+ * XPM file and returns information from the header.
+ *
+ * Results:
+ * The return value is 1 if file "f" appears to start with a valid
+ * XPM 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))
+
+static int
+ReadXPMFileHeader(handle, widthPtr, heightPtr, numColors, byteSize)
+ tkimg_MFile *handle; /* handle to read the header from */
+ int *widthPtr, *heightPtr; /* The dimensions of the image are
+ * returned here. */
+ int *numColors; /* the number of colors is returned here */
+ int *byteSize; /* number of bytes per pixel */
+{
+ char buffer[MAX_BUFFER];
+ char *p;
+
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return 0;
+ }
+ p = buffer;
+ while (*p && isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (strncmp("/* XPM", p, 6) != 0) {
+ return 0;
+ }
+ while ((p = strchr(p,'{')) == NULL) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return 0;
+ }
+ p = buffer;
+ }
+ while ((p = strchr(p,'"')) == NULL) {
+ p = Gets(handle, buffer,MAX_BUFFER);
+ if (p == NULL) {
+ return 0;
+ }
+ p = buffer;
+ }
+ p++;
+ while (p && *p && isspace(UCHAR(*p))) {
+ p++;
+ }
+ *widthPtr = strtoul(p, &p, 0);
+ if (p == NULL || *widthPtr <= 0) {
+ return 0;
+ }
+ while (p && *p && isspace(UCHAR(*p))) {
+ p++;
+ }
+ *heightPtr = strtoul(p, &p, 0);
+ if (p == NULL || *heightPtr <= 0) {
+ return 0;
+ }
+ while (p && *p && isspace(UCHAR(*p))) {
+ p++;
+ }
+ *numColors = strtoul(p, &p, 0);
+ if (p == NULL) {
+ return 0;
+ }
+ while (p && *p && isspace(UCHAR(*p))) {
+ p++;
+ }
+ *byteSize = strtoul(p, &p, 0);
+ if (p == NULL) {
+ return 0;
+ }
+ return 1;
+}
+
+static char * GetType(colorDefn, type_ret)
+ char * colorDefn;
+ int * type_ret;
+{
+ char * p = colorDefn;
+
+ /* skip white spaces */
+ while (*p && isspace(UCHAR(*p))) {
+ p ++;
+ }
+
+ /* parse the type */
+ if (p[0] != '\0' && p[0] == 'm' &&
+ p[1] != '\0' && isspace(UCHAR(p[1]))) {
+ *type_ret = XPM_MONO;
+ p += 2;
+ }
+ else if (p[0] != '\0' && p[0] == 'g' &&
+ p[1] != '\0' && p[1] == '4' &&
+ p[2] != '\0' && isspace(UCHAR(p[2]))) {
+ *type_ret = XPM_GRAY_4;
+ p += 3;
+ }
+ else if (p[0] != '\0' && p[0] == 'g' &&
+ p[1] != '\0' && isspace(UCHAR(p[1]))) {
+ *type_ret = XPM_GRAY;
+ p += 2;
+ }
+ else if (p[0] != '\0' && p[0] == 'c' &&
+ p[1] != '\0' && isspace(UCHAR(p[1]))) {
+ *type_ret = XPM_COLOR;
+ p += 2;
+ }
+ else if (p[0] != '\0' && p[0] == 's' &&
+ p[1] != '\0' && isspace(UCHAR(p[1]))) {
+ *type_ret = XPM_SYMBOLIC;
+ p += 2;
+ }
+ else {
+ *type_ret = XPM_UNKNOWN;
+ return NULL;
+ }
+
+ return p;
+}
+
+/* colorName is guaranteed to be big enough */
+static char * GetColor(colorDefn, colorName, type_ret)
+ char * colorDefn;
+ char * colorName; /* if found, name is copied to this array */
+ int * type_ret;
+{
+ int type;
+ char * p;
+
+ if (!colorDefn) {
+ return NULL;
+ }
+
+ if ((colorDefn = GetType(colorDefn, &type)) == NULL) {
+ /* unknown type */
+ return NULL;
+ }
+ else {
+ *type_ret = type;
+ }
+
+ /* skip white spaces */
+ while (*colorDefn && isspace(UCHAR(*colorDefn))) {
+ colorDefn ++;
+ }
+
+ p = colorName;
+
+ while (1) {
+ int dummy;
+
+ while (*colorDefn && !isspace(UCHAR(*colorDefn))) {
+ *p++ = *colorDefn++;
+ }
+
+ if (!*colorDefn) {
+ break;
+ }
+
+ if (GetType(colorDefn, &dummy) == NULL) {
+ /* the next string should also be considered as a part of a color
+ * name */
+
+ while (*colorDefn && isspace(UCHAR(*colorDefn))) {
+ *p++ = *colorDefn++;
+ }
+ } else {
+ break;
+ }
+ if (!*colorDefn) {
+ break;
+ }
+ }
+
+ /* Mark the end of the colorName */
+ *p = '\0';
+
+ return colorDefn;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChnWrite
+ *
+ * Writes a XPM 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 XPM 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 (!dataPtr) Tcl_Write(chan, buf, -1); else Tcl_DStringAppend(dataPtr, buf, -1);}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CommonWrite
+ *
+ * This procedure writes a XPM image to the file filename
+ * (if filename != NULL) or to dataPtr.
+ *
+ * Results:
+ * Returns TCL_OK on success, or TCL_ERROR on error.
+ * Possible failures are:
+ * 1. cannot access file (permissions or path not found)
+ * 2. TkPhotoGetMask fails to retrieve the region mask
+ * for the image (should not happen)
+ *
+ * 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;
+{
+ int x, y, i;
+ int found;
+ Tcl_Channel chan = (Tcl_Channel) NULL;
+ Tcl_HashTable colors;
+ Tcl_HashEntry *entry;
+ Tcl_HashSearch search;
+ unsigned char *pp;
+ unsigned char *pixelPtr, *rowPixPtr;
+ int ncolors, chars_per_pixel;
+ int greenOffset, blueOffset, alphaOffset;
+ union {
+ ClientData value;
+ char component[3];
+ } col;
+ union {
+ ClientData value;
+ char component[5];
+ } temp;
+ char buffer[256], *p, *imgName;
+
+ /*
+ * xpm_chars[] must be 64 chars long
+ */
+ static char xpm_chars[] =
+ ".#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
+ blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
+ 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;
+ }
+ if (Tcl_SetChannelOption (interp, chan, "-buffersize", "131072") != TCL_OK) {
+ Tcl_Close(interp, 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, "/* XPM */\nstatic char * %s[] = {\n", imgName);
+ WRITE(buffer);
+
+ /*
+ * Compute size of colortable
+ */
+ Tcl_InitHashTable(&colors, TCL_ONE_WORD_KEYS);
+ ncolors = 0;
+ col.value = 0;
+ for (y = 0; y < blockPtr->height; y++) {
+ pp = blockPtr->pixelPtr + y * blockPtr->pitch + blockPtr->offset[0];
+ for (x = blockPtr->width; x >0; x--) {
+ if (!alphaOffset || pp[alphaOffset]) {
+ col.component[0] = pp[0];
+ col.component[1] = pp[greenOffset];
+ col.component[2] = pp[blueOffset];
+ if (Tcl_FindHashEntry(&colors, (char *) col.value) == NULL) {
+ ncolors++;
+ entry = Tcl_CreateHashEntry(&colors, (char *) col.value,
+ &found);
+ }
+ }
+ pp += blockPtr->pixelSize;
+ }
+ }
+
+ /* compute number of characters per pixel */
+ chars_per_pixel = 1;
+ i = ncolors;
+ while (i > 64) {
+ chars_per_pixel++;
+ i /= 64;
+ }
+
+ /* write image info into XPM */
+ sprintf(buffer, "\"%d %d %d %d\",\n", blockPtr->width, blockPtr->height,
+ ncolors+(alphaOffset != 0), chars_per_pixel);
+ WRITE(buffer);
+
+ /* write transparent color id if transparency is available*/
+ if (alphaOffset) {
+ strcpy(temp.component, " ");
+ temp.component[chars_per_pixel] = 0;
+ sprintf(buffer, "\"%s s None c None\",\n", temp.component);
+ WRITE(buffer);
+ }
+
+ /* write colormap strings */
+ entry = Tcl_FirstHashEntry(&colors, &search);
+ y = 0;
+ temp.component[chars_per_pixel] = 0;
+ while (entry) {
+ /* compute a color identifier for color #y */
+ for (i = 0, x = y++; i < chars_per_pixel; i++, x /= 64)
+ temp.component[i] = xpm_chars[x & 63];
+
+ /*
+ * and put it in the hashtable
+ * this is a little bit tricky
+ */
+ Tcl_SetHashValue(entry, (char *) temp.value);
+ pp = (unsigned char *)&entry->key.oneWordValue;
+ sprintf(buffer, "\"%s c #%02x%02x%02x\",\n",
+ temp.component, pp[0], pp[1], pp[2]);
+ WRITE(buffer);
+ entry = Tcl_NextHashEntry(&search);
+ }
+
+ /* write image itself */
+ rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0];
+ buffer[chars_per_pixel] = 0;
+ for (y = 0; y < blockPtr->height; y++) {
+ WRITE("\"");
+ pixelPtr = rowPixPtr;
+ for (x = 0; x < blockPtr->width; x++) {
+ if (!alphaOffset || pixelPtr[alphaOffset]) {
+ col.component[0] = pixelPtr[0];
+ col.component[1] = pixelPtr[greenOffset];
+ col.component[2] = pixelPtr[blueOffset];
+ entry = Tcl_FindHashEntry(&colors, (char *) col.value);
+ temp.value = Tcl_GetHashValue(entry);
+ memcpy(buffer, temp.component, chars_per_pixel);
+ } else {
+ /* make transparent pixel */
+ memcpy(buffer, " ", chars_per_pixel);
+ }
+ pixelPtr += blockPtr->pixelSize;
+ WRITE(buffer);
+ }
+ if (y == blockPtr->height - 1) {
+ WRITE("\"};");
+ } else {
+ WRITE("\",\n");
+ }
+ rowPixPtr += blockPtr->pitch;
+ }
+
+ /* Delete the hash table */
+ Tcl_DeleteHashTable(&colors);
+
+ /* close the file */
+ if (chan) {
+ Tcl_Close(interp, chan);
+ }
+ return TCL_OK;
+}