1/* STARTHEADER 2 * 3 * File : ico.c 4 * 5 * Author : Paul Obermeier (paul@poSoft.de) 6 * 7 * Date : Mon Aug 12 20:30:46 CEST 2002 8 * 9 * Copyright : (C) 2002 Paul Obermeier 10 * 11 * Description : 12 * 13 * A photo image handler for Windows Icon file format. 14 * 15 * The following icon types are supported: 16 * 17 * 1-bit pixels: Black and White. 18 * 4-bit pixels: Grayscale or indexed. 19 * 8-bit pixels: Grayscale or indexed. 20 * 24-bit pixels: True-color (GBR, each channel 8 bit). 21 * 32-bit pixels: True-color (GBRA, each channel 8 bit). 22 * 23 * List of currently supported features: 24 * 25 * Type | Read | Write | 26 * | -file | -data | -file | -data | 27 * ---------------------------------------- 28 * 1-bit | Yes | Yes | No | No | 29 * 4-bit | Yes | Yes | No | No | 30 * 8-bit | Yes | Yes | Yes | Yes | 31 * 24-bit | Yes | Yes | Yes | Yes | 32 * 32-bit | Yes | Yes | No | No | 33 * 34 * 35 * The following format options are available: 36 * 37 * Read ICO image: "ico -verbose <bool> -index <uint>" 38 * Write ICO image: "ico -verbose <bool>" 39 * 40 * -verbose <bool>: If set to true, additional information about the file 41 * format is printed to stdout. Default is "false". 42 * -index <uint>: Read the icon with specified index. Default is 0. 43 * 44 * Notes: 45 * 46 * 47 * ENDHEADER 48 * 49 * $Id: ico.c 258 2010-05-04 08:59:42Z nijtmans $ 50 * 51 */ 52 53/* 54 * Generic initialization code, parameterized via CPACKAGE and PACKAGE. 55 */ 56 57#include "init.c" 58 59 60/* #define DEBUG_LOCAL */ 61 62/* Some defines and typedefs. */ 63#define TRUE 1 64#define FALSE 0 65typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ 66typedef unsigned char UByte; /* Unsigned 8 bit integer */ 67typedef char Byte; /* Signed 8 bit integer */ 68typedef unsigned short UShort; /* Unsigned 16 bit integer */ 69typedef short Short; /* Signed 16 bit integer */ 70typedef unsigned int UInt; /* Unsigned 32 bit integer */ 71typedef int Int; /* Signed 32 bit integer */ 72 73#define BI_RGB 0 74 75typedef struct { 76 UByte width; 77 UByte height; 78 UShort nColors; 79 UByte reserved; 80 UShort nPlanes; 81 UShort bitCount; 82 UInt sizeInBytes; 83 UInt fileOffset; 84} ICOENTRY; 85 86/* ICO file header structure */ 87typedef struct { 88 UShort nIcons; 89 ICOENTRY *entries; 90} ICOHEADER; 91 92typedef struct { 93 UInt size; 94 UInt width; 95 UInt height; 96 UShort nPlanes; 97 UShort nBitsPerPixel; 98 UInt compression; 99 UInt imageSize; 100 UInt xPixelsPerM; 101 UInt yPixelsPerM; 102 UInt nColorsUsed; 103 UInt nColorsImportant; 104} INFOHEADER; 105 106typedef struct { 107 UByte red; 108 UByte green; 109 UByte blue; 110 UByte matte; 111} ICOCOLOR; 112 113/* ICO file format options structure for use with ParseFormatOpts */ 114typedef struct { 115 UInt index; 116 Boln verbose; 117} FMTOPT; 118 119static Boln readUByte (tkimg_MFile *handle, UByte *b) 120{ 121 char buf[1]; 122 if (1 != tkimg_Read(handle, buf, 1)) { 123 return FALSE; 124 } 125 *b = buf[0] & 0xFF; 126 return TRUE; 127} 128 129/* Read 2 bytes, representing a unsigned 16 bit integer in the form 130 <LowByte, HighByte>, from a file and convert them into the current 131 machine's format. */ 132 133static Boln readUShort (tkimg_MFile *handle, UShort *s) 134{ 135 char buf[2]; 136 UShort tmp; 137 138 if (2 != tkimg_Read(handle, buf, 2)) { 139 return FALSE; 140 } 141 tmp = buf[0] & 0xFF; 142 tmp |= (buf[1] & 0xFF) << 8; 143 *s = tmp; 144 return TRUE; 145} 146 147/* Read 4 bytes, representing a unsigned 32 bit integer in the form 148 <LowByte, HighByte>, from a file and convert them into the current 149 machine's format. */ 150 151static Boln readUInt (tkimg_MFile *handle, UInt *i) 152{ 153 char buf[4]; 154 UInt tmp; 155 156 if (4 != tkimg_Read(handle, buf, 4)) { 157 return FALSE; 158 } 159 tmp = buf[0] & 0xFF; 160 tmp |= (buf[1] & 0xFF) << 8; 161 tmp |= (buf[2] & 0xFF) << 16; 162 tmp |= (buf[3] & 0xFF) << 24; 163 *i = tmp; 164 return TRUE; 165} 166 167/* Write a byte, representing an unsigned integer to a file. */ 168 169static Boln writeUByte (tkimg_MFile *handle, UByte b) 170{ 171 UByte buf[1]; 172 buf[0] = b; 173 if (1 != tkimg_Write(handle, (const char *)buf, 1)) { 174 return FALSE; 175 } 176 return TRUE; 177} 178 179/* Convert a unsigned 16 bit integer number into the format 180 <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */ 181 182static Boln writeUShort (tkimg_MFile *handle, UShort s) 183{ 184 Byte buf[2]; 185 buf[0] = (Byte)s; 186 buf[1] = s >> 8; 187 if (2 != tkimg_Write(handle, buf, 2)) { 188 return FALSE; 189 } 190 return TRUE; 191} 192 193/* Convert a unsigned 32 bit integer number into the format 194 <LowByte, HighByte> (an array of 4 bytes) and write the array to a file. */ 195 196static Boln writeUInt (tkimg_MFile *handle, UInt i) 197{ 198 Byte buf[4]; 199 buf[0] = i; 200 buf[1] = i >> 8; 201 buf[2] = i >> 16; 202 buf[3] = i >> 24; 203 if (4 != tkimg_Write(handle, buf, 4)) { 204 return FALSE; 205 } 206 return TRUE; 207} 208 209#define OUT Tcl_WriteChars (outChan, str, -1) 210static void printImgInfo (ICOHEADER *th, INFOHEADER *ih, FMTOPT *opts, 211 const char *filename, const char *msg) 212{ 213 Tcl_Channel outChan; 214 char str[256]; 215 int i = opts->index; 216 217 outChan = Tcl_GetStdChannel (TCL_STDOUT); 218 if (!outChan) { 219 return; 220 } 221 sprintf(str, "%s %s\n", msg, filename); OUT; 222 sprintf(str, " No. of icons : %d\n", th->nIcons); OUT; 223 sprintf(str, " Icon %d:\n", i); OUT; 224 sprintf(str, " Width and Height: %dx%d\n", ih->width, ih->height/2); OUT; 225 sprintf(str, " Number of colors: %d\n", th->entries[i].nColors); OUT; 226 sprintf(str, " Number of planes: %d\n", ih->nPlanes); OUT; 227 sprintf(str, " Bits per pixel: %d\n", ih->nBitsPerPixel); OUT; 228 sprintf(str, " Size in bytes: %d\n", th->entries[i].sizeInBytes); OUT; 229 sprintf(str, " File offset: %d\n", th->entries[i].fileOffset); OUT; 230 Tcl_Flush(outChan); 231} 232#undef OUT 233 234static Boln readIcoHeader (tkimg_MFile *handle, ICOHEADER *th) 235{ 236 int i; 237 UByte nColors; 238 UShort reserved, type, nIcons; 239 240 if (!readUShort (handle, &reserved)) { 241 return FALSE; 242 } 243 if (reserved != 0) { 244 return FALSE; 245 } 246 247 if (!readUShort (handle, &type)) { 248 return FALSE; 249 } 250 if (type != 1) { 251 return FALSE; 252 } 253 if (!readUShort (handle, &nIcons)) { 254 return FALSE; 255 } 256 if (nIcons <= 0) { 257 return FALSE; 258 } 259 260 th->nIcons = nIcons; 261 if (!(th->entries = (ICOENTRY *)ckalloc (sizeof (ICOENTRY) * nIcons))) { 262 return FALSE; 263 } 264 265 for (i=0; i<nIcons; i++) { 266 if (!readUByte (handle, &th->entries[i].width) || 267 !readUByte (handle, &th->entries[i].height) || 268 !readUByte (handle, &nColors) || 269 !readUByte (handle, &th->entries[i].reserved) || 270 !readUShort (handle, &th->entries[i].nPlanes) || 271 !readUShort (handle, &th->entries[i].bitCount) || 272 !readUInt (handle, &th->entries[i].sizeInBytes) || 273 !readUInt (handle, &th->entries[i].fileOffset)) { 274 ckfree ((char *)th->entries); 275 return FALSE; 276 } 277 th->entries[i].nColors = (nColors == 0? 256: nColors); 278#ifdef DEBUG_LOCAL 279 printf ("Icon %d:\n", i); 280 printf (" Width : %d\n", th->entries[i].width); 281 printf (" Height : %d\n", th->entries[i].height); 282 printf (" Colors : %d\n", th->entries[i].nColors); 283 printf (" Planes : %d\n", th->entries[i].nPlanes); 284 printf (" BitCount : %d\n", th->entries[i].bitCount); 285 printf (" Size : %d\n", th->entries[i].sizeInBytes); 286 printf (" FileOffset: %d\n", th->entries[i].fileOffset); 287#endif 288 } 289 return TRUE; 290} 291 292static Boln writeIcoHeader (tkimg_MFile *handle, ICOHEADER *th) 293{ 294 int i; 295 UByte nColors; 296 UShort reserved = 0, 297 type = 1; 298 299 if (!writeUShort (handle, reserved)) { 300 return FALSE; 301 } 302 if (!writeUShort (handle, type)) { 303 return FALSE; 304 } 305 if (!writeUShort (handle, th->nIcons)) { 306 return FALSE; 307 } 308 for (i=0; i<th->nIcons; i++) { 309 nColors = (th->entries[i].nColors == 256? 0: th->entries[i].nColors); 310 if (!writeUByte (handle, th->entries[i].width) || 311 !writeUByte (handle, th->entries[i].height) || 312 !writeUByte (handle, nColors) || 313 !writeUByte (handle, th->entries[i].reserved) || 314 !writeUShort (handle, th->entries[i].nPlanes) || 315 !writeUShort (handle, th->entries[i].bitCount) || 316 !writeUInt (handle, th->entries[i].sizeInBytes) || 317 !writeUInt (handle, th->entries[i].fileOffset)) { 318 return FALSE; 319 } 320 } 321 return TRUE; 322} 323 324static Boln readInfoHeader (tkimg_MFile *handle, INFOHEADER *ih) 325{ 326 if (!readUInt (handle, &ih->size) || 327 !readUInt (handle, &ih->width) || 328 !readUInt (handle, &ih->height) || 329 !readUShort (handle, &ih->nPlanes) || 330 !readUShort (handle, &ih->nBitsPerPixel) || 331 !readUInt (handle, &ih->compression) || 332 !readUInt (handle, &ih->imageSize) || 333 !readUInt (handle, &ih->xPixelsPerM) || 334 !readUInt (handle, &ih->yPixelsPerM) || 335 !readUInt (handle, &ih->nColorsUsed) || 336 !readUInt (handle, &ih->nColorsImportant)) { 337 return FALSE; 338 } 339#ifdef DEBUG_LOCAL 340 printf("Info header:\n"); 341 printf("Size: %d\n", ih->size); 342 printf("Width: %d\n", ih->width); 343 printf("Height: %d\n", ih->height); 344 printf("Planes: %d\n", ih->nPlanes); 345 printf("BitsPerPixel: %d\n", ih->nBitsPerPixel); 346 printf("Compression: %d\n", ih->compression); 347 printf("Image size: %d\n", ih->imageSize); 348 printf("XPixelsPerM: %d\n", ih->xPixelsPerM); 349 printf("YPixelsPerM: %d\n", ih->yPixelsPerM); 350 printf("ColorsUsed: %d\n", ih->nColorsUsed); 351 printf("ColorsImportant: %d\n", ih->nColorsImportant); 352#endif 353 return TRUE; 354} 355 356static Boln writeInfoHeader (tkimg_MFile *handle, INFOHEADER *ih) 357{ 358 if (!writeUInt (handle, ih->size) || 359 !writeUInt (handle, ih->width) || 360 !writeUInt (handle, ih->height) || 361 !writeUShort (handle, ih->nPlanes) || 362 !writeUShort (handle, ih->nBitsPerPixel) || 363 !writeUInt (handle, ih->compression) || 364 !writeUInt (handle, ih->imageSize) || 365 !writeUInt (handle, ih->xPixelsPerM) || 366 !writeUInt (handle, ih->yPixelsPerM) || 367 !writeUInt (handle, ih->nColorsUsed) || 368 !writeUInt (handle, ih->nColorsImportant)) { 369 return FALSE; 370 } 371#ifdef DEBUG_LOCAL 372 printf("Writing Info header:\n"); 373 printf("Size : %d\n", ih->size); 374 printf("Width : %d\n", ih->width); 375 printf("Height : %d\n", ih->height); 376 printf("Planes : %d\n", ih->nPlanes); 377 printf("BitsPerPixel: %d\n", ih->nBitsPerPixel); 378 printf("Compression : %d\n", ih->compression); 379 printf("Image size : %d\n", ih->imageSize); 380 printf("XPixelsPerM : %d\n", ih->xPixelsPerM); 381 printf("YPixelsPerM : %d\n", ih->yPixelsPerM); 382 printf("ColorsUsed : %d\n", ih->nColorsUsed); 383 printf("ColorsImport: %d\n", ih->nColorsImportant); 384#endif 385 return TRUE; 386} 387 388static Boln readColorMap (tkimg_MFile *handle, int mapSize, ICOCOLOR *colorMap) 389{ 390 int i; 391 ICOCOLOR color; 392 393 for (i=0; i<mapSize; i++) { 394 if (!readUByte (handle, &color.blue) || 395 !readUByte (handle, &color.green) || 396 !readUByte (handle, &color.red) || 397 !readUByte (handle, &color.matte)) { 398 return FALSE; 399 } 400 colorMap[i] = color; 401 } 402 return TRUE; 403} 404 405static Boln writeColorMap (tkimg_MFile *handle, int mapSize, ICOCOLOR *colorMap) 406{ 407 int i; 408 409 for (i=0; i<mapSize; i++) { 410 if (!writeUByte (handle, colorMap[i].blue) || 411 !writeUByte (handle, colorMap[i].green) || 412 !writeUByte (handle, colorMap[i].red) || 413 !writeUByte (handle, colorMap[i].matte)) { 414 return FALSE; 415 } 416 } 417 return TRUE; 418} 419 420/* 421 * Prototypes for local procedures defined in this file. 422 */ 423 424static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, 425 FMTOPT *opts); 426static int CommonMatch(tkimg_MFile *handle, int *widthPtr, 427 int *heightPtr, ICOHEADER *icoHeaderPtr); 428static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, 429 const char *filename, Tcl_Obj *format, 430 Tk_PhotoHandle imageHandle, int destX, int destY, 431 int width, int height, int srcX, int srcY); 432static int CommonWrite(Tcl_Interp *interp, tkimg_MFile *handle, 433 Tk_PhotoImageBlock *blockPtr); 434 435static int ParseFormatOpts (interp, format, opts) 436 Tcl_Interp *interp; 437 Tcl_Obj *format; 438 FMTOPT *opts; 439{ 440 static const char *const icoOptions[] = { 441 "-verbose", "-index" 442 }; 443 int objc, length, c, i, index; 444 Tcl_Obj **objv; 445 const char *indexStr, *verboseStr; 446 447 /* Initialize format options with default values. */ 448 verboseStr = "0"; 449 indexStr = "0"; 450 451 if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) 452 return TCL_ERROR; 453 if (objc) { 454 for (i=1; i<objc; i++) { 455 if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)icoOptions, 456 "format option", 0, &index) != TCL_OK) { 457 return TCL_ERROR; 458 } 459 if (++i >= objc) { 460 Tcl_AppendResult(interp, "No value for option \"", 461 Tcl_GetStringFromObj (objv[--i], (int *) NULL), 462 "\"", (char *) NULL); 463 return TCL_ERROR; 464 } 465 switch(index) { 466 case 0: 467 verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 468 break; 469 case 1: 470 indexStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 471 break; 472 } 473 } 474 } 475 476 /* OPA TODO: Check for valid integer strings. */ 477 opts->index = atoi (indexStr); 478 479 c = verboseStr[0]; length = strlen (verboseStr); 480 if (!strncmp (verboseStr, "1", length) || \ 481 !strncmp (verboseStr, "true", length) || \ 482 !strncmp (verboseStr, "on", length)) { 483 opts->verbose = 1; 484 } else if (!strncmp (verboseStr, "0", length) || \ 485 !strncmp (verboseStr, "false", length) || \ 486 !strncmp (verboseStr, "off", length)) { 487 opts->verbose = 0; 488 } else { 489 Tcl_AppendResult(interp, "invalid verbose mode \"", verboseStr, 490 "\": should be 1 or 0, on or off, true or false", 491 (char *) NULL); 492 return TCL_ERROR; 493 } 494 495 return TCL_OK; 496} 497 498static int ChnMatch( 499 Tcl_Channel chan, 500 const char *fileName, 501 Tcl_Obj *format, 502 int *widthPtr, 503 int *heightPtr, 504 Tcl_Interp *interp 505) { 506 tkimg_MFile handle; 507 508 handle.data = (char *) chan; 509 handle.state = IMG_CHAN; 510 511 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 512} 513 514static int ObjMatch( 515 Tcl_Obj *data, 516 Tcl_Obj *format, 517 int *widthPtr, 518 int *heightPtr, 519 Tcl_Interp *interp 520) { 521 tkimg_MFile handle; 522 523 if (!tkimg_ReadInit(data, '\000', &handle)) { 524 return 0; 525 } 526 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 527} 528 529static int CommonMatch(handle, widthPtr, heightPtr, icoHeaderPtr) 530 tkimg_MFile *handle; 531 int *widthPtr, *heightPtr; 532 ICOHEADER *icoHeaderPtr; 533{ 534 ICOHEADER icoHeader, *headerPtr; 535 536 if (!icoHeaderPtr) { 537 headerPtr = &icoHeader; 538 } else { 539 headerPtr = icoHeaderPtr; 540 } 541 if (!readIcoHeader (handle, headerPtr)) { 542 return 0; 543 } 544 545 *widthPtr = headerPtr->entries[0].width; 546 *heightPtr = headerPtr->entries[0].height; 547 548 if (!icoHeaderPtr) { 549 ckfree ((char *) headerPtr->entries); 550 } 551 return 1; 552} 553 554static int ChnRead(interp, chan, filename, format, imageHandle, 555 destX, destY, width, height, srcX, srcY) 556 Tcl_Interp *interp; 557 Tcl_Channel chan; 558 const char *filename; 559 Tcl_Obj *format; 560 Tk_PhotoHandle imageHandle; 561 int destX, destY; 562 int width, height; 563 int srcX, srcY; 564{ 565 tkimg_MFile handle; 566 567 handle.data = (char *) chan; 568 handle.state = IMG_CHAN; 569 570 return CommonRead (interp, &handle, filename, format, 571 imageHandle, destX, destY, 572 width, height, srcX, srcY); 573} 574 575static int ObjRead (interp, data, format, imageHandle, 576 destX, destY, width, height, srcX, srcY) 577 Tcl_Interp *interp; 578 Tcl_Obj *data; 579 Tcl_Obj *format; 580 Tk_PhotoHandle imageHandle; 581 int destX, destY; 582 int width, height; 583 int srcX, srcY; 584{ 585 tkimg_MFile handle; 586 587 tkimg_ReadInit(data, '\000', &handle); 588 589 return CommonRead (interp, &handle, "InlineData", format, imageHandle, 590 destX, destY, width, height, srcX, srcY); 591} 592 593static int CommonRead (interp, handle, filename, format, imageHandle, 594 destX, destY, width, height, srcX, srcY) 595 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 596 tkimg_MFile *handle; /* The image file, open for reading. */ 597 const char *filename; /* The name of the image file. */ 598 Tcl_Obj *format; /* User-specified format object, or NULL. */ 599 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 600 int destX, destY; /* Coordinates of top-left pixel in 601 * photo image to be written to. */ 602 int width, height; /* Dimensions of block of photo image to 603 * be written to. */ 604 int srcX, srcY; /* Coordinates of top-left pixel to be used 605 * in image being read. */ 606{ 607 Tk_PhotoImageBlock block; 608 int x, y; 609 int fileWidth, fileHeight; 610 int icoHeaderWidth, icoHeaderHeight; 611 int outWidth, outHeight, outY; 612 int bytesPerLine; 613 int nBytesToSkip; 614 int errorFlag = TCL_OK; 615 unsigned char *line = NULL, *expline = NULL; 616 char msgStr[1024]; 617 ICOHEADER icoHeader; 618 INFOHEADER infoHeader; 619 ICOCOLOR colorMap[256]; 620 FMTOPT opts; 621 622 if (ParseFormatOpts(interp, format, &opts) != TCL_OK) { 623 return TCL_ERROR; 624 } 625 626 if (!CommonMatch(handle, &fileWidth, &fileHeight, &icoHeader)) { 627 Tcl_AppendResult(interp, "Error reading header", (char *)NULL); 628 errorFlag = TCL_ERROR; 629 goto error; 630 } 631 632 if (opts.index < 0 || opts.index >= icoHeader.nIcons) { 633 sprintf(msgStr, "Invalid icon index: %d", opts.index); 634 Tcl_AppendResult(interp, msgStr, (char *)NULL); 635 errorFlag = TCL_ERROR; 636 goto error; 637 } 638 639 /* Instead of seeking, which does not work on strings, 640 we calculate the number of bytes from the current position 641 till the start of the INFOHEADER and read these bytes with tkimg_Read. */ 642 nBytesToSkip = icoHeader.entries[opts.index].fileOffset -6 - 643 16 * icoHeader.nIcons; 644 if (nBytesToSkip > 0) { 645 char *dummy = ckalloc (nBytesToSkip); 646 tkimg_Read(handle, dummy, nBytesToSkip); 647 ckfree ((char *) dummy); 648 } 649 650 /* Read Info header and color map */ 651 if (!readInfoHeader (handle, &infoHeader)) { 652 Tcl_AppendResult(interp, "Error reading info header", (char *)NULL); 653 errorFlag = TCL_ERROR; 654 goto error; 655 } 656 657 if (infoHeader.compression != BI_RGB) { 658 sprintf(msgStr,"Unsupported compression type (%d)", infoHeader.compression); 659 Tcl_AppendResult(interp, msgStr, (char *)NULL); 660 errorFlag = TCL_ERROR; 661 goto error; 662 } 663 664 if (infoHeader.nBitsPerPixel != 24 && infoHeader.nBitsPerPixel != 32) { 665 if (!readColorMap (handle, icoHeader.entries[opts.index].nColors, 666 colorMap)) { 667 Tcl_AppendResult(interp, "Error reading color map", (char *)NULL); 668 errorFlag = TCL_ERROR; 669 goto error; 670 } 671 } 672 673 fileWidth = infoHeader.width; 674 fileHeight = infoHeader.height / 2; 675 icoHeaderWidth = icoHeader.entries[opts.index].width; 676 icoHeaderHeight = icoHeader.entries[opts.index].height; 677 if (icoHeaderWidth == 0) { 678 icoHeaderWidth = 256; 679 } 680 if (icoHeaderHeight == 0) { 681 icoHeaderHeight = 256; 682 } 683 if (fileWidth != icoHeaderWidth || fileHeight != icoHeaderHeight) { 684 sprintf(msgStr,"ICO sizes don't match (%dx%d) vs. (%dx%d)", 685 fileWidth, fileHeight, 686 icoHeaderWidth, icoHeaderHeight); 687 Tcl_AppendResult(interp, msgStr, (char *)NULL); 688 errorFlag = TCL_ERROR; 689 goto error; 690 } 691 outWidth = fileWidth; 692 outHeight = fileHeight; 693 if (fileWidth != width || fileHeight != height) { 694 if (srcX != 0 || srcY != 0 || destX != 0 || destY != 0) { 695 if ((srcX + width) > fileWidth) { 696 outWidth = fileWidth - srcX; 697 } else { 698 outWidth = width; 699 } 700 if ((srcY + height) > fileHeight) { 701 outHeight = fileHeight - srcY; 702 } else { 703 outHeight = height; 704 } 705 } 706 } 707 if ((outWidth <= 0) || (outHeight <= 0) 708 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 709 return TCL_OK; 710 } 711 712 if (opts.verbose) { 713 printImgInfo (&icoHeader, &infoHeader, &opts, 714 filename, "Reading image:"); 715 } 716 717 if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { 718 errorFlag = TCL_ERROR; 719 goto error; 720 } 721 722 bytesPerLine = ((infoHeader.nBitsPerPixel * fileWidth + 31)/32)*4; 723 724 block.pixelSize = 4; 725 block.pitch = fileWidth * 4; 726 block.width = outWidth; 727 block.height = 1; 728 block.offset[0] = 0; 729 block.offset[1] = 1; 730 block.offset[2] = 2; 731 block.offset[3] = 3; 732 block.pixelPtr = (unsigned char *) ckalloc (4 * fileWidth * fileHeight); 733 expline = block.pixelPtr; 734 735 line = (unsigned char *) ckalloc(bytesPerLine); 736 switch (infoHeader.nBitsPerPixel) { 737 case 32: 738 for (y=0; y<fileHeight; y++) { 739 tkimg_Read(handle, (char *)line, bytesPerLine); 740 for (x = 0; x < fileWidth; x++) { 741 expline[0] = line[x*4 + 2]; 742 expline[1] = line[x*4 + 1]; 743 expline[2] = line[x*4 + 0]; 744 expline[3] = line[x*4 + 3]; 745 expline += 4; 746 } 747 } 748 break; 749 case 24: 750 for (y=0; y<fileHeight; y++) { 751 tkimg_Read(handle, (char *)line, bytesPerLine); 752 for (x = 0; x < fileWidth; x++) { 753 expline[0] = line[x*3 + 2]; 754 expline[1] = line[x*3 + 1]; 755 expline[2] = line[x*3 + 0]; 756 expline += 4; 757 } 758 } 759 break; 760 case 8: 761 for (y=0; y<fileHeight; y++) { 762 tkimg_Read(handle, (char *)line, bytesPerLine); 763 for (x = 0; x < fileWidth; x++) { 764 expline[0] = colorMap[line[x]].red; 765 expline[1] = colorMap[line[x]].green; 766 expline[2] = colorMap[line[x]].blue; 767 expline += 4; 768 } 769 } 770 break; 771 case 4: 772 for (y=0; y<fileHeight; y++) { 773 int c; 774 tkimg_Read(handle, (char *)line, bytesPerLine); 775 for (x=0; x<fileWidth; x++) { 776 if (x&1) { 777 c = line[x/2] & 0x0f; 778 } else { 779 c = line[x/2] >> 4; 780 } 781 expline[0] = colorMap[c].red; 782 expline[1] = colorMap[c].green; 783 expline[2] = colorMap[c].blue; 784 expline += 4; 785 } 786 } 787 break; 788 case 1: 789 for (y=0; y<fileHeight; y++) { 790 int c; 791 tkimg_Read(handle, (char *)line, bytesPerLine); 792 for (x=0; x<fileWidth; x++) { 793 c = (line[x/8] >> (7-(x%8))) & 1; 794 expline[0] = colorMap[c].red; 795 expline[1] = colorMap[c].green; 796 expline[2] = colorMap[c].blue; 797 expline += 4; 798 } 799 } 800 break; 801 default: 802 sprintf(msgStr,"%d-bits ICO file not supported", 803 infoHeader.nBitsPerPixel); 804 Tcl_AppendResult(interp, msgStr, (char *)NULL); 805 errorFlag = TCL_ERROR; 806 goto error; 807 } 808 809 if (infoHeader.nBitsPerPixel != 32) { 810 /* Read XAND bitmap. We don't need to read the alpha bitmap, if 811 alpha is supplied already in the 32-bit case. */ 812 bytesPerLine = ((1 * fileWidth + 31)/32)*4; 813 814 expline = block.pixelPtr; 815 for (y=0; y<fileHeight; y++) { 816 int c; 817 tkimg_Read(handle, (char *)line, bytesPerLine); 818 for (x=0; x<fileWidth; x++) { 819 c = (line[x/8] >> (7-(x%8))) & 1; 820 expline[3] = (c? 0: 255); 821 expline += 4; 822 } 823 } 824 } 825 826 /* Store the pointer to allocated buffer for later freeing. */ 827 expline = block.pixelPtr; 828 block.pixelPtr += srcX * 4; 829 830 outY = destY + outHeight - 1; 831 for (y=fileHeight-1; y>=0; y--) { 832 if (y >= srcY && y < srcY + outHeight) { 833 errorFlag = tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, 834 outWidth, 1, TK_PHOTO_COMPOSITE_SET); 835 if (errorFlag == TCL_ERROR) { 836 break; 837 } 838 outY--; 839 } 840 block.pixelPtr += 4 * fileWidth; 841 } 842 block.pixelPtr = expline; 843 844error: 845 if (icoHeader.entries) { 846 ckfree((char *) icoHeader.entries); 847 } 848 if (line) { 849 ckfree((char *) line); 850 } 851 if (expline) { 852 ckfree((char *) block.pixelPtr); 853 } 854 return errorFlag; 855} 856 857static int ChnWrite (interp, filename, format, blockPtr) 858 Tcl_Interp *interp; 859 const char *filename; 860 Tcl_Obj *format; 861 Tk_PhotoImageBlock *blockPtr; 862{ 863 Tcl_Channel chan; 864 tkimg_MFile handle; 865 int result; 866 867 chan = tkimg_OpenFileChannel(interp, filename, 0644); 868 if (!chan) { 869 return TCL_ERROR; 870 } 871 872 handle.data = (char *) chan; 873 handle.state = IMG_CHAN; 874 875 result = CommonWrite(interp, &handle, blockPtr); 876 if (Tcl_Close(interp, chan) == TCL_ERROR) { 877 return TCL_ERROR; 878 } 879 return result; 880} 881 882static int StringWrite( 883 Tcl_Interp *interp, 884 Tcl_Obj *format, 885 Tk_PhotoImageBlock *blockPtr 886) { 887 tkimg_MFile handle; 888 int result; 889 Tcl_DString data; 890 891 Tcl_DStringInit(&data); 892 tkimg_WriteInit(&data, &handle); 893 result = CommonWrite(interp, &handle, blockPtr); 894 tkimg_Putc(IMG_DONE, &handle); 895 896 if (result == TCL_OK) { 897 Tcl_DStringResult(interp, &data); 898 } else { 899 Tcl_DStringFree(&data); 900 } 901 return result; 902} 903 904static int CommonWrite (interp, handle, blockPtr) 905 Tcl_Interp *interp; 906 tkimg_MFile *handle; 907 Tk_PhotoImageBlock *blockPtr; 908{ 909 int bytesPerLineXOR, bytesPerLineAND, nbytes, ncolors, i, x, y; 910 int redOffset, greenOffset, blueOffset, alphaOffset; 911 int foundColor; 912 UByte *imagePtr, *pixelPtr; 913 UByte buf[4]; 914 ICOHEADER icoHeader; 915 INFOHEADER infoHeader; 916 ICOCOLOR colorMap[256]; 917 ICOCOLOR pixel; 918 919 if (blockPtr->width > 255 || blockPtr->height > 255) { 920 Tcl_AppendResult(interp, "ICO images must be less than 256 pixels.", 921 (char *) NULL); 922 return TCL_ERROR; 923 } 924 925 redOffset = 0; 926 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 927 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 928 alphaOffset = blockPtr->offset[0]; 929 if (alphaOffset < blockPtr->offset[2]) { 930 alphaOffset = blockPtr->offset[2]; 931 } 932 if (++alphaOffset < blockPtr->pixelSize) { 933 alphaOffset -= blockPtr->offset[0]; 934 } else { 935 alphaOffset = 0; 936 } 937 ncolors = 0; 938 if (greenOffset || blueOffset) { 939 for (y = 0; ncolors <= 256 && y < blockPtr->height; y++) { 940 pixelPtr = blockPtr->pixelPtr + y*blockPtr->pitch + blockPtr->offset[0]; 941 for (x=0; ncolors <= 256 && x<blockPtr->width; x++) { 942 pixel.red = pixelPtr[redOffset]; 943 pixel.green = pixelPtr[greenOffset]; 944 pixel.blue = pixelPtr[blueOffset]; 945 if (alphaOffset && (pixelPtr[alphaOffset] == 0)) { 946 pixel.matte = 0; 947 } else { 948 pixel.matte = 1; 949 } 950 foundColor = 0; 951 for (i=0; i<ncolors; i++) { 952 if (pixel.red == colorMap[i].red && 953 pixel.green == colorMap[i].green && 954 pixel.blue == colorMap[i].blue) { 955 foundColor = 1; 956 break; 957 } 958 } 959 if (!foundColor) { 960 if (ncolors < 256) { 961 colorMap[ncolors] = pixel; 962 } 963 ncolors++; 964 } 965 pixelPtr += blockPtr->pixelSize; 966 } 967 } 968 if (ncolors <= 256) { 969 pixel.red = pixel.green = pixel.blue = pixel.matte = 0; 970 while (ncolors < 256) { 971 colorMap[ncolors++] = pixel; 972 } 973 nbytes = 1; 974 } else { 975 nbytes = 3; 976 ncolors = 0; 977 } 978 } else { 979 nbytes = 1; 980 } 981 982 bytesPerLineXOR = ((blockPtr->width * nbytes + 3) / 4) * 4; 983 bytesPerLineAND = ((blockPtr->width * 1 + 31) / 32 )* 4; 984 985 icoHeader.nIcons = 1; 986 if (!(icoHeader.entries = (ICOENTRY *) ckalloc (sizeof (ICOENTRY)))) { 987 return TCL_ERROR; 988 } 989 icoHeader.entries[0].width = blockPtr->width; 990 icoHeader.entries[0].height = blockPtr->height; 991 icoHeader.entries[0].nColors = (ncolors > 0? ncolors: 0); 992 icoHeader.entries[0].reserved = 0; 993 icoHeader.entries[0].nPlanes = 1; 994 icoHeader.entries[0].bitCount = (ncolors > 0? 8: 24); 995 icoHeader.entries[0].sizeInBytes = sizeof (INFOHEADER) + 996 ncolors * sizeof (ICOCOLOR) + 997 bytesPerLineXOR * blockPtr->height + 998 bytesPerLineAND * blockPtr->height; 999 icoHeader.entries[0].fileOffset = 6 + icoHeader.nIcons * 16; 1000 1001 if (!writeIcoHeader (handle, &icoHeader)) { 1002 return TCL_ERROR; 1003 } 1004 1005 infoHeader.size = sizeof (INFOHEADER); 1006 infoHeader.width = blockPtr->width; 1007 infoHeader.height = blockPtr->height * 2; 1008 infoHeader.nPlanes = 1; 1009 infoHeader.nBitsPerPixel = (ncolors > 0? 8: 24); 1010 infoHeader.compression = 0; 1011 infoHeader.imageSize = 0; 1012 infoHeader.xPixelsPerM = 0; 1013 infoHeader.yPixelsPerM = 0; 1014 infoHeader.nColorsUsed = 0; 1015 infoHeader.nColorsImportant = 0; 1016 1017 if (!writeInfoHeader (handle, &infoHeader)) { 1018 return TCL_ERROR; 1019 } 1020 1021 if (ncolors > 0) { 1022 if (!writeColorMap (handle, ncolors, colorMap)) { 1023 return TCL_ERROR; 1024 } 1025 } 1026 1027 bytesPerLineXOR -= blockPtr->width * nbytes; 1028 1029 imagePtr = blockPtr->pixelPtr + blockPtr->offset[0] + 1030 blockPtr->height * blockPtr->pitch; 1031 for (y = 0; y < blockPtr->height; y++) { 1032 pixelPtr = imagePtr -= blockPtr->pitch; 1033 for (x=0; x<blockPtr->width; x++) { 1034 if (ncolors) { 1035 for (i=0; i<ncolors; i++) { 1036 if (pixelPtr[redOffset] == colorMap[i].red && 1037 pixelPtr[greenOffset] == colorMap[i].green && 1038 pixelPtr[blueOffset] == colorMap[i].blue) { 1039 buf[0] = i; 1040 } 1041 } 1042 } else { 1043 buf[0] = pixelPtr[blueOffset]; 1044 buf[1] = pixelPtr[greenOffset]; 1045 buf[2] = pixelPtr[redOffset]; 1046 } 1047 tkimg_Write(handle, (char *) buf, nbytes); 1048 pixelPtr += blockPtr->pixelSize; 1049 } 1050 if (bytesPerLineXOR) { 1051 tkimg_Write(handle, "\0\0\0", bytesPerLineXOR); 1052 } 1053 } 1054 1055 bytesPerLineAND -= blockPtr->width / 8; 1056 1057 imagePtr = blockPtr->pixelPtr + blockPtr->offset[0] + 1058 blockPtr->height * blockPtr->pitch; 1059 for (y = 0; y < blockPtr->height; y++) { 1060 int c; 1061 pixelPtr = imagePtr -= blockPtr->pitch; 1062 for (x=0; x<blockPtr->width; x++) { 1063 if (x % 8 == 0) { 1064 buf[0] = 0; 1065 } 1066 if (alphaOffset) { 1067 c = pixelPtr[alphaOffset]; 1068 if (c == 0) { 1069 buf[0] |= 1<<(7-x%8); 1070 } 1071 } 1072 if (x % 8 == 7) { 1073 tkimg_Write(handle, (char *) buf, 1); 1074 } 1075 pixelPtr += blockPtr->pixelSize; 1076 } 1077 if (bytesPerLineAND) { 1078 tkimg_Write(handle, "\0\0\0", bytesPerLineAND); 1079 } 1080 } 1081 return TCL_OK; 1082} 1083