/* 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 -index " * Write ICO image: "ico -verbose " * * -verbose : If set to true, additional information about the file * format is printed to stdout. Default is "false". * -index : Read the icon with specified index. Default is 0. * * Notes: * * * ENDHEADER * * $Id: ico.c 380 2015-04-11 13:15:29Z obermeier $ * */ /* * 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 , 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 , 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 (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 (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; ientries[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; inIcons; 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= 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; FMTOPT opts; handle.data = (char *) chan; handle.state = IMG_CHAN; if (ParseFormatOpts(interp, format, &opts) != TCL_OK) { return 0; } return CommonMatch(&handle, widthPtr, heightPtr, opts.index, NULL); } static int ObjMatch( Tcl_Obj *data, Tcl_Obj *format, int *widthPtr, int *heightPtr, Tcl_Interp *interp ) { tkimg_MFile handle; FMTOPT opts; if (ParseFormatOpts(interp, format, &opts) != TCL_OK) { return 0; } if (!tkimg_ReadInit(data, '\000', &handle)) { return 0; } return CommonMatch(&handle, widthPtr, heightPtr, opts.index, NULL); } static int CommonMatch(handle, widthPtr, heightPtr, index, icoHeaderPtr) tkimg_MFile *handle; int *widthPtr, *heightPtr; int index; ICOHEADER *icoHeaderPtr; { ICOHEADER icoHeader, *headerPtr; if (!icoHeaderPtr) { headerPtr = &icoHeader; } else { headerPtr = icoHeaderPtr; } if (!readIcoHeader (handle, headerPtr)) { return 0; } *widthPtr = headerPtr->entries[index].width; *heightPtr = headerPtr->entries[index].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, opts.index, &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> 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> (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> (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 && xwidth; 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; ipixelSize; } } 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; xwidth; x++) { if (ncolors) { for (i=0; ipixelSize; } 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; xwidth; 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; }