summaryrefslogtreecommitdiffstats
path: root/tkimg/ico/ico.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkimg/ico/ico.c')
-rw-r--r--tkimg/ico/ico.c1082
1 files changed, 1082 insertions, 0 deletions
diff --git a/tkimg/ico/ico.c b/tkimg/ico/ico.c
new file mode 100644
index 0000000..5ec2699
--- /dev/null
+++ b/tkimg/ico/ico.c
@@ -0,0 +1,1082 @@
+/* STARTHEADER
+ *
+ * File : ico.c
+ *
+ * Author : Paul Obermeier (paul@poSoft.de)
+ *
+ * Date : Mon Aug 12 20:30:46 CEST 2002
+ *
+ * Copyright : (C) 2002 Paul Obermeier
+ *
+ * Description :
+ *
+ * A photo image handler for Windows Icon file format.
+ *
+ * The following icon types are supported:
+ *
+ * 1-bit pixels: Black and White.
+ * 4-bit pixels: Grayscale or indexed.
+ * 8-bit pixels: Grayscale or indexed.
+ * 24-bit pixels: True-color (GBR, each channel 8 bit).
+ * 32-bit pixels: True-color (GBRA, each channel 8 bit).
+ *
+ * List of currently supported features:
+ *
+ * Type | Read | Write |
+ * | -file | -data | -file | -data |
+ * ----------------------------------------
+ * 1-bit | Yes | Yes | No | No |
+ * 4-bit | Yes | Yes | No | No |
+ * 8-bit | Yes | Yes | Yes | Yes |
+ * 24-bit | Yes | Yes | Yes | Yes |
+ * 32-bit | Yes | Yes | No | No |
+ *
+ *
+ * The following format options are available:
+ *
+ * Read ICO image: "ico -verbose <bool> -index <uint>"
+ * Write ICO image: "ico -verbose <bool>"
+ *
+ * -verbose <bool>: If set to true, additional information about the file
+ * format is printed to stdout. Default is "false".
+ * -index <uint>: Read the icon with specified index. Default is 0.
+ *
+ * Notes:
+ *
+ *
+ * ENDHEADER
+ *
+ * $Id: ico.c,v 1.1.1.1 2016/01/25 21:20:46 joye Exp $
+ *
+ */
+
+/*
+ * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
+ */
+
+#include "init.c"
+
+
+/* #define DEBUG_LOCAL */
+
+/* Some defines and typedefs. */
+#define TRUE 1
+#define FALSE 0
+typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */
+typedef unsigned char UByte; /* Unsigned 8 bit integer */
+typedef char Byte; /* Signed 8 bit integer */
+typedef unsigned short UShort; /* Unsigned 16 bit integer */
+typedef short Short; /* Signed 16 bit integer */
+typedef unsigned int UInt; /* Unsigned 32 bit integer */
+typedef int Int; /* Signed 32 bit integer */
+
+#define BI_RGB 0
+
+typedef struct {
+ UByte width;
+ UByte height;
+ UShort nColors;
+ UByte reserved;
+ UShort nPlanes;
+ UShort bitCount;
+ UInt sizeInBytes;
+ UInt fileOffset;
+} ICOENTRY;
+
+/* ICO file header structure */
+typedef struct {
+ UShort nIcons;
+ ICOENTRY *entries;
+} ICOHEADER;
+
+typedef struct {
+ UInt size;
+ UInt width;
+ UInt height;
+ UShort nPlanes;
+ UShort nBitsPerPixel;
+ UInt compression;
+ UInt imageSize;
+ UInt xPixelsPerM;
+ UInt yPixelsPerM;
+ UInt nColorsUsed;
+ UInt nColorsImportant;
+} INFOHEADER;
+
+typedef struct {
+ UByte red;
+ UByte green;
+ UByte blue;
+ UByte matte;
+} ICOCOLOR;
+
+/* ICO file format options structure for use with ParseFormatOpts */
+typedef struct {
+ UInt index;
+ Boln verbose;
+} FMTOPT;
+
+static Boln readUByte (tkimg_MFile *handle, UByte *b)
+{
+ char buf[1];
+ if (1 != tkimg_Read(handle, buf, 1)) {
+ return FALSE;
+ }
+ *b = buf[0] & 0xFF;
+ return TRUE;
+}
+
+/* Read 2 bytes, representing a unsigned 16 bit integer in the form
+ <LowByte, HighByte>, from a file and convert them into the current
+ machine's format. */
+
+static Boln readUShort (tkimg_MFile *handle, UShort *s)
+{
+ char buf[2];
+ UShort tmp;
+
+ if (2 != tkimg_Read(handle, buf, 2)) {
+ return FALSE;
+ }
+ tmp = buf[0] & 0xFF;
+ tmp |= (buf[1] & 0xFF) << 8;
+ *s = tmp;
+ return TRUE;
+}
+
+/* Read 4 bytes, representing a unsigned 32 bit integer in the form
+ <LowByte, HighByte>, from a file and convert them into the current
+ machine's format. */
+
+static Boln readUInt (tkimg_MFile *handle, UInt *i)
+{
+ char buf[4];
+ UInt tmp;
+
+ if (4 != tkimg_Read(handle, buf, 4)) {
+ return FALSE;
+ }
+ tmp = buf[0] & 0xFF;
+ tmp |= (buf[1] & 0xFF) << 8;
+ tmp |= (buf[2] & 0xFF) << 16;
+ tmp |= (buf[3] & 0xFF) << 24;
+ *i = tmp;
+ return TRUE;
+}
+
+/* Write a byte, representing an unsigned integer to a file. */
+
+static Boln writeUByte (tkimg_MFile *handle, UByte b)
+{
+ UByte buf[1];
+ buf[0] = b;
+ if (1 != tkimg_Write(handle, (const char *)buf, 1)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Convert a unsigned 16 bit integer number into the format
+ <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */
+
+static Boln writeUShort (tkimg_MFile *handle, UShort s)
+{
+ Byte buf[2];
+ buf[0] = (Byte)s;
+ buf[1] = s >> 8;
+ if (2 != tkimg_Write(handle, buf, 2)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Convert a unsigned 32 bit integer number into the format
+ <LowByte, HighByte> (an array of 4 bytes) and write the array to a file. */
+
+static Boln writeUInt (tkimg_MFile *handle, UInt i)
+{
+ Byte buf[4];
+ buf[0] = i;
+ buf[1] = i >> 8;
+ buf[2] = i >> 16;
+ buf[3] = i >> 24;
+ if (4 != tkimg_Write(handle, buf, 4)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define OUT Tcl_WriteChars (outChan, str, -1)
+static void printImgInfo (ICOHEADER *th, INFOHEADER *ih, FMTOPT *opts,
+ const char *filename, const char *msg)
+{
+ Tcl_Channel outChan;
+ char str[256];
+ int i = opts->index;
+
+ outChan = Tcl_GetStdChannel (TCL_STDOUT);
+ if (!outChan) {
+ return;
+ }
+ sprintf(str, "%s %s\n", msg, filename); OUT;
+ sprintf(str, " No. of icons : %d\n", th->nIcons); OUT;
+ sprintf(str, " Icon %d:\n", i); OUT;
+ sprintf(str, " Width and Height: %dx%d\n", ih->width, ih->height/2); OUT;
+ sprintf(str, " Number of colors: %d\n", th->entries[i].nColors); OUT;
+ sprintf(str, " Number of planes: %d\n", ih->nPlanes); OUT;
+ sprintf(str, " Bits per pixel: %d\n", ih->nBitsPerPixel); OUT;
+ sprintf(str, " Size in bytes: %d\n", th->entries[i].sizeInBytes); OUT;
+ sprintf(str, " File offset: %d\n", th->entries[i].fileOffset); OUT;
+ Tcl_Flush(outChan);
+}
+#undef OUT
+
+static Boln readIcoHeader (tkimg_MFile *handle, ICOHEADER *th)
+{
+ int i;
+ UByte nColors;
+ UShort reserved, type, nIcons;
+
+ if (!readUShort (handle, &reserved)) {
+ return FALSE;
+ }
+ if (reserved != 0) {
+ return FALSE;
+ }
+
+ if (!readUShort (handle, &type)) {
+ return FALSE;
+ }
+ if (type != 1) {
+ return FALSE;
+ }
+ if (!readUShort (handle, &nIcons)) {
+ return FALSE;
+ }
+ if (nIcons <= 0) {
+ return FALSE;
+ }
+
+ th->nIcons = nIcons;
+ if (!(th->entries = (ICOENTRY *)ckalloc (sizeof (ICOENTRY) * nIcons))) {
+ return FALSE;
+ }
+
+ for (i=0; i<nIcons; i++) {
+ if (!readUByte (handle, &th->entries[i].width) ||
+ !readUByte (handle, &th->entries[i].height) ||
+ !readUByte (handle, &nColors) ||
+ !readUByte (handle, &th->entries[i].reserved) ||
+ !readUShort (handle, &th->entries[i].nPlanes) ||
+ !readUShort (handle, &th->entries[i].bitCount) ||
+ !readUInt (handle, &th->entries[i].sizeInBytes) ||
+ !readUInt (handle, &th->entries[i].fileOffset)) {
+ ckfree ((char *)th->entries);
+ return FALSE;
+ }
+ th->entries[i].nColors = (nColors == 0? 256: nColors);
+#ifdef DEBUG_LOCAL
+ printf ("Icon %d:\n", i);
+ printf (" Width : %d\n", th->entries[i].width);
+ printf (" Height : %d\n", th->entries[i].height);
+ printf (" Colors : %d\n", th->entries[i].nColors);
+ printf (" Planes : %d\n", th->entries[i].nPlanes);
+ printf (" BitCount : %d\n", th->entries[i].bitCount);
+ printf (" Size : %d\n", th->entries[i].sizeInBytes);
+ printf (" FileOffset: %d\n", th->entries[i].fileOffset);
+#endif
+ }
+ return TRUE;
+}
+
+static Boln writeIcoHeader (tkimg_MFile *handle, ICOHEADER *th)
+{
+ int i;
+ UByte nColors;
+ UShort reserved = 0,
+ type = 1;
+
+ if (!writeUShort (handle, reserved)) {
+ return FALSE;
+ }
+ if (!writeUShort (handle, type)) {
+ return FALSE;
+ }
+ if (!writeUShort (handle, th->nIcons)) {
+ return FALSE;
+ }
+ for (i=0; i<th->nIcons; i++) {
+ nColors = (th->entries[i].nColors == 256? 0: th->entries[i].nColors);
+ if (!writeUByte (handle, th->entries[i].width) ||
+ !writeUByte (handle, th->entries[i].height) ||
+ !writeUByte (handle, nColors) ||
+ !writeUByte (handle, th->entries[i].reserved) ||
+ !writeUShort (handle, th->entries[i].nPlanes) ||
+ !writeUShort (handle, th->entries[i].bitCount) ||
+ !writeUInt (handle, th->entries[i].sizeInBytes) ||
+ !writeUInt (handle, th->entries[i].fileOffset)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static Boln readInfoHeader (tkimg_MFile *handle, INFOHEADER *ih)
+{
+ if (!readUInt (handle, &ih->size) ||
+ !readUInt (handle, &ih->width) ||
+ !readUInt (handle, &ih->height) ||
+ !readUShort (handle, &ih->nPlanes) ||
+ !readUShort (handle, &ih->nBitsPerPixel) ||
+ !readUInt (handle, &ih->compression) ||
+ !readUInt (handle, &ih->imageSize) ||
+ !readUInt (handle, &ih->xPixelsPerM) ||
+ !readUInt (handle, &ih->yPixelsPerM) ||
+ !readUInt (handle, &ih->nColorsUsed) ||
+ !readUInt (handle, &ih->nColorsImportant)) {
+ return FALSE;
+ }
+#ifdef DEBUG_LOCAL
+ printf("Info header:\n");
+ printf("Size: %d\n", ih->size);
+ printf("Width: %d\n", ih->width);
+ printf("Height: %d\n", ih->height);
+ printf("Planes: %d\n", ih->nPlanes);
+ printf("BitsPerPixel: %d\n", ih->nBitsPerPixel);
+ printf("Compression: %d\n", ih->compression);
+ printf("Image size: %d\n", ih->imageSize);
+ printf("XPixelsPerM: %d\n", ih->xPixelsPerM);
+ printf("YPixelsPerM: %d\n", ih->yPixelsPerM);
+ printf("ColorsUsed: %d\n", ih->nColorsUsed);
+ printf("ColorsImportant: %d\n", ih->nColorsImportant);
+#endif
+ return TRUE;
+}
+
+static Boln writeInfoHeader (tkimg_MFile *handle, INFOHEADER *ih)
+{
+ if (!writeUInt (handle, ih->size) ||
+ !writeUInt (handle, ih->width) ||
+ !writeUInt (handle, ih->height) ||
+ !writeUShort (handle, ih->nPlanes) ||
+ !writeUShort (handle, ih->nBitsPerPixel) ||
+ !writeUInt (handle, ih->compression) ||
+ !writeUInt (handle, ih->imageSize) ||
+ !writeUInt (handle, ih->xPixelsPerM) ||
+ !writeUInt (handle, ih->yPixelsPerM) ||
+ !writeUInt (handle, ih->nColorsUsed) ||
+ !writeUInt (handle, ih->nColorsImportant)) {
+ return FALSE;
+ }
+#ifdef DEBUG_LOCAL
+ printf("Writing Info header:\n");
+ printf("Size : %d\n", ih->size);
+ printf("Width : %d\n", ih->width);
+ printf("Height : %d\n", ih->height);
+ printf("Planes : %d\n", ih->nPlanes);
+ printf("BitsPerPixel: %d\n", ih->nBitsPerPixel);
+ printf("Compression : %d\n", ih->compression);
+ printf("Image size : %d\n", ih->imageSize);
+ printf("XPixelsPerM : %d\n", ih->xPixelsPerM);
+ printf("YPixelsPerM : %d\n", ih->yPixelsPerM);
+ printf("ColorsUsed : %d\n", ih->nColorsUsed);
+ printf("ColorsImport: %d\n", ih->nColorsImportant);
+#endif
+ return TRUE;
+}
+
+static Boln readColorMap (tkimg_MFile *handle, int mapSize, ICOCOLOR *colorMap)
+{
+ int i;
+ ICOCOLOR color;
+
+ for (i=0; i<mapSize; i++) {
+ if (!readUByte (handle, &color.blue) ||
+ !readUByte (handle, &color.green) ||
+ !readUByte (handle, &color.red) ||
+ !readUByte (handle, &color.matte)) {
+ return FALSE;
+ }
+ colorMap[i] = color;
+ }
+ return TRUE;
+}
+
+static Boln writeColorMap (tkimg_MFile *handle, int mapSize, ICOCOLOR *colorMap)
+{
+ int i;
+
+ for (i=0; i<mapSize; i++) {
+ if (!writeUByte (handle, colorMap[i].blue) ||
+ !writeUByte (handle, colorMap[i].green) ||
+ !writeUByte (handle, colorMap[i].red) ||
+ !writeUByte (handle, colorMap[i].matte)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Prototypes for local procedures defined in this file.
+ */
+
+static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
+ FMTOPT *opts);
+static int CommonMatch(tkimg_MFile *handle, int *widthPtr,
+ int *heightPtr, ICOHEADER *icoHeaderPtr);
+static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
+ const char *filename, Tcl_Obj *format,
+ Tk_PhotoHandle imageHandle, int destX, int destY,
+ int width, int height, int srcX, int srcY);
+static int CommonWrite(Tcl_Interp *interp, tkimg_MFile *handle,
+ Tk_PhotoImageBlock *blockPtr);
+
+static int ParseFormatOpts (interp, format, opts)
+ Tcl_Interp *interp;
+ Tcl_Obj *format;
+ FMTOPT *opts;
+{
+ static const char *const icoOptions[] = {
+ "-verbose", "-index", NULL
+ };
+ int objc, length, i, index;
+ Tcl_Obj **objv;
+ const char *indexStr, *verboseStr;
+
+ /* Initialize format options with default values. */
+ verboseStr = "0";
+ indexStr = "0";
+
+ if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK)
+ return TCL_ERROR;
+ if (objc) {
+ for (i=1; i<objc; i++) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)icoOptions,
+ "format option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (++i >= objc) {
+ Tcl_AppendResult(interp, "No value for option \"",
+ Tcl_GetStringFromObj (objv[--i], (int *) NULL),
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ switch(index) {
+ case 0:
+ verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 1:
+ indexStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ }
+ }
+ }
+
+ /* OPA TODO: Check for valid integer strings. */
+ opts->index = atoi (indexStr);
+
+ length = strlen (verboseStr);
+ if (!strncmp (verboseStr, "1", length) || \
+ !strncmp (verboseStr, "true", length) || \
+ !strncmp (verboseStr, "on", length)) {
+ opts->verbose = 1;
+ } else if (!strncmp (verboseStr, "0", length) || \
+ !strncmp (verboseStr, "false", length) || \
+ !strncmp (verboseStr, "off", length)) {
+ opts->verbose = 0;
+ } else {
+ Tcl_AppendResult(interp, "invalid verbose mode \"", verboseStr,
+ "\": should be 1 or 0, on or off, true or false",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+static int ChnMatch(
+ Tcl_Channel chan,
+ const char *fileName,
+ Tcl_Obj *format,
+ int *widthPtr,
+ int *heightPtr,
+ Tcl_Interp *interp
+) {
+ tkimg_MFile handle;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ return CommonMatch(&handle, widthPtr, heightPtr, NULL);
+}
+
+static int ObjMatch(
+ Tcl_Obj *data,
+ Tcl_Obj *format,
+ int *widthPtr,
+ int *heightPtr,
+ Tcl_Interp *interp
+) {
+ tkimg_MFile handle;
+
+ if (!tkimg_ReadInit(data, '\000', &handle)) {
+ return 0;
+ }
+ return CommonMatch(&handle, widthPtr, heightPtr, NULL);
+}
+
+static int CommonMatch(handle, widthPtr, heightPtr, icoHeaderPtr)
+ tkimg_MFile *handle;
+ int *widthPtr, *heightPtr;
+ ICOHEADER *icoHeaderPtr;
+{
+ ICOHEADER icoHeader, *headerPtr;
+
+ if (!icoHeaderPtr) {
+ headerPtr = &icoHeader;
+ } else {
+ headerPtr = icoHeaderPtr;
+ }
+ if (!readIcoHeader (handle, headerPtr)) {
+ return 0;
+ }
+
+ *widthPtr = headerPtr->entries[0].width;
+ *heightPtr = headerPtr->entries[0].height;
+
+ if (!icoHeaderPtr) {
+ ckfree ((char *) headerPtr->entries);
+ }
+ return 1;
+}
+
+static int ChnRead(interp, chan, filename, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp;
+ Tcl_Channel chan;
+ const char *filename;
+ Tcl_Obj *format;
+ Tk_PhotoHandle imageHandle;
+ int destX, destY;
+ int width, height;
+ int srcX, srcY;
+{
+ tkimg_MFile handle;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ return CommonRead (interp, &handle, filename, format,
+ imageHandle, destX, destY,
+ width, height, srcX, srcY);
+}
+
+static int ObjRead (interp, data, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp;
+ Tcl_Obj *data;
+ Tcl_Obj *format;
+ Tk_PhotoHandle imageHandle;
+ int destX, destY;
+ int width, height;
+ int srcX, srcY;
+{
+ tkimg_MFile handle;
+
+ tkimg_ReadInit(data, '\000', &handle);
+
+ return CommonRead (interp, &handle, "InlineData", format, imageHandle,
+ destX, destY, width, height, srcX, srcY);
+}
+
+static int CommonRead (interp, handle, filename, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
+ tkimg_MFile *handle; /* The image file, 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. */
+{
+ Tk_PhotoImageBlock block;
+ int x, y;
+ int fileWidth, fileHeight;
+ int icoHeaderWidth, icoHeaderHeight;
+ int outWidth, outHeight, outY;
+ int bytesPerLine;
+ int nBytesToSkip;
+ int errorFlag = TCL_OK;
+ unsigned char *line = NULL, *expline = NULL;
+ char msgStr[1024];
+ ICOHEADER icoHeader;
+ INFOHEADER infoHeader;
+ ICOCOLOR colorMap[256];
+ FMTOPT opts;
+
+ if (ParseFormatOpts(interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (!CommonMatch(handle, &fileWidth, &fileHeight, &icoHeader)) {
+ Tcl_AppendResult(interp, "Error reading header", (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ if (opts.index < 0 || opts.index >= icoHeader.nIcons) {
+ sprintf(msgStr, "Invalid icon index: %d", opts.index);
+ Tcl_AppendResult(interp, msgStr, (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ /* Instead of seeking, which does not work on strings,
+ we calculate the number of bytes from the current position
+ till the start of the INFOHEADER and read these bytes with tkimg_Read. */
+ nBytesToSkip = icoHeader.entries[opts.index].fileOffset -6 -
+ 16 * icoHeader.nIcons;
+ if (nBytesToSkip > 0) {
+ char *dummy = ckalloc (nBytesToSkip);
+ tkimg_Read(handle, dummy, nBytesToSkip);
+ ckfree ((char *) dummy);
+ }
+
+ /* Read Info header and color map */
+ if (!readInfoHeader (handle, &infoHeader)) {
+ Tcl_AppendResult(interp, "Error reading info header", (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ if (infoHeader.compression != BI_RGB) {
+ sprintf(msgStr,"Unsupported compression type (%d)", infoHeader.compression);
+ Tcl_AppendResult(interp, msgStr, (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ if (infoHeader.nBitsPerPixel != 24 && infoHeader.nBitsPerPixel != 32) {
+ if (!readColorMap (handle, icoHeader.entries[opts.index].nColors,
+ colorMap)) {
+ Tcl_AppendResult(interp, "Error reading color map", (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+ }
+
+ fileWidth = infoHeader.width;
+ fileHeight = infoHeader.height / 2;
+ icoHeaderWidth = icoHeader.entries[opts.index].width;
+ icoHeaderHeight = icoHeader.entries[opts.index].height;
+ if (icoHeaderWidth == 0) {
+ icoHeaderWidth = 256;
+ }
+ if (icoHeaderHeight == 0) {
+ icoHeaderHeight = 256;
+ }
+ if (fileWidth != icoHeaderWidth || fileHeight != icoHeaderHeight) {
+ sprintf(msgStr,"ICO sizes don't match (%dx%d) vs. (%dx%d)",
+ fileWidth, fileHeight,
+ icoHeaderWidth, icoHeaderHeight);
+ Tcl_AppendResult(interp, msgStr, (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+ outWidth = fileWidth;
+ outHeight = fileHeight;
+ if (fileWidth != width || fileHeight != height) {
+ if (srcX != 0 || srcY != 0 || destX != 0 || destY != 0) {
+ if ((srcX + width) > fileWidth) {
+ outWidth = fileWidth - srcX;
+ } else {
+ outWidth = width;
+ }
+ if ((srcY + height) > fileHeight) {
+ outHeight = fileHeight - srcY;
+ } else {
+ outHeight = height;
+ }
+ }
+ }
+ if ((outWidth <= 0) || (outHeight <= 0)
+ || (srcX >= fileWidth) || (srcY >= fileHeight)) {
+ return TCL_OK;
+ }
+
+ if (opts.verbose) {
+ printImgInfo (&icoHeader, &infoHeader, &opts,
+ filename, "Reading image:");
+ }
+
+ if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ bytesPerLine = ((infoHeader.nBitsPerPixel * fileWidth + 31)/32)*4;
+
+ block.pixelSize = 4;
+ block.pitch = fileWidth * 4;
+ block.width = outWidth;
+ block.height = 1;
+ block.offset[0] = 0;
+ block.offset[1] = 1;
+ block.offset[2] = 2;
+ block.offset[3] = 3;
+ block.pixelPtr = (unsigned char *) ckalloc (4 * fileWidth * fileHeight);
+ expline = block.pixelPtr;
+
+ line = (unsigned char *) ckalloc(bytesPerLine);
+ switch (infoHeader.nBitsPerPixel) {
+ case 32:
+ for (y=0; y<fileHeight; y++) {
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x = 0; x < fileWidth; x++) {
+ expline[0] = line[x*4 + 2];
+ expline[1] = line[x*4 + 1];
+ expline[2] = line[x*4 + 0];
+ expline[3] = line[x*4 + 3];
+ expline += 4;
+ }
+ }
+ break;
+ case 24:
+ for (y=0; y<fileHeight; y++) {
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x = 0; x < fileWidth; x++) {
+ expline[0] = line[x*3 + 2];
+ expline[1] = line[x*3 + 1];
+ expline[2] = line[x*3 + 0];
+ expline += 4;
+ }
+ }
+ break;
+ case 8:
+ for (y=0; y<fileHeight; y++) {
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x = 0; x < fileWidth; x++) {
+ expline[0] = colorMap[line[x]].red;
+ expline[1] = colorMap[line[x]].green;
+ expline[2] = colorMap[line[x]].blue;
+ expline += 4;
+ }
+ }
+ break;
+ case 4:
+ for (y=0; y<fileHeight; y++) {
+ int c;
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x=0; x<fileWidth; x++) {
+ if (x&1) {
+ c = line[x/2] & 0x0f;
+ } else {
+ c = line[x/2] >> 4;
+ }
+ expline[0] = colorMap[c].red;
+ expline[1] = colorMap[c].green;
+ expline[2] = colorMap[c].blue;
+ expline += 4;
+ }
+ }
+ break;
+ case 1:
+ for (y=0; y<fileHeight; y++) {
+ int c;
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x=0; x<fileWidth; x++) {
+ c = (line[x/8] >> (7-(x%8))) & 1;
+ expline[0] = colorMap[c].red;
+ expline[1] = colorMap[c].green;
+ expline[2] = colorMap[c].blue;
+ expline += 4;
+ }
+ }
+ break;
+ default:
+ sprintf(msgStr,"%d-bits ICO file not supported",
+ infoHeader.nBitsPerPixel);
+ Tcl_AppendResult(interp, msgStr, (char *)NULL);
+ errorFlag = TCL_ERROR;
+ goto error;
+ }
+
+ if (infoHeader.nBitsPerPixel != 32) {
+ /* Read XAND bitmap. We don't need to read the alpha bitmap, if
+ alpha is supplied already in the 32-bit case. */
+ bytesPerLine = ((1 * fileWidth + 31)/32)*4;
+
+ expline = block.pixelPtr;
+ for (y=0; y<fileHeight; y++) {
+ int c;
+ tkimg_Read(handle, (char *)line, bytesPerLine);
+ for (x=0; x<fileWidth; x++) {
+ c = (line[x/8] >> (7-(x%8))) & 1;
+ expline[3] = (c? 0: 255);
+ expline += 4;
+ }
+ }
+ }
+
+ /* Store the pointer to allocated buffer for later freeing. */
+ expline = block.pixelPtr;
+ block.pixelPtr += srcX * 4;
+
+ outY = destY + outHeight - 1;
+ for (y=fileHeight-1; y>=0; y--) {
+ if (y >= srcY && y < srcY + outHeight) {
+ errorFlag = tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
+ outWidth, 1, TK_PHOTO_COMPOSITE_SET);
+ if (errorFlag == TCL_ERROR) {
+ break;
+ }
+ outY--;
+ }
+ block.pixelPtr += 4 * fileWidth;
+ }
+ block.pixelPtr = expline;
+
+error:
+ if (icoHeader.entries) {
+ ckfree((char *) icoHeader.entries);
+ }
+ if (line) {
+ ckfree((char *) line);
+ }
+ if (expline) {
+ ckfree((char *) block.pixelPtr);
+ }
+ return errorFlag;
+}
+
+static int ChnWrite (interp, filename, format, blockPtr)
+ Tcl_Interp *interp;
+ const char *filename;
+ Tcl_Obj *format;
+ Tk_PhotoImageBlock *blockPtr;
+{
+ Tcl_Channel chan;
+ tkimg_MFile handle;
+ int result;
+
+ chan = tkimg_OpenFileChannel(interp, filename, 0644);
+ if (!chan) {
+ return TCL_ERROR;
+ }
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ result = CommonWrite(interp, &handle, blockPtr);
+ if (Tcl_Close(interp, chan) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ return result;
+}
+
+static int StringWrite(
+ Tcl_Interp *interp,
+ Tcl_Obj *format,
+ Tk_PhotoImageBlock *blockPtr
+) {
+ tkimg_MFile handle;
+ int result;
+ Tcl_DString data;
+
+ Tcl_DStringInit(&data);
+ tkimg_WriteInit(&data, &handle);
+ result = CommonWrite(interp, &handle, blockPtr);
+ tkimg_Putc(IMG_DONE, &handle);
+
+ if (result == TCL_OK) {
+ Tcl_DStringResult(interp, &data);
+ } else {
+ Tcl_DStringFree(&data);
+ }
+ return result;
+}
+
+static int CommonWrite (interp, handle, blockPtr)
+ Tcl_Interp *interp;
+ tkimg_MFile *handle;
+ Tk_PhotoImageBlock *blockPtr;
+{
+ int bytesPerLineXOR, bytesPerLineAND, nbytes, ncolors, i, x, y;
+ int redOffset, greenOffset, blueOffset, alphaOffset;
+ int foundColor;
+ UByte *imagePtr, *pixelPtr;
+ UByte buf[4];
+ ICOHEADER icoHeader;
+ INFOHEADER infoHeader;
+ ICOCOLOR colorMap[256];
+ ICOCOLOR pixel;
+
+ if (blockPtr->width > 255 || blockPtr->height > 255) {
+ Tcl_AppendResult(interp, "ICO images must be less than 256 pixels.",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ redOffset = 0;
+ greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
+ blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
+ alphaOffset = blockPtr->offset[0];
+ if (alphaOffset < blockPtr->offset[2]) {
+ alphaOffset = blockPtr->offset[2];
+ }
+ if (++alphaOffset < blockPtr->pixelSize) {
+ alphaOffset -= blockPtr->offset[0];
+ } else {
+ alphaOffset = 0;
+ }
+ ncolors = 0;
+ if (greenOffset || blueOffset) {
+ for (y = 0; ncolors <= 256 && y < blockPtr->height; y++) {
+ pixelPtr = blockPtr->pixelPtr + y*blockPtr->pitch + blockPtr->offset[0];
+ for (x=0; ncolors <= 256 && x<blockPtr->width; x++) {
+ pixel.red = pixelPtr[redOffset];
+ pixel.green = pixelPtr[greenOffset];
+ pixel.blue = pixelPtr[blueOffset];
+ if (alphaOffset && (pixelPtr[alphaOffset] == 0)) {
+ pixel.matte = 0;
+ } else {
+ pixel.matte = 1;
+ }
+ foundColor = 0;
+ for (i=0; i<ncolors; i++) {
+ if (pixel.red == colorMap[i].red &&
+ pixel.green == colorMap[i].green &&
+ pixel.blue == colorMap[i].blue) {
+ foundColor = 1;
+ break;
+ }
+ }
+ if (!foundColor) {
+ if (ncolors < 256) {
+ colorMap[ncolors] = pixel;
+ }
+ ncolors++;
+ }
+ pixelPtr += blockPtr->pixelSize;
+ }
+ }
+ if (ncolors <= 256) {
+ pixel.red = pixel.green = pixel.blue = pixel.matte = 0;
+ while (ncolors < 256) {
+ colorMap[ncolors++] = pixel;
+ }
+ nbytes = 1;
+ } else {
+ nbytes = 3;
+ ncolors = 0;
+ }
+ } else {
+ nbytes = 1;
+ }
+
+ bytesPerLineXOR = ((blockPtr->width * nbytes + 3) / 4) * 4;
+ bytesPerLineAND = ((blockPtr->width * 1 + 31) / 32 )* 4;
+
+ icoHeader.nIcons = 1;
+ if (!(icoHeader.entries = (ICOENTRY *) ckalloc (sizeof (ICOENTRY)))) {
+ return TCL_ERROR;
+ }
+ icoHeader.entries[0].width = blockPtr->width;
+ icoHeader.entries[0].height = blockPtr->height;
+ icoHeader.entries[0].nColors = (ncolors > 0? ncolors: 0);
+ icoHeader.entries[0].reserved = 0;
+ icoHeader.entries[0].nPlanes = 1;
+ icoHeader.entries[0].bitCount = (ncolors > 0? 8: 24);
+ icoHeader.entries[0].sizeInBytes = sizeof (INFOHEADER) +
+ ncolors * sizeof (ICOCOLOR) +
+ bytesPerLineXOR * blockPtr->height +
+ bytesPerLineAND * blockPtr->height;
+ icoHeader.entries[0].fileOffset = 6 + icoHeader.nIcons * 16;
+
+ if (!writeIcoHeader (handle, &icoHeader)) {
+ return TCL_ERROR;
+ }
+
+ infoHeader.size = sizeof (INFOHEADER);
+ infoHeader.width = blockPtr->width;
+ infoHeader.height = blockPtr->height * 2;
+ infoHeader.nPlanes = 1;
+ infoHeader.nBitsPerPixel = (ncolors > 0? 8: 24);
+ infoHeader.compression = 0;
+ infoHeader.imageSize = 0;
+ infoHeader.xPixelsPerM = 0;
+ infoHeader.yPixelsPerM = 0;
+ infoHeader.nColorsUsed = 0;
+ infoHeader.nColorsImportant = 0;
+
+ if (!writeInfoHeader (handle, &infoHeader)) {
+ return TCL_ERROR;
+ }
+
+ if (ncolors > 0) {
+ if (!writeColorMap (handle, ncolors, colorMap)) {
+ return TCL_ERROR;
+ }
+ }
+
+ bytesPerLineXOR -= blockPtr->width * nbytes;
+
+ imagePtr = blockPtr->pixelPtr + blockPtr->offset[0] +
+ blockPtr->height * blockPtr->pitch;
+ for (y = 0; y < blockPtr->height; y++) {
+ pixelPtr = imagePtr -= blockPtr->pitch;
+ for (x=0; x<blockPtr->width; x++) {
+ if (ncolors) {
+ for (i=0; i<ncolors; i++) {
+ if (pixelPtr[redOffset] == colorMap[i].red &&
+ pixelPtr[greenOffset] == colorMap[i].green &&
+ pixelPtr[blueOffset] == colorMap[i].blue) {
+ buf[0] = i;
+ }
+ }
+ } else {
+ buf[0] = pixelPtr[blueOffset];
+ buf[1] = pixelPtr[greenOffset];
+ buf[2] = pixelPtr[redOffset];
+ }
+ tkimg_Write(handle, (char *) buf, nbytes);
+ pixelPtr += blockPtr->pixelSize;
+ }
+ if (bytesPerLineXOR) {
+ tkimg_Write(handle, "\0\0\0", bytesPerLineXOR);
+ }
+ }
+
+ bytesPerLineAND -= blockPtr->width / 8;
+
+ imagePtr = blockPtr->pixelPtr + blockPtr->offset[0] +
+ blockPtr->height * blockPtr->pitch;
+ for (y = 0; y < blockPtr->height; y++) {
+ int c;
+ pixelPtr = imagePtr -= blockPtr->pitch;
+ for (x=0; x<blockPtr->width; x++) {
+ if (x % 8 == 0) {
+ buf[0] = 0;
+ }
+ if (alphaOffset) {
+ c = pixelPtr[alphaOffset];
+ if (c == 0) {
+ buf[0] |= 1<<(7-x%8);
+ }
+ }
+ if (x % 8 == 7) {
+ tkimg_Write(handle, (char *) buf, 1);
+ }
+ pixelPtr += blockPtr->pixelSize;
+ }
+ if (bytesPerLineAND) {
+ tkimg_Write(handle, "\0\0\0", bytesPerLineAND);
+ }
+ }
+ return TCL_OK;
+}