1/* STARTHEADER 2 * 3 * File : tga.c 4 * 5 * Author : Paul Obermeier (paul@poSoft.de) 6 * 7 * Date : Wed Nov 22 21:45:17 CET 2000 8 * 9 * Copyright : (C) 2000-2002 Paul Obermeier 10 * 11 * Description : 12 * 13 * A photo image handler for Truevision's TARGA file format. 14 * 15 * The following image types are supported: 16 * 17 * 24-bit pixels: True-color (RGB, each channel 8 bit). 18 * 32-bit pixels: True-color with alpha channel (RGBA, each channel 8 bit). 19 * 20 * List of currently supported features: 21 * 22 * Type | Read | Write | 23 * | -file | -data | -file | -data | 24 * ---------------------------------------- 25 * 24-bit | Yes | Yes | Yes | Yes | 26 * 32-bit | Yes | Yes | Yes | Yes | 27 * 28 * All images types may be either uncompressed (Targa-Type 2) or 29 * run-length encoded (Targa-Type 10). 30 * 31 * 32 * The following format options are available: 33 * 34 * Read TGA image: "tga -matte <bool> -verbose <bool>" 35 * Write TGA image: "tga -matte <bool> -verbose <bool> -compression <type>" 36 * 37 * -matte <bool>: If set to false, a matte (alpha) channel is ignored 38 * during reading or writing. Default is true. 39 * -verbose <bool>: If set to true, additional information about the file 40 * format is printed to stdout. Default is false. 41 * -compression <type>: Set the compression mode to either "none" or "rle". 42 * Default is "rle". 43 * 44 * Notes: 45 * 46 * - As Targa files do not have a "magic number" somewhere in the file header, 47 * it is difficult to automatically recognize this format. 48 * Therefore it should be specified as one of the first entries in the list of 49 * package require tkimg::*. 50 * 51 * ENDHEADER 52 * 53 * $Id: tga.c 233 2010-04-01 09:28:00Z nijtmans $ 54 * 55 */ 56 57/* 58 * Generic initialization code, parameterized via CPACKAGE and PACKAGE. 59 */ 60 61#include "init.c" 62 63 64/* #define DEBUG_LOCAL */ 65 66/* Some defines and typedefs. */ 67#define TRUE 1 68#define FALSE 0 69typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ 70typedef unsigned char UByte; /* Unsigned 8 bit integer */ 71typedef char Byte; /* Signed 8 bit integer */ 72typedef short Short; /* Signed 16 bit integer */ 73typedef int Int; /* Signed 32 bit integer */ 74 75/* Supported TARGA version numbers */ 76#define TGA_RGB_UNCOMP 2 77#define TGA_RGB_COMP 10 78 79/* Macros needed for run-length encoding. */ 80#define TGA_MODE_SAME 0 81#define TGA_MODE_DIFF 1 82#define MINRUN 3 83#define MAXRUN 127 84 85/* Macros for acessing header fields */ 86#define ENC_LEFT_RIGHT(imgdes) (((imgdes >> 4) & 0x1)? FALSE: TRUE) 87#define ENC_TOP_BOTTOM(imgdes) (((imgdes >> 5) & 0x1)? TRUE: FALSE) 88#define NCHAN(pixsize) ((pixsize == 24) ? 3: 4) 89#define IS_COMPRESSED(imgtyp) ((imgtyp == TGA_RGB_COMP)? TRUE: FALSE) 90 91/* The Targa header structure */ 92typedef struct { 93 UByte numid; 94 UByte maptyp; 95 UByte imgtyp; 96 Short maporig; 97 Short mapsize; 98 UByte mapbits; 99 Short xorig; 100 Short yorig; 101 Short xsize; 102 Short ysize; 103 UByte pixsize; 104 UByte imgdes; 105} TGAHEADER; 106 107/* Structure to hold information about the Targa file being processed. */ 108typedef struct { 109 TGAHEADER th; 110 Int scanrest, /* Number of pixels belonging to next scanline */ 111 scanmode; /* Current compression mode */ 112 UByte *red, /* Pointers to step through scanlines */ 113 *green, 114 *blue, 115 *matte; 116 UByte *redScan, /* Buffer for one scanline: Red channel */ 117 *greenScan, /* Buffer for one scanline: Green channel */ 118 *blueScan, /* Buffer for one scanline: Blue channel */ 119 *matteScan; /* Buffer for one scanline: Matte channel */ 120 UByte *pixbuf; 121#ifdef DEBUG_LOCAL 122 Int total; 123#endif 124} TGAFILE; 125 126static void tgaClose(TGAFILE *tf) 127{ 128 if (tf->redScan) ckfree((char *)tf->redScan); 129 if (tf->greenScan) ckfree((char *)tf->greenScan); 130 if (tf->blueScan) ckfree((char *)tf->blueScan); 131 if (tf->matteScan) ckfree((char *)tf->matteScan); 132 if (tf->pixbuf) ckfree((char *)tf->pixbuf); 133 return; 134} 135 136static Boln readError(Tcl_Interp *interp) 137{ 138 Tcl_AppendResult(interp, "Unexpected end of file", (char *) NULL); 139 return FALSE; 140} 141 142/* This function is commented out because it is not used anywhere 143static Boln writeError(Tcl_Interp *interp) 144{ 145 Tcl_AppendResult(interp, "Error writing to file", (char *) NULL); 146 return FALSE; 147} 148*/ 149 150/* Read 1 byte, representing an unsigned integer number. */ 151 152static Boln readUByte (tkimg_MFile *handle, UByte *b) 153{ 154 char buf[1]; 155 if (1 != tkimg_Read(handle, buf, 1)) 156 return FALSE; 157 *b = buf[0]; 158 return TRUE; 159} 160 161/* Read 2 bytes, representing a short integer in the form <LowByte, HighByte>, 162 from a file and convert them into the current machine's format. */ 163 164static Boln readShort (tkimg_MFile *handle, Short *s) 165{ 166 char buf[2]; 167 if (2 != tkimg_Read(handle, buf, 2)) 168 return FALSE; 169 *s = (buf[0] & 0xFF) | (buf[1] << 8); 170 return TRUE; 171} 172 173/* Write a byte, representing an unsigned integer to a file. */ 174 175static Boln writeUByte (tkimg_MFile *handle, UByte b) 176{ 177 UByte buf[1]; 178 buf[0] = b; 179 if (1 != tkimg_Write(handle, (const char *)buf, 1)) 180 return FALSE; 181 return TRUE; 182} 183 184/* Write a byte, representing a signed integer to a file. */ 185 186static Boln writeByte(tkimg_MFile *handle, Byte b) 187{ 188 Byte buf[1]; 189 buf[0] = b; 190 if (1 != tkimg_Write(handle, buf, 1)) 191 return FALSE; 192 return TRUE; 193} 194 195/* Convert a short integer number into the format <LowByte, HighByte> (an array 196 of 2 bytes) and write the array to a file. */ 197 198static Boln writeShort (tkimg_MFile *handle, Short s) 199{ 200 Byte buf[2]; 201 buf[0] = s; 202 buf[1] = s >> 8; 203 if (2 != tkimg_Write(handle, buf, 2)) 204 return FALSE; 205 return TRUE; 206} 207 208#define OUT Tcl_WriteChars (outChan, str, -1) 209static void printImgInfo (TGAHEADER *th, const char *filename, const char *msg) 210{ 211 Tcl_Channel outChan; 212 char str[256]; 213 214 outChan = Tcl_GetStdChannel (TCL_STDOUT); 215 if (!outChan) { 216 return; 217 } 218 219 sprintf(str, "%s %s\n", msg, filename); OUT; 220 sprintf(str, "\tSize in pixel : %d x %d\n", th->xsize, th->ysize); OUT; 221 sprintf(str, "\tNo. of channels : %d\n", NCHAN(th->pixsize)); OUT; 222 sprintf(str, "\tCompression : %s\n", 223 IS_COMPRESSED(th->imgtyp)? "RLE": "None"); OUT; 224 sprintf(str, "\tVertical encoding : %s\n", 225 ENC_TOP_BOTTOM(th->imgdes)? "Top -> Bottom": "Bottom -> Top"); OUT; 226 sprintf(str, "\tHorizontal encoding: %s\n", 227 ENC_LEFT_RIGHT(th->imgdes)? "Left -> Right": "Right -> Left"); OUT; 228 Tcl_Flush(outChan); 229} 230#undef OUT 231static Boln readHeader (tkimg_MFile *handle, TGAHEADER *th) 232{ 233 Int i; 234 UByte dummy; 235 236 if (!readUByte (handle, &th->numid) || 237 !readUByte (handle, &th->maptyp) || 238 !readUByte (handle, &th->imgtyp) || 239 !readShort (handle, &th->maporig) || 240 !readShort (handle, &th->mapsize) || 241 !readUByte (handle, &th->mapbits) || 242 !readShort (handle, &th->xorig) || 243 !readShort (handle, &th->yorig) || 244 !readShort (handle, &th->xsize) || 245 !readShort (handle, &th->ysize) || 246 !readUByte (handle, &th->pixsize) || 247 !readUByte (handle, &th->imgdes)) 248 return FALSE; 249 250 /* Try to find out if this file can possibly be a TARGA pixel file. */ 251 if (!((th->imgtyp == TGA_RGB_UNCOMP || th->imgtyp == TGA_RGB_COMP) && 252 (th->pixsize == 24 || th->pixsize == 32))) { 253 return FALSE; 254 } 255 256 for ( i=0; i<th->numid; i++) { 257 if (!readUByte (handle, &dummy)) 258 return FALSE; 259 } 260 261 if (th->xsize < 1 || th->ysize < 1) { 262 return FALSE; 263 } 264 265 /* Skip colormap data, if present. */ 266 if (th->mapsize > 0) 267 { Int mapbytes; 268 switch (th->mapbits) 269 { 270 case 15: 271 case 16: 272 mapbytes = 2 * th->mapsize; 273 break; 274 case 24: 275 mapbytes = 3 * th->mapsize; 276 break; 277 case 32: 278 mapbytes = 4 * th->mapsize; 279 break; 280 default: 281 return FALSE; 282 } 283 while (mapbytes--) 284 if (!readUByte (handle, &dummy)) 285 return FALSE; 286 } 287 return TRUE; 288} 289 290static Boln writeHeader(tkimg_MFile *handle, TGAHEADER *th) 291{ 292 if (!writeUByte (handle, th->numid) || 293 !writeUByte (handle, th->maptyp) || 294 !writeUByte (handle, th->imgtyp) || 295 !writeShort (handle, th->maporig) || 296 !writeShort (handle, th->mapsize) || 297 !writeUByte (handle, th->mapbits) || 298 !writeShort (handle, th->xorig) || 299 !writeShort (handle, th->yorig) || 300 !writeShort (handle, th->xsize) || 301 !writeShort (handle, th->ysize) || 302 !writeUByte (handle, th->pixsize) || 303 !writeUByte (handle, th->imgdes)) 304 return FALSE; 305 return TRUE; 306} 307 308/* A pixel is represented by 3 or 4 bytes in the order Blue/Green/Red/Alpha. 309 We are converting the order into standard RGBA order. 310 Note that TARGA allows pixel values to be compressed across scanline 311 boundaries. 312*/ 313 314/* Read the value of a pixel from "handle" and assume it must be repeated "n" 315 times. 316*/ 317 318static Boln readRlePixel (Tcl_Interp *interp, tkimg_MFile *handle, UByte **pixBufPtr, 319 Int *countPtr, Int stop, Int n, TGAFILE *tf) 320{ 321 Int i, count, nchan; 322 UByte localBuf[4]; 323 324 nchan = NCHAN(tf->th.pixsize); 325 if (nchan != tkimg_Read(handle, (char *)localBuf, nchan)) 326 return readError (interp); 327 count = *countPtr; 328 for (i=0; i<n; i++) 329 { 330#ifdef DEBUG_LOCAL 331 tf->total++; 332#endif 333 (*pixBufPtr)[0] = localBuf[2]; 334 (*pixBufPtr)[1] = localBuf[1]; 335 (*pixBufPtr)[2] = localBuf[0]; 336 if (nchan == 4) 337 (*pixBufPtr)[3] = localBuf[3]; 338 (*pixBufPtr) += nchan; 339 count++; 340 341 if (count == stop) 342 { /* Scanline is filled with pixel values. 343 Determine the number of pixels to keep for next scanline. */ 344 tf->scanrest = n - i - 1; 345 *countPtr = count; 346 return TRUE; 347 } 348 } 349 *countPtr = count; 350 return TRUE; 351} 352 353/* The channels of scan line number "y" are read. */ 354 355static Boln tgaReadScan (Tcl_Interp *interp, tkimg_MFile *handle, 356 TGAFILE *tf, Int y) 357{ 358 Int nchan; 359 Int count, stop; 360 UByte localBuf[4]; 361 UByte *pixBufPtr; 362 363 count = 0; 364 stop = tf->th.xsize; 365 nchan = NCHAN(tf->th.pixsize); 366 pixBufPtr = tf->pixbuf; 367 368#ifdef DEBUG_LOCAL 369 tf->total = 0; 370#endif 371 372 if (IS_COMPRESSED (tf->th.imgtyp)) { 373 Byte cbuf[1]; 374 Int pix, numpix; 375 /* While there are pixels left from the previous scanline, 376 either fill the current scanline with the pixel value 377 still stored in "pixbuf" (TGA_MODE_SAME) or read in the 378 appropriate number of pixel values (TGA_MODE_DIFF). */ 379 while (tf->scanrest) { 380 if (tf->scanmode == TGA_MODE_DIFF) { 381 if (nchan != tkimg_Read(handle, (char *)localBuf, nchan)) 382 return readError (interp); 383 } 384#ifdef DEBUG_LOCAL 385 tf->total++; 386#endif 387 *pixBufPtr++ = localBuf[2]; 388 *pixBufPtr++ = localBuf[1]; 389 *pixBufPtr++ = localBuf[0]; 390 if (nchan == 4) 391 *pixBufPtr++ = localBuf[3]; 392 count++; 393 394 tf->scanrest--; 395 /* If the image is small, the compression might go over several 396 scanlines. */ 397 if (count == stop) 398 return TRUE; 399 } 400 401 /* Read the byte telling us the compression mode and the compression 402 count. Then read the pixel values till a scanline is filled. */ 403 do { 404 if (1 != tkimg_Read(handle, cbuf, 1)) 405 return readError (interp); 406 numpix = (cbuf[0] & 0x7F) + 1; 407 408 if ((cbuf[0] & 0x80) != 0x80) { 409 tf->scanmode = TGA_MODE_DIFF; 410 for (pix=0; pix<numpix; pix++) { 411 if (!readRlePixel (interp, handle, &pixBufPtr, 412 &count, stop, 1, tf)) 413 return FALSE; 414 if (count == stop) { 415 tf->scanrest = numpix - pix - 1; 416 break; 417 } 418 } 419 } else { 420 tf->scanmode = TGA_MODE_SAME; 421 if (!readRlePixel (interp, handle, &pixBufPtr, 422 &count, stop, numpix, tf)) 423 return FALSE; 424 } 425 } while (count < stop); 426 427#ifdef DEBUG_LOCAL 428 printf("\tScanline %d: Pixels: %d Rest: %d\n", 429 y, tf->total, tf->scanrest); 430#endif 431 } else { 432 /* Read uncompressed pixel data. */ 433 Int i, bytesPerLine; 434 UByte curPix; 435 436 bytesPerLine = nchan * tf->th.xsize; 437 if (bytesPerLine != tkimg_Read(handle, (char *)tf->pixbuf, bytesPerLine)) 438 return readError (interp); 439 440 for (i=0; i<stop; i++) { 441 curPix = pixBufPtr[2]; 442 pixBufPtr[2] = pixBufPtr[0]; 443 pixBufPtr[0] = curPix; 444 pixBufPtr += nchan; 445 } 446 } 447 return TRUE; 448} 449 450static Boln writePixel(tkimg_MFile *handle, UByte b, UByte g, 451 UByte r, UByte m, Int nchan) 452{ 453 UByte buf[4]; 454 buf[0] = b; 455 buf[1] = g; 456 buf[2] = r; 457 buf[3] = m; 458 if (nchan != tkimg_Write(handle, (const char *)buf, nchan)) 459 return FALSE; 460 return TRUE; 461} 462 463static Boln tgaWriteScan(Tcl_Interp *interp, tkimg_MFile *handle, 464 TGAFILE *tf, Int y) 465{ 466 UByte *stop, *red_end, *green_end, *blue_end, *matte_end; 467 Int nchan; 468 469 tf->red = tf->redScan; 470 tf->green = tf->greenScan; 471 tf->blue = tf->blueScan; 472 tf->matte = tf->matteScan; 473 stop = tf->red + tf->th.xsize; 474 nchan = NCHAN(tf->th.pixsize); 475 476 /* Write the scanline data to the file. */ 477 if (! IS_COMPRESSED(tf->th.imgtyp)) { 478 while (tf->red < stop) 479 { 480 if (!writePixel(handle, *tf->blue, *tf->green, *tf->red, *tf->matte, nchan)) 481 return FALSE; 482 tf->blue++; 483 tf->green++; 484 tf->red++; 485 tf->matte++; 486 } 487 } 488 else /* Run-length Compression */ 489 { 490 red_end = tf->red + 1; 491 green_end = tf->green + 1; 492 blue_end = tf->blue + 1; 493 matte_end = tf->matte + 1; 494 while (tf->red < stop) 495 { 496 while (red_end < stop && 497 *tf->red == *red_end && 498 *tf->green == *green_end && 499 *tf->blue == *blue_end && 500 red_end - tf->red - 1 < MAXRUN) 501 { 502 if (nchan == 4) 503 { 504 if (*tf->matte != *matte_end) 505 break; 506 } 507 red_end++; 508 green_end++; 509 blue_end++; 510 matte_end++; 511 } 512 if (red_end - tf->red >= MINRUN) 513 { /* Found a run of compressable data */ 514 if (!writeByte(handle, (Byte)(((red_end - tf->red)-1)|0x80)) || 515 !writePixel(handle, *tf->blue, *tf->green, *tf->red, *tf->matte, nchan)) 516 return FALSE; 517 tf->red = red_end; 518 tf->green = green_end; 519 tf->blue = blue_end; 520 tf->matte = matte_end; 521 } 522 else 523 { /* Found a run of uncompressable data */ 524 while (red_end < stop && 525 ((red_end + 1 >= stop || 526 *red_end != *(red_end + 1)) || 527 (red_end + 2 >= stop || 528 *(red_end + 1) != *(red_end + 2))) && 529 ((green_end + 1 >= stop || 530 *green_end != *(green_end + 1)) || 531 (green_end + 2 >= stop || 532 *(green_end + 1) != *(green_end + 2))) && 533 ((blue_end + 1 >= stop || 534 *blue_end != *(blue_end + 1)) || 535 (blue_end + 2 >= stop || 536 *(blue_end + 1) != *(blue_end + 2))) && 537 red_end - tf->red < MAXRUN) 538 { 539 if (nchan == 4) 540 { 541 if (! ((matte_end + 1 >= stop || 542 *matte_end != *(matte_end + 1)) || 543 (matte_end + 2 >= stop || 544 *(matte_end + 1) != *(matte_end + 2)))) 545 break; 546 } 547 red_end++; 548 green_end++; 549 blue_end++; 550 matte_end++; 551 } 552 if (!writeByte(handle, (Byte)((red_end - tf->red) - 1))) 553 return FALSE; 554 while (tf->red < red_end) 555 { 556 if (!writePixel(handle, *tf->blue, *tf->green, *tf->red, *tf->matte, nchan)) 557 return FALSE; 558 tf->red++; 559 tf->green++; 560 tf->blue++; 561 tf->matte++; 562 } 563 } 564 red_end++; 565 green_end++; 566 blue_end++; 567 matte_end++; 568 } 569 } 570 return TRUE; 571} 572 573/* 574 * Here is the start of the standard functions needed for every image format. 575 */ 576 577/* 578 * Prototypes for local procedures defined in this file: 579 */ 580 581static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, 582 int *comp, int *verb, int *matte); 583static int CommonMatch(tkimg_MFile *handle, int *widthPtr, 584 int *heightPtr, TGAHEADER *tgaHeaderPtr); 585static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, 586 const char *filename, Tcl_Obj *format, 587 Tk_PhotoHandle imageHandle, int destX, int destY, 588 int width, int height, int srcX, int srcY); 589static int CommonWrite(Tcl_Interp *interp, 590 const char *filename, Tcl_Obj *format, 591 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr); 592 593static int ParseFormatOpts(interp, format, comp, verb, matte) 594 Tcl_Interp *interp; 595 Tcl_Obj *format; 596 int *comp; 597 int *verb; 598 int *matte; 599{ 600 static const char *const tgaOptions[] = {"-compression", "-verbose", "-matte"}; 601 int objc, length, c, i, index; 602 Tcl_Obj **objv; 603 const char *compression, *verbose, *transp; 604 605 *comp = TGA_RGB_COMP; 606 *verb = 0; 607 *matte = 1; 608 if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) 609 return TCL_ERROR; 610 if (objc) { 611 compression = "rle"; 612 verbose = "0"; 613 transp = "1"; 614 for (i=1; i<objc; i++) { 615 if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)tgaOptions, 616 "format option", 0, &index) != TCL_OK) { 617 return TCL_ERROR; 618 } 619 if (++i >= objc) { 620 Tcl_AppendResult(interp, "No value for option \"", 621 Tcl_GetStringFromObj (objv[--i], (int *) NULL), 622 "\"", (char *) NULL); 623 return TCL_ERROR; 624 } 625 switch(index) { 626 case 0: 627 compression = Tcl_GetStringFromObj(objv[i], (int *) NULL); 628 break; 629 case 1: 630 verbose = Tcl_GetStringFromObj(objv[i], (int *) NULL); 631 break; 632 case 2: 633 transp = Tcl_GetStringFromObj(objv[i], (int *) NULL); 634 break; 635 } 636 } 637 638 c = compression[0]; length = strlen (compression); 639 if ((c == 'n') && (!strncmp (compression, "none", length))) { 640 *comp = TGA_RGB_UNCOMP; 641 } else if ((c == 'r') && (!strncmp (compression, "rle",length))) { 642 *comp = TGA_RGB_COMP; 643 } else { 644 Tcl_AppendResult(interp, "invalid compression mode \"", 645 compression, "\": should be rle or none", (char *) NULL); 646 return TCL_ERROR; 647 } 648 649 c = verbose[0]; length = strlen (verbose); 650 if (!strncmp (verbose, "1", length) || \ 651 !strncmp (verbose, "true", length) || \ 652 !strncmp (verbose, "on", length)) { 653 *verb = 1; 654 } else if (!strncmp (verbose, "0", length) || \ 655 !strncmp (verbose, "false", length) || \ 656 !strncmp (verbose, "off", length)) { 657 *verb = 0; 658 } else { 659 Tcl_AppendResult(interp, "invalid verbose mode \"", verbose, 660 "\": should be 1 or 0, on or off, true or false", 661 (char *) NULL); 662 return TCL_ERROR; 663 } 664 665 c = transp[0]; length = strlen (transp); 666 if (!strncmp (transp, "1", length) || \ 667 !strncmp (transp, "true", length) || \ 668 !strncmp (transp, "on", length)) { 669 *matte = 1; 670 } else if (!strncmp (transp, "0", length) || \ 671 !strncmp (transp, "false", length) || \ 672 !strncmp (transp, "off", length)) { 673 *matte = 0; 674 } else { 675 Tcl_AppendResult(interp, "invalid alpha (matte) mode \"", verbose, 676 "\": should be 1 or 0, on or off, true or false", 677 (char *) NULL); 678 return TCL_ERROR; 679 } 680 } 681 return TCL_OK; 682} 683 684static int ChnMatch( 685 Tcl_Channel chan, 686 const char *filename, 687 Tcl_Obj *format, 688 int *widthPtr, 689 int *heightPtr, 690 Tcl_Interp *interp 691) { 692 tkimg_MFile handle; 693 694 handle.data = (char *) chan; 695 handle.state = IMG_CHAN; 696 697 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 698} 699 700static int ObjMatch( 701 Tcl_Obj *data, 702 Tcl_Obj *format, 703 int *widthPtr, 704 int *heightPtr, 705 Tcl_Interp *interp 706) { 707 tkimg_MFile handle; 708 709 if (!tkimg_ReadInit (data, 0, &handle)) { 710 tkimg_ReadInit (data, '*', &handle); 711 } 712 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 713} 714 715static int CommonMatch(handle, widthPtr, heightPtr, tgaHeaderPtr) 716 tkimg_MFile *handle; 717 int *widthPtr; 718 int *heightPtr; 719 TGAHEADER *tgaHeaderPtr; 720{ 721 TGAHEADER th; 722 723 if (!readHeader (handle, &th)) 724 return 0; 725 726 *widthPtr = th.xsize; 727 *heightPtr = th.ysize; 728 if (tgaHeaderPtr) 729 *tgaHeaderPtr = th; 730 return 1; 731} 732 733static int ChnRead(interp, chan, filename, format, imageHandle, 734 destX, destY, width, height, srcX, srcY) 735 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 736 Tcl_Channel chan; /* The image channel, open for reading. */ 737 const char *filename; /* The name of the image file. */ 738 Tcl_Obj *format; /* User-specified format object, or NULL. */ 739 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 740 int destX, destY; /* Coordinates of top-left pixel in 741 * photo image to be written to. */ 742 int width, height; /* Dimensions of block of photo image to 743 * be written to. */ 744 int srcX, srcY; /* Coordinates of top-left pixel to be used 745 * in image being read. */ 746{ 747 tkimg_MFile handle; 748 749 handle.data = (char *) chan; 750 handle.state = IMG_CHAN; 751 752 return CommonRead(interp, &handle, filename, format, imageHandle, 753 destX, destY, width, height, srcX, srcY); 754} 755 756static int ObjRead(interp, data, format, imageHandle, 757 destX, destY, width, height, srcX, srcY) 758 Tcl_Interp *interp; 759 Tcl_Obj *data; 760 Tcl_Obj *format; 761 Tk_PhotoHandle imageHandle; 762 int destX, destY; 763 int width, height; 764 int srcX, srcY; 765{ 766 tkimg_MFile handle; 767 768 if (!tkimg_ReadInit(data, 0, &handle)) { 769 tkimg_ReadInit(data, '*', &handle); 770 } 771 return CommonRead(interp, &handle, "InlineData", format, imageHandle, 772 destX, destY, width, height, srcX, srcY); 773} 774 775static int CommonRead(interp, handle, filename, format, imageHandle, 776 destX, destY, width, height, srcX, srcY) 777 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 778 tkimg_MFile *handle; /* The image file, open for reading. */ 779 const char *filename; /* The name of the image file. */ 780 Tcl_Obj *format; /* User-specified format object, or NULL. */ 781 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 782 int destX, destY; /* Coordinates of top-left pixel in 783 * photo image to be written to. */ 784 int width, height; /* Dimensions of block of photo image to 785 * be written to. */ 786 int srcX, srcY; /* Coordinates of top-left pixel to be used 787 * in image being read. */ 788{ 789 Tk_PhotoImageBlock block; 790 Int y, nchan; 791 int fileWidth, fileHeight; 792 int stopY, outY, outWidth, outHeight; 793 TGAFILE tf; 794 int compr, verbose, matte; 795 char errMsg[200]; 796 int result = TCL_OK; 797 798 memset (&tf, 0, sizeof (TGAFILE)); 799 if (ParseFormatOpts (interp, format, &compr, &verbose, &matte) != TCL_OK) { 800 return TCL_ERROR; 801 } 802 803 if (!CommonMatch(handle, &fileWidth, &fileHeight, &tf.th)) 804 return TCL_ERROR; 805 if (verbose) 806 printImgInfo (&tf.th, filename, "Reading image:"); 807 808 if ((srcX + width) > fileWidth) { 809 outWidth = fileWidth - srcX; 810 } else { 811 outWidth = width; 812 } 813 if ((srcY + height) > fileHeight) { 814 outHeight = fileHeight - srcY; 815 } else { 816 outHeight = height; 817 } 818 if ((outWidth <= 0) || (outHeight <= 0) 819 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 820 return TCL_OK; 821 } 822 823 if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { 824 return TCL_ERROR; 825 } 826 827 if (IS_COMPRESSED(tf.th.imgtyp)) { 828 tkimg_ReadBuffer(1); 829 } 830 831 tf.scanmode = TGA_MODE_DIFF; 832 nchan = NCHAN(tf.th.pixsize); 833 834 tf.pixbuf = (UByte *) ckalloc (fileWidth * nchan); 835 if (!tf.pixbuf) { 836 sprintf(errMsg, "Can't allocate memory of size %d", fileWidth * nchan); 837 Tcl_AppendResult(interp, errMsg, (char *)NULL); 838 tkimg_ReadBuffer (0); 839 return TCL_ERROR; 840 } 841 842 block.pixelSize = nchan; 843 block.pitch = fileWidth * nchan; 844 block.width = outWidth; 845 block.height = 1; 846 block.offset[0] = 0; 847 block.offset[1] = 1; 848 block.offset[2] = 2; 849 if (nchan < 4) { 850 matte = 0; 851 } 852 block.offset[3] = matte? 3: 0; 853 block.pixelPtr = tf.pixbuf + srcX * nchan; 854 855 stopY = srcY + outHeight; 856 857 if (ENC_TOP_BOTTOM (tf.th.imgdes)) { 858 outY = destY; 859 for (y=0; y<stopY; y++) { 860 tgaReadScan(interp, handle, &tf, y); 861 if (y >= srcY) { 862 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, matte? TK_PHOTO_COMPOSITE_OVERLAY: TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { 863 result = TCL_ERROR; 864 break; 865 } 866 outY++; 867 } 868 } 869 } else { 870 outY = destY + outHeight - 1; 871 for (y=fileHeight-1; y>=0; y--) { 872 tgaReadScan(interp, handle, &tf, y); 873 if (y >= srcY && y < stopY) { 874 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { 875 result = TCL_ERROR; 876 break; 877 } 878 outY--; 879 } 880 } 881 } 882 tgaClose(&tf); 883 tkimg_ReadBuffer(0); 884 return result; 885} 886 887static int ChnWrite(interp, filename, format, blockPtr) 888 Tcl_Interp *interp; 889 const char *filename; 890 Tcl_Obj *format; 891 Tk_PhotoImageBlock *blockPtr; 892{ 893 Tcl_Channel chan; 894 tkimg_MFile handle; 895 int result; 896 897 chan = tkimg_OpenFileChannel(interp, filename, 0644); 898 if (!chan) { 899 return TCL_ERROR; 900 } 901 902 handle.data = (char *) chan; 903 handle.state = IMG_CHAN; 904 905 result = CommonWrite(interp, filename, format, &handle, blockPtr); 906 if (Tcl_Close(interp, chan) == TCL_ERROR) { 907 return TCL_ERROR; 908 } 909 return result; 910} 911 912static int StringWrite( 913 Tcl_Interp *interp, 914 Tcl_Obj *format, 915 Tk_PhotoImageBlock *blockPtr 916) { 917 tkimg_MFile handle; 918 int result; 919 Tcl_DString data; 920 921 Tcl_DStringInit(&data); 922 tkimg_WriteInit(&data, &handle); 923 result = CommonWrite(interp, "InlineData", format, &handle, blockPtr); 924 tkimg_Putc(IMG_DONE, &handle); 925 926 if (result == TCL_OK) { 927 Tcl_DStringResult(interp, &data); 928 } else { 929 Tcl_DStringFree(&data); 930 } 931 return result; 932} 933 934static int CommonWrite (interp, filename, format, handle, blockPtr) 935 Tcl_Interp *interp; 936 const char *filename; 937 Tcl_Obj *format; 938 tkimg_MFile *handle; 939 Tk_PhotoImageBlock *blockPtr; 940{ 941 Int x, y, nchan; 942 Int redOffset, greenOffset, blueOffset, alphaOffset; 943 UByte *pixelPtr, *rowPixPtr; 944 TGAFILE tf; 945 int compr, verbose, matte; /* Format options */ 946 char errMsg[200]; 947 948 memset(&tf, 0, sizeof(TGAFILE)); 949 if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) { 950 return TCL_ERROR; 951 } 952 953 redOffset = 0; 954 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 955 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 956 alphaOffset = blockPtr->offset[0]; 957 958 if (alphaOffset < blockPtr->offset[2]) { 959 alphaOffset = blockPtr->offset[2]; 960 } 961 if (++alphaOffset < blockPtr->pixelSize) { 962 alphaOffset -= blockPtr->offset[0]; 963 } else { 964 alphaOffset = 0; 965 } 966 967 nchan = ((matte && alphaOffset)? 4: 3); 968 969 tf.redScan = (UByte *) ckalloc(blockPtr->width); 970 tf.greenScan = (UByte *) ckalloc(blockPtr->width); 971 tf.blueScan = (UByte *) ckalloc(blockPtr->width); 972 tf.matteScan = (UByte *) ckalloc(blockPtr->width); 973 if (!tf.redScan || !tf.greenScan || !tf.blueScan || !tf.matteScan) { 974 sprintf(errMsg, "Can't allocate memory of size %d", blockPtr->width); 975 Tcl_AppendResult(interp, errMsg, (char *)NULL); 976 return TCL_ERROR; 977 } 978 979 /* Fill the targa header struct and write the header to the channel. */ 980 tf.th.pixsize = nchan * 8; 981 tf.th.xsize = blockPtr->width; 982 tf.th.ysize = blockPtr->height; 983 tf.th.imgdes = (1 << 5); /* Top->Bottom, Left->Right encoding */ 984 tf.th.imgtyp = compr; /* Uncompressed or RLE-compressed */ 985 986 if (!writeHeader(handle, &tf.th)) { 987 return TCL_ERROR; 988 } 989 990 rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0]; 991 for (y = 0; y < blockPtr->height; y++) { 992 tf.red = tf.redScan; 993 tf.green = tf.greenScan; 994 tf.blue = tf.blueScan; 995 tf.matte = tf.matteScan; 996 pixelPtr = rowPixPtr; 997 for (x = 0; x < blockPtr->width; x++) { 998 *(tf.red++) = pixelPtr[redOffset]; 999 *(tf.green++) = pixelPtr[greenOffset]; 1000 *(tf.blue++) = pixelPtr[blueOffset]; 1001 if (nchan == 4) { 1002 /* Have a matte channel and write it. */ 1003 *(tf.matte++) = pixelPtr[alphaOffset]; 1004 } 1005 pixelPtr += blockPtr->pixelSize; 1006 } 1007 if (!tgaWriteScan(interp, handle, &tf, y)) { 1008 tgaClose (&tf); 1009 return TCL_ERROR; 1010 } 1011 rowPixPtr += blockPtr->pitch; 1012 } 1013 if (verbose) 1014 printImgInfo(&tf.th, filename, "Saving image:"); 1015 tgaClose (&tf); 1016 return TCL_OK; 1017} 1018