1/* STARTHEADER 2 * 3 * File : sun.c 4 * 5 * Author : Paul Obermeier (paul@poSoft.de) 6 * 7 * Date : Mon Jan 22 21:32:48 CET 2001 8 * 9 * Copyright : (C) 2001-2002 Paul Obermeier 10 * 11 * Description : 12 * 13 * A photo image handler for SUN's Raster file format. 14 * 15 * The following image types are supported: 16 * 17 * 1-bit pixels: Black and White. 18 * 8-bit pixels: Grayscale or indexed. 19 * 24-bit pixels: True-color (RGB, each channel 8 bit). 20 * 32-bit pixels: True-color with alpha channel (RGBA, each channel 8 bit). 21 * 22 * List of currently supported features: 23 * 24 * Type | Read | Write | 25 * | -file | -data | -file | -data | 26 * ---------------------------------------- 27 * 1-bit | Yes | Yes | No | No | 28 * 8-bit | Yes | Yes | No | No | 29 * 24-bit | Yes | Yes | Yes | Yes | 30 * 32-bit | Yes | Yes | Yes | Yes | 31 * 32 * All images types may be either uncompressed or run-length encoded. 33 * 34 * 35 * The following format options are available: 36 * 37 * Read SUN image: "sun -matte <bool> -verbose <bool>" 38 * Write SUN image: "sun -matte <bool> -verbose <bool> -compression <type>" 39 * 40 * -matte <bool>: If set to false, a matte (alpha) channel is ignored 41 * during reading or writing. Default is true. 42 * -verbose <bool>: If set to true, additional information about the file 43 * format is printed to stdout. Default is false. 44 * -compression <type>: Set the compression mode to either "none" or "rle". 45 * Default is "rle". 46 * 47 * Notes: 48 * 49 * - The "UNIX" encoding of SUN's "imagetool" is not supported. 50 * 51 * - Part of this code was taken from the "sunras" GIMP plugin: 52 * 53 * >> The GIMP -- an image manipulation program 54 * >> Copyright (C) 1995 Spencer Kimball and Peter Mattis 55 * >> SUN raster reading and writing code Copyright (C) 1996 Peter Kirchgessner 56 * >> (email: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg) 57 * 58 * ENDHEADER 59 * 60 * $Id: sun.c 233 2010-04-01 09:28:00Z nijtmans $ 61 * 62 */ 63 64/* 65 * Generic initialization code, parameterized via CPACKAGE and PACKAGE. 66 */ 67 68#include "init.c" 69 70 71/* #define DEBUG_LOCAL */ 72 73/* Some defines and typedefs. */ 74#define TRUE 1 75#define FALSE 0 76typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ 77typedef char Byte; /* Signed 8 bit integer */ 78typedef unsigned char UByte; /* Unsigned 8 bit integer */ 79typedef unsigned short UShort; /* Unsigned 16 bit integer */ 80typedef unsigned int UInt; /* Unsigned 32 bit integer */ 81 82/* SunRaster magic number */ 83#define RAS_MAGIC 0x59a66a95 84 85/* Supported SunRaster types */ 86#define RAS_TYPE_STD 1 /* Standard uncompressed format */ 87#define RAS_TYPE_RLE 2 /* Runlength compression format */ 88 89/* The SunRaster header structure */ 90typedef struct { 91 UInt ras_magic; /* Magic Number */ 92 UInt ras_width; /* Width */ 93 UInt ras_height; /* Height */ 94 UInt ras_depth; /* Number of bits per pixel (1,8,24,32) */ 95 UInt ras_length; /* Length of image data (but may also be 0) */ 96 UInt ras_type; /* Encoding */ 97 UInt ras_maptype; /* Type of colormap */ 98 UInt ras_maplength;/* Number of bytes for colormap */ 99} SUNHEADER; 100 101/* Buffer for run-length encoding and decoding. */ 102typedef struct { 103 int val; /* The value that is to be repeated */ 104 int n; /* How many times it is repeated */ 105} RLEBUF; 106 107/* Forward declarations of static functions. */ 108/* This function is commented out because it is not used anywhere 109static void byte2bit (UByte *byteline, int width, UByte *bitline, int invert); 110*/ 111 112static void rle_startread (tkimg_MFile *ifp); 113static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp); 114static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp); 115static int rle_fgetc (tkimg_MFile *ifp); 116static int sun_getc (tkimg_MFile *ifp); 117#define rle_getc(fp) ((rlebuf.n > 0) ? (rlebuf.n)--,rlebuf.val : rle_fgetc (fp)) 118 119static void rle_startwrite (tkimg_MFile *ofp); 120/* This function is commented out because it is not used anywhere 121static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp); 122*/ 123static int rle_fputc (int val, tkimg_MFile *ofp); 124static int rle_putrun (int n, int val, tkimg_MFile *ofp); 125static void rle_endwrite (tkimg_MFile *ofp); 126 127static Boln read_sun_header (tkimg_MFile *ifp, SUNHEADER *sunhdr); 128static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr); 129static Boln read_sun_cols (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap); 130/* This function is commented out because it is not used anywhere 131static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap); 132*/ 133 134static RLEBUF 135 rlebuf; 136 137#ifdef DEBUG_LOCAL 138static Boln readUByte (tkimg_MFile *handle, UByte *b) 139{ 140 char buf[1]; 141 if (1 != tkimg_Read(handle, buf, 1)) 142 return FALSE; 143 *b = (UByte) buf[0]; 144 return TRUE; 145} 146#else 147 /* Use this macro for better performance, esp. when reading RLE files. */ 148# define readUByte(h,b) (1 == tkimg_Read((h),(char *)(b),1)) 149#endif 150 151static Boln writeUByte (tkimg_MFile *handle, UByte b) 152{ 153 UByte buf[1]; 154 buf[0] = b; 155 if (1 != tkimg_Write(handle, (const char *)buf, 1)) 156 return FALSE; 157 return TRUE; 158} 159 160static Boln readUInt (tkimg_MFile *ifp, UInt *i) 161{ 162 UByte buf[4]; 163 UInt c; 164 165 if (4 != tkimg_Read(ifp, (char *)buf, 4)) { 166 return FALSE; 167 } 168 169 c = (((UInt)(buf[0])) << 24); 170 c |= (((UInt)(buf[1])) << 16); 171 c |= (((UInt)(buf[2])) << 8); 172 c |= ((UInt)(buf[3])); 173 *i = c; 174 return TRUE; 175} 176 177static Boln writeUInt (tkimg_MFile *ofp, UInt c) 178{ 179 UByte buf[4]; 180 181 buf[0] = (c >> 24) & 0xff; 182 buf[1] = (c >> 16) & 0xff; 183 buf[2] = (c >> 8) & 0xff; 184 buf[3] = (c ) & 0xff; 185 if (4 != tkimg_Write(ofp, (const char *)buf, 4)) { 186 return FALSE; 187 } 188 return TRUE; 189} 190 191/* Convert n bytes of 0/1 to a line of bits */ 192/* This function is commented out because it is not used anywhere 193static void byte2bit (UByte *byteline, int width, 194 UByte *bitline, int invert) 195{ 196 UByte bitval; 197 UByte rest[8]; 198 199 while (width >= 8) { 200 bitval = 0; 201 if (*(byteline++)) bitval |= 0x80; 202 if (*(byteline++)) bitval |= 0x40; 203 if (*(byteline++)) bitval |= 0x20; 204 if (*(byteline++)) bitval |= 0x10; 205 if (*(byteline++)) bitval |= 0x08; 206 if (*(byteline++)) bitval |= 0x04; 207 if (*(byteline++)) bitval |= 0x02; 208 if (*(byteline++)) bitval |= 0x01; 209 *(bitline++) = invert ? ~bitval : bitval; 210 width -= 8; 211 } 212 if (width > 0) { 213 memset (rest, 0, 8); 214 memcpy (rest, byteline, width); 215 bitval = 0; 216 byteline = rest; 217 if (*(byteline++)) bitval |= 0x80; 218 if (*(byteline++)) bitval |= 0x40; 219 if (*(byteline++)) bitval |= 0x20; 220 if (*(byteline++)) bitval |= 0x10; 221 if (*(byteline++)) bitval |= 0x08; 222 if (*(byteline++)) bitval |= 0x04; 223 if (*(byteline++)) bitval |= 0x02; 224 *bitline = invert ? ~bitval : bitval; 225 } 226} 227*/ 228 229/* Start reading Runlength Encoded Data */ 230static void rle_startread (tkimg_MFile *ifp) 231{ 232 (void) ifp; 233 rlebuf.val = rlebuf.n = 0; 234} 235 236/* Read pixels from RLE-stream */ 237static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp) 238{ 239 int elem_read, cnt, val, err = 0; 240 241 for (elem_read = 0; elem_read < nelem; elem_read++) 242 { 243 for (cnt = 0; cnt < sz; cnt++) 244 { 245 val = rle_getc (ifp); 246 if (val < 0) { 247 err = 1; 248 break; 249 } 250 *(ptr++) = (char)val; 251 } 252 if (err) 253 break; 254 } 255 return elem_read; 256} 257 258/* Read uncompressed pixels from input stream "ifp" */ 259static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp) 260{ 261 if (nelem*sz != tkimg_Read(ifp, ptr, nelem*sz)) { 262 return -1; 263 } 264 return nelem; 265} 266 267/* Get one pixel from RLE-stream */ 268static int rle_fgetc (tkimg_MFile *ifp) 269{ 270 UByte flag, runcnt, runval; 271 272 if (rlebuf.n > 0) /* Something in the buffer ? */ 273 { 274 (rlebuf.n)--; 275 return rlebuf.val; 276 } 277 278 /* Nothing in the buffer. We have to read something */ 279 if (!readUByte (ifp, &flag)) 280 return -1; 281 if (flag != 0x80) return flag; /* Single byte run ? */ 282 283 if (!readUByte (ifp, &runcnt)) 284 return -1; 285 if (runcnt == 0) return (0x80); /* Single 0x80 ? */ 286 287 /* The run */ 288 if (!readUByte (ifp, &runval)) 289 return -1; 290 rlebuf.n = runcnt; 291 rlebuf.val = runval; 292 return runval; 293} 294 295/* Read one byte from input stream "ifp" */ 296static int sun_getc (tkimg_MFile *ifp) 297{ 298 UByte val; 299 if (!readUByte (ifp, &val)) 300 return -1; 301 return val; 302} 303 304/* Start writing Runlength Encoded Data */ 305static void rle_startwrite (tkimg_MFile *ofp) 306{ 307 (void) ofp; 308 rlebuf.val = 0; 309 rlebuf.n = 0; 310} 311 312/* Write uncompressed elements to RLE-stream */ 313/* This function is commented out because it is not used anywhere 314static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp) 315{ 316 int elem_write, cnt, val, err = 0; 317 UByte *pixels = (UByte *)ptr; 318 319 for (elem_write = 0; elem_write < nelem; elem_write++) { 320 for (cnt = 0; cnt < sz; cnt++) { 321 val = rle_fputc (*(pixels++), ofp); 322 if (val < 0) { 323 err = 1; 324 break; 325 } 326 } 327 if (err) 328 break; 329 } 330 return elem_write; 331} 332*/ 333 334/* Write uncompressed character to RLE-stream */ 335static int rle_fputc (int val, tkimg_MFile *ofp) 336{ 337 int retval; 338 339 if (rlebuf.n == 0) { /* Nothing in the buffer ? Save the value */ 340 rlebuf.n = 1; 341 rlebuf.val = val; 342 return val; 343 } 344 345 /* Something in the buffer */ 346 if (rlebuf.val == val) { /* Same value in the buffer ? */ 347 rlebuf.n++; 348 if (rlebuf.n == 257) { /* Can not be encoded in a single run ? */ 349 retval = rle_putrun (256, rlebuf.val, ofp); 350 if (retval < 0) 351 return retval; 352 rlebuf.n -= 256; 353 } 354 return val; 355 } 356 357 /* Something different in the buffer ? Write out the run */ 358 retval = rle_putrun (rlebuf.n, rlebuf.val, ofp); 359 if (retval < 0) 360 return retval; 361 362 /* Save the new value */ 363 rlebuf.n = 1; 364 return (rlebuf.val = val); 365} 366 367/* Write out a run with 0 < n < 257 */ 368static int rle_putrun (int n, int val, tkimg_MFile *ofp) 369{ 370 int retval = 1, 371 flag = 0x80; 372 373 /* Useful to write a 3 byte run ? */ 374 if ((n > 2) || ((n == 2) && (val == flag))) { 375 if (!writeUByte (ofp, flag) || 376 !writeUByte (ofp, n-1) || 377 !writeUByte (ofp, val)) { 378 retval = -1; 379 } 380 } 381 else if (n == 2) { 382 /* Write two single runs (could not be value 0x80) */ 383 if (!writeUByte (ofp, val) || !writeUByte (ofp, val)) { 384 retval = -1; 385 } 386 } else { /* Write a single run */ 387 if (val == flag) { 388 if (!writeUByte (ofp, flag) || !writeUByte (ofp, 0)) { 389 retval = -1; 390 } 391 } else { 392 if (!writeUByte (ofp, val)) { 393 retval = -1; 394 } 395 } 396 } 397 398 return ((retval < 0) ? retval : val); 399} 400 401/* End writing Runlength Encoded Data */ 402static void rle_endwrite (tkimg_MFile *ofp) 403{ 404 if (rlebuf.n > 0) { 405 rle_putrun (rlebuf.n, rlebuf.val, ofp); 406 rlebuf.val = rlebuf.n = 0; /* Clear RLE-buffer */ 407 } 408} 409 410#define OUT Tcl_WriteChars (outChan, str, -1) 411static void printImgInfo (SUNHEADER *sh, const char *filename, const char *msg) 412{ 413 Tcl_Channel outChan; 414 char str[256]; 415 UInt type = sh->ras_type; 416 417 outChan = Tcl_GetStdChannel (TCL_STDOUT); 418 if (!outChan) { 419 return; 420 } 421 422 sprintf(str, "%s %s\n", msg, filename); OUT; 423 sprintf(str, "\tSize in pixel : %d x %d\n", sh->ras_width, sh->ras_height); OUT; 424 sprintf(str, "\tDepth of pixels : %d\n", sh->ras_depth); OUT; 425 sprintf(str, "\tCompression : %s\n", (type == RAS_TYPE_STD? "None": 426 (type == RAS_TYPE_RLE? "RLE": 427 "Unknown"))); OUT; 428 sprintf(str, "\tColormap type : %d\n", sh->ras_maptype); OUT; 429 Tcl_Flush(outChan); 430} 431#undef OUT 432 433static Boln read_sun_header (tkimg_MFile *ifp, SUNHEADER *sunhdr) 434{ 435 int i; 436 UInt *cp; 437 438 cp = (UInt *)sunhdr; 439 440 /* Read in all 32-bit values of the header and check for byte order */ 441 for (i=0; i<sizeof(SUNHEADER) / sizeof(sunhdr->ras_magic); i++) { 442 if (!readUInt (ifp, cp)) 443 return FALSE; 444 cp++; 445 } 446 if (sunhdr->ras_magic != RAS_MAGIC) 447 return FALSE; 448 return TRUE; 449} 450 451/* Write out a SUN-fileheader */ 452static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr) 453{ 454 int i, hdr_entries; 455 UInt *cp; 456 457 cp = (UInt *)sunhdr; 458 459 hdr_entries = sizeof (SUNHEADER)/sizeof(sunhdr->ras_magic); 460 /* Write out all 32-bit values of the header and check for byte order */ 461 for (i=0; i<hdr_entries; i++) { 462 if (!writeUInt (ofp, *(cp++))) { 463 return FALSE; 464 } 465 } 466 return TRUE; 467} 468 469/* Read the sun colourmap */ 470static Boln read_sun_cols (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap) 471{ 472 int ncols; 473 474 /* Read in SUN-raster Colormap */ 475 ncols = sunhdr->ras_maplength / 3; 476 if (ncols <= 0) 477 return FALSE; 478 479 if (3*ncols != tkimg_Read(ifp, (char *)colormap, 3*ncols)) { 480 return FALSE; 481 } 482 return TRUE; 483} 484 485/* Write a sun colourmap */ 486/* This function is commented out because it is not used anywhere 487static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap) 488{ 489 int ncols; 490 491 ncols = sunhdr->ras_maplength / 3; 492 if (3*ncols != tkimg_Write(ofp, (const char *)colormap, 3*ncols)) { 493 return FALSE; 494 } 495 return TRUE; 496} 497*/ 498 499/* Load SUN Raster file with depth 1 */ 500static Boln load_sun_d1 (Tcl_Interp *interp, tkimg_MFile *ifp, 501 Tk_PhotoHandle imageHandle, int destX, int destY, 502 int width, int height, int srcX, int srcY, 503 int fileWidth, int fileHeight, int type) 504{ 505 UByte *dest, bit2byte[256*8]; 506 UByte *pixbuf; 507 Tk_PhotoImageBlock block; 508 int pix8; 509 int linepad; 510 int x, y; 511 int stopY, outY; 512 int i, j; 513 int err = 0, rle; 514 char errMsg[200]; 515 Boln result = TRUE; 516 517 pixbuf = (UByte *) ckalloc (fileWidth); 518 if (!pixbuf) { 519 sprintf(errMsg, "Can't allocate memory of size %d", fileWidth); 520 Tcl_AppendResult(interp, errMsg, (char *)NULL); 521 return TCL_ERROR; 522 } 523 524 block.pixelSize = 1; 525 block.pitch = fileWidth; 526 block.width = width; 527 block.height = 1; 528 block.offset[0] = 0; 529 block.offset[1] = 0; 530 block.offset[2] = 0; 531 block.offset[3] = 0; 532 block.pixelPtr = pixbuf + srcX; 533 534 rle = (type == RAS_TYPE_RLE); 535 linepad = ((fileWidth+7)/8) % 2; /* Check for 16bit align */ 536 537 if (rle) 538 rle_startread (ifp); 539 540 /* Get an array for mapping 8 bits in a byte to 8 bytes */ 541 dest = bit2byte; 542 for (j=0; j<256; j++) { 543 for (i=7; i>=0; i--) { 544 *(dest++) = ((j & (1 << i)) == 0) * 255; 545 } 546 } 547 548 stopY = srcY + height; 549 outY = destY; 550 551 for (y=0; y<stopY; y++) { 552 dest = pixbuf; 553 x = fileWidth; 554 while (x >= 8) { 555 pix8 = rle ? rle_getc (ifp) : sun_getc (ifp); 556 if (pix8 < 0) { err = 1; pix8 = 0; } 557 558 memcpy (dest, bit2byte + pix8*8, 8); 559 dest += 8; 560 x -= 8; 561 } 562 563 if (x>0) { 564 pix8 = rle ? rle_getc (ifp) : sun_getc (ifp); 565 if (pix8 < 0) { err = 1; pix8 = 0; } 566 567 memcpy (dest, bit2byte + pix8*8, x); 568 dest += x; 569 } 570 571 if (linepad) 572 err |= ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); 573 574 if (err) { 575 sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); 576 Tcl_AppendResult(interp, errMsg, (char *) NULL); 577 return FALSE; 578 } 579 if (y >= srcY) { 580 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { 581 result = FALSE; 582 break; 583 } 584 outY++; 585 } 586 } 587 return result; 588} 589 590/* Load SUN Raster file with depth 8 */ 591static Boln load_sun_d8 (Tcl_Interp *interp, tkimg_MFile *ifp, 592 Tk_PhotoHandle imageHandle, int destX, int destY, 593 int width, int height, int srcX, int srcY, 594 int fileWidth, int fileHeight, 595 int type, UByte *suncolmap, int maplength) 596{ 597 UByte *dest, *indData = NULL, *src; 598 UByte *pixbuf = NULL; 599 Tk_PhotoImageBlock block; 600 int linepad; 601 int x, y; 602 int stopY, outY; 603 int ncols; 604 int greyscale, nchan; 605 int err, rle; 606 char errMsg[200]; 607 Boln result = TRUE; 608 609 rle = (type == RAS_TYPE_RLE); 610 linepad = fileWidth % 2; 611 ncols = maplength / 3; 612 613 /* Check, if it's a greyscale or color indexed image. */ 614 greyscale = 1; 615 nchan = 1; 616 if ((ncols > 0) && (suncolmap != NULL)) { 617 greyscale = 0; 618 nchan = 3; 619 } 620 621 if (!greyscale) { 622 pixbuf = (UByte *) ckalloc (fileWidth * nchan); 623 if (!pixbuf) { 624 sprintf(errMsg, "Can't allocate memory of size %d", 625 fileWidth * nchan); 626 Tcl_AppendResult(interp, errMsg, (char *)NULL); 627 if (suncolmap) 628 ckfree ((char *)suncolmap); 629 return TCL_ERROR; 630 } 631 } 632 633 /* This buffer contains either the color indices or the greyscale value. */ 634 indData = (UByte *)ckalloc (fileWidth * sizeof (UByte)); 635 if (!indData) { 636 sprintf(errMsg, "Can't allocate memory of size %d", 637 fileWidth * sizeof (UByte)); 638 Tcl_AppendResult(interp, errMsg, (char *)NULL); 639 return TCL_ERROR; 640 } 641 642 block.pixelSize = nchan; 643 block.pitch = fileWidth * nchan; 644 block.width = width; 645 block.height = 1; 646 block.offset[0] = 0; 647 block.offset[1] = greyscale? 0: 1; 648 block.offset[2] = greyscale? 0: 2; 649 block.offset[3] = 0; 650 block.pixelPtr = (greyscale? 651 (indData + srcX * nchan): 652 (pixbuf + srcX * nchan)); 653 654 if (rle) 655 rle_startread (ifp); /* Initialize RLE-buffer */ 656 657 stopY = srcY + height; 658 outY = destY; 659 660 for (y=0; y<stopY; y++) { 661 src = indData; 662 memset ((char *)src, 0, fileWidth); 663 err = ((rle ? rle_fread ((char *)src, 1, fileWidth, ifp) : 664 sun_fread ((char *)src, 1, fileWidth, ifp)) != fileWidth); 665 if (err && (y != height -1)) { 666 sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); 667 Tcl_AppendResult(interp, errMsg, (char *) NULL); 668 ckfree ((char *)indData); 669 return FALSE; 670 } 671 if (linepad) { 672 err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); 673 if (err) { 674 sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); 675 Tcl_AppendResult(interp, errMsg, (char *) NULL); 676 ckfree ((char *)indData); 677 return FALSE; 678 } 679 } 680 681 if (!greyscale) { 682 src = indData; 683 dest = pixbuf; 684 for (x=0; x<width; x++) { 685 *dest++ = suncolmap[*src]; 686 *dest++ = suncolmap[*src + ncols]; 687 *dest++ = suncolmap[*src + 2*ncols]; 688 src++; 689 } 690 } 691 692 if (y >= srcY) { 693 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { 694 result = FALSE; 695 break; 696 } 697 outY++; 698 } 699 } 700 ckfree ((char *)indData); 701 return result; 702} 703 704/* Load SUN Raster file with true color image: depth = 24 or 32 */ 705 706static Boln load_rgb (Tcl_Interp *interp, tkimg_MFile *ifp, 707 Tk_PhotoHandle imageHandle, int destX, int destY, 708 int width, int height, int srcX, int srcY, 709 int fileWidth, int fileHeight, 710 int nchan, int type, int showMatte) 711{ 712 UByte *dest, tmp; 713 UByte *pixbuf; 714 Tk_PhotoImageBlock block; 715 int linepad; 716 int x, y; 717 int stopY, outY; 718 int err, rle; 719 char errMsg[200]; 720 Boln result = TRUE; 721 722 pixbuf = (UByte *) ckalloc (fileWidth * nchan); 723 if (!pixbuf) { 724 sprintf(errMsg, "Can't allocate memory of size %d", 725 fileWidth * nchan); 726 Tcl_AppendResult(interp, errMsg, (char *)NULL); 727 return TCL_ERROR; 728 } 729 730 block.pixelSize = nchan; 731 block.pitch = fileWidth * nchan; 732 block.width = width; 733 block.height = 1; 734 block.offset[0] = 0; 735 block.offset[1] = 1; 736 block.offset[2] = 2; 737 if (nchan < 4) { 738 showMatte = 0; 739 } 740 block.offset[3] = showMatte? 3: 0; 741 742 block.pixelPtr = pixbuf + srcX * nchan; 743 744 rle = (type == RAS_TYPE_RLE); 745 linepad = (fileWidth*nchan) % 2; 746 747 if (rle) 748 rle_startread (ifp); /* Initialize RLE-buffer */ 749 750 stopY = srcY + height; 751 outY = destY; 752 753 for (y=0; y<stopY; y++) { 754 dest = pixbuf; 755 memset ((char *)dest, 0, nchan*fileWidth); 756 err = ((rle ? rle_fread ((char *)dest, nchan, fileWidth, ifp) : 757 sun_fread ((char *)dest, nchan, fileWidth, ifp)) != fileWidth); 758 if (err && (y != height -1)) { 759 sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); 760 Tcl_AppendResult(interp, errMsg, (char *) NULL); 761 ckfree ((char *)pixbuf); 762 return FALSE; 763 } 764 if (linepad) { 765 err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); 766 if (err) { 767 sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); 768 Tcl_AppendResult(interp, errMsg, (char *) NULL); 769 ckfree ((char *)pixbuf); 770 return FALSE; 771 } 772 } 773 774 if (y >= srcY) { 775 dest = pixbuf + srcX * nchan; 776 if (type != 3) { 777 if (nchan == 3) { /* GBR Format. Swap to RGB. */ 778 for (x=0; x<width; x++) { 779 tmp = dest[0]; 780 dest[0] = dest[2]; 781 dest[2] = tmp; 782 783 dest += 3; 784 } 785 } else { /* AGBR Format. Swap to RGBA. */ 786 for (x=0; x<width; x++) { 787 tmp = dest[0]; 788 dest[0] = dest[3]; 789 dest[3] = tmp; 790 791 tmp = dest[1]; 792 dest[1] = dest[2]; 793 dest[2] = tmp; 794 795 dest += 4; 796 } 797 } 798 } 799 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, showMatte? TK_PHOTO_COMPOSITE_OVERLAY: TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { 800 result = FALSE; 801 break; 802 } 803 outY++; 804 } 805 } 806 ckfree ((char *)pixbuf); 807 return result; 808} 809 810/* 811 * Here is the start of the standard functions needed for every image format. 812 */ 813 814/* 815 * Prototypes for local procedures defined in this file: 816 */ 817 818static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, 819 int *comp, int *verb, int *matte); 820static int CommonMatch(tkimg_MFile *handle, int *widthPtr, 821 int *heightPtr, SUNHEADER *sunHeaderPtr); 822static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, 823 const char *filename, Tcl_Obj *format, 824 Tk_PhotoHandle imageHandle, int destX, int destY, 825 int width, int height, int srcX, int srcY); 826static int CommonWrite(Tcl_Interp *interp, 827 const char *filename, Tcl_Obj *format, 828 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr); 829 830static int ParseFormatOpts (interp, format, comp, verb, matte) 831 Tcl_Interp *interp; 832 Tcl_Obj *format; 833 int *comp; 834 int *verb; 835 int *matte; 836{ 837 static const char *const sunOptions[] = {"-compression", "-verbose", "-matte"}; 838 int objc, length, c, i, index; 839 Tcl_Obj **objv; 840 const char *compression, *verbose, *transp; 841 842 *comp = 1; 843 *verb = 0; 844 *matte = 1; 845 if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) 846 return TCL_ERROR; 847 if (objc) { 848 compression = "rle"; 849 verbose = "0"; 850 transp = "1"; 851 for (i=1; i<objc; i++) { 852 if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)sunOptions, 853 "format option", 0, &index) != TCL_OK) { 854 return TCL_ERROR; 855 } 856 if (++i >= objc) { 857 Tcl_AppendResult(interp, "No value for option \"", 858 Tcl_GetStringFromObj (objv[--i], (int *) NULL), 859 "\"", (char *) NULL); 860 return TCL_ERROR; 861 } 862 switch(index) { 863 case 0: 864 compression = Tcl_GetStringFromObj(objv[i], (int *) NULL); 865 break; 866 case 1: 867 verbose = Tcl_GetStringFromObj(objv[i], (int *) NULL); 868 break; 869 case 2: 870 transp = Tcl_GetStringFromObj(objv[i], (int *) NULL); 871 break; 872 } 873 } 874 875 c = compression[0]; length = strlen (compression); 876 if ((c == 'n') && (!strncmp (compression, "none", length))) { 877 *comp = 0; 878 } else if ((c == 'r') && (!strncmp (compression, "rle",length))) { 879 *comp = 1; 880 } else { 881 Tcl_AppendResult(interp, "invalid compression mode \"", 882 compression, "\": should be rle or none", (char *) NULL); 883 return TCL_ERROR; 884 } 885 886 c = verbose[0]; length = strlen (verbose); 887 if (!strncmp (verbose, "1", length) || \ 888 !strncmp (verbose, "true", length) || \ 889 !strncmp (verbose, "on", length)) { 890 *verb = 1; 891 } else if (!strncmp (verbose, "0", length) || \ 892 !strncmp (verbose, "false", length) || \ 893 !strncmp (verbose, "off", length)) { 894 *verb = 0; 895 } else { 896 Tcl_AppendResult(interp, "invalid verbose mode \"", verbose, 897 "\": should be 1 or 0, on or off, true or false", 898 (char *) NULL); 899 return TCL_ERROR; 900 } 901 902 c = transp[0]; length = strlen (transp); 903 if (!strncmp (transp, "1", length) || \ 904 !strncmp (transp, "true", length) || \ 905 !strncmp (transp, "on", length)) { 906 *matte = 1; 907 } else if (!strncmp (transp, "0", length) || \ 908 !strncmp (transp, "false", length) || \ 909 !strncmp (transp, "off", length)) { 910 *matte = 0; 911 } else { 912 Tcl_AppendResult(interp, "invalid alpha (matte) mode \"", verbose, 913 "\": should be 1 or 0, on or off, true or false", 914 (char *) NULL); 915 return TCL_ERROR; 916 } 917 } 918 return TCL_OK; 919} 920 921static int ChnMatch( 922 Tcl_Channel chan, 923 const char *filename, 924 Tcl_Obj *format, 925 int *widthPtr, 926 int *heightPtr, 927 Tcl_Interp *interp 928) { 929 tkimg_MFile handle; 930 931#ifdef DEBUG_LOCAL 932 printf("ChnMatch\n"); fflush(stdout); 933#endif 934 935 handle.data = (char *) chan; 936 handle.state = IMG_CHAN; 937 938 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 939} 940 941static int ObjMatch( 942 Tcl_Obj *data, 943 Tcl_Obj *format, 944 int *widthPtr, 945 int *heightPtr, 946 Tcl_Interp *interp 947) { 948 tkimg_MFile handle; 949 950#ifdef DEBUG_LOCAL 951 printf("ObjMatch\n"); fflush(stdout); 952#endif 953 954 if (!tkimg_ReadInit(data, 'Y', &handle)) { 955 return 0; 956 } 957 return CommonMatch(&handle, widthPtr, heightPtr, NULL); 958} 959 960static int CommonMatch(handle, widthPtr, heightPtr, sunHeaderPtr) 961 tkimg_MFile *handle; 962 int *widthPtr; 963 int *heightPtr; 964 SUNHEADER *sunHeaderPtr; 965{ 966 SUNHEADER sh; 967 968 if (!read_sun_header (handle, &sh)) 969 return 0; 970 971 *widthPtr = sh.ras_width; 972 *heightPtr = sh.ras_height; 973 if (sunHeaderPtr) 974 *sunHeaderPtr = sh; 975 return 1; 976} 977 978static int ChnRead(interp, chan, filename, format, imageHandle, 979 destX, destY, width, height, srcX, srcY) 980 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 981 Tcl_Channel chan; /* The image channel, open for reading. */ 982 const char *filename; /* The name of the image file. */ 983 Tcl_Obj *format; /* User-specified format object, or NULL. */ 984 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 985 int destX, destY; /* Coordinates of top-left pixel in 986 * photo image to be written to. */ 987 int width, height; /* Dimensions of block of photo image to 988 * be written to. */ 989 int srcX, srcY; /* Coordinates of top-left pixel to be used 990 * in image being read. */ 991{ 992 tkimg_MFile handle; 993 994 handle.data = (char *) chan; 995 handle.state = IMG_CHAN; 996 997 return CommonRead (interp, &handle, filename, format, 998 imageHandle, destX, destY, 999 width, height, srcX, srcY); 1000} 1001 1002static int ObjRead (interp, data, format, imageHandle, 1003 destX, destY, width, height, srcX, srcY) 1004 Tcl_Interp *interp; 1005 Tcl_Obj *data; 1006 Tcl_Obj *format; 1007 Tk_PhotoHandle imageHandle; 1008 int destX, destY; 1009 int width, height; 1010 int srcX, srcY; 1011{ 1012 tkimg_MFile handle; 1013 1014 tkimg_ReadInit (data, 'Y', &handle); 1015 return CommonRead (interp, &handle, "InlineData", format, imageHandle, 1016 destX, destY, width, height, srcX, srcY); 1017} 1018 1019static int CommonRead (interp, handle, filename, format, imageHandle, 1020 destX, destY, width, height, srcX, srcY) 1021 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 1022 tkimg_MFile *handle; /* The image file, open for reading. */ 1023 const char *filename; /* The name of the image file. */ 1024 Tcl_Obj *format; /* User-specified format object, or NULL. */ 1025 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 1026 int destX, destY; /* Coordinates of top-left pixel in 1027 * photo image to be written to. */ 1028 int width, height; /* Dimensions of block of photo image to 1029 * be written to. */ 1030 int srcX, srcY; /* Coordinates of top-left pixel to be used 1031 * in image being read. */ 1032{ 1033 int nchan; 1034 int fileWidth, fileHeight; 1035 int outWidth, outHeight; 1036 int retCode = TCL_OK; 1037 SUNHEADER sh; 1038 UByte *suncolmap = NULL; 1039 int compr, verbose, matte; 1040 char errMsg[200]; 1041 1042 if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) { 1043 return TCL_ERROR; 1044 } 1045 1046 CommonMatch(handle, &fileWidth, &fileHeight, &sh); 1047 if (verbose) 1048 printImgInfo (&sh, filename, "Reading image:"); 1049 1050 if ((srcX + width) > fileWidth) { 1051 outWidth = fileWidth - srcX; 1052 } else { 1053 outWidth = width; 1054 } 1055 if ((srcY + height) > fileHeight) { 1056 outHeight = fileHeight - srcY; 1057 } else { 1058 outHeight = height; 1059 } 1060 if ((outWidth <= 0) || (outHeight <= 0) 1061 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 1062 return TCL_OK; 1063 } 1064 1065 if (sh.ras_type > 5) { 1066 sprintf(errMsg, "Unknown Sun Raster type: %d", sh.ras_type); 1067 Tcl_AppendResult(interp, errMsg, (char *)NULL); 1068 return TCL_ERROR; 1069 } 1070 1071 if (sh.ras_type == RAS_TYPE_RLE) 1072 tkimg_ReadBuffer (1); 1073 1074 /* Is there a RGB colourmap ? */ 1075 if ((sh.ras_maptype == 1) && (sh.ras_maplength > 0)) { 1076 suncolmap = (UByte *)ckalloc (sh.ras_maplength); 1077 if (!suncolmap) { 1078 sprintf(errMsg, "Can't allocate memory of size %d", 1079 sh.ras_maplength); 1080 Tcl_AppendResult(interp, errMsg, (char *)NULL); 1081 tkimg_ReadBuffer (0); 1082 return TCL_ERROR; 1083 } 1084 1085 if (!read_sun_cols (handle, &sh, suncolmap)) { 1086 Tcl_AppendResult(interp, "Unable to read color map", (char *)NULL); 1087 ckfree ((char *)suncolmap); 1088 tkimg_ReadBuffer (0); 1089 return TCL_ERROR; 1090 } 1091#ifdef DEBUG 1092 { 1093 int j, ncols; 1094 printf("Colormap values:\n"); 1095 ncols = sh.ras_maplength/3; 1096 for (j=0; j < ncols; j++) 1097 printf("Entry 0x%08x: 0x%04x, 0x%04x, 0x%04x\n", 1098 j,suncolmap[j],suncolmap[j+ncols],suncolmap[j+2*ncols]); 1099 } 1100#endif 1101 } else if (sh.ras_maplength > 0) { 1102 UByte dummy[1]; 1103 int d, length; 1104 1105 /* This type of colourmap is not supported. Ignore it. */ 1106 length = (sizeof (SUNHEADER)/sizeof (UInt)) * 4 + sh.ras_maplength; 1107 for (d=0; d<length; d++) { 1108 (void) readUByte (handle, dummy); 1109 } 1110 } 1111 1112 if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { 1113 if (suncolmap) { 1114 ckfree ((char *)suncolmap); 1115 } 1116 tkimg_ReadBuffer(0); 1117 return TCL_ERROR; 1118 } 1119 1120 nchan = (sh.ras_depth == 32? 4: 3); 1121 1122 switch (sh.ras_depth) 1123 { 1124 case 1: /* 2 colors B/W */ 1125 if (!load_sun_d1 (interp, handle, imageHandle, destX, destY, 1126 outWidth, outHeight, srcX, srcY, 1127 fileWidth, fileHeight, sh.ras_type)) 1128 retCode = TCL_ERROR; 1129 break; 1130 1131 case 8: /* 256 colours */ 1132 if (!load_sun_d8 (interp, handle, imageHandle, destX, destY, 1133 outWidth, outHeight, srcX, srcY, 1134 fileWidth, fileHeight, sh.ras_type, 1135 suncolmap, sh.ras_maplength)) 1136 retCode = TCL_ERROR; 1137 break; 1138 1139 case 24: /* True color */ 1140 case 32: /* True color with matte channel */ 1141 if (!load_rgb (interp, handle, imageHandle, destX, destY, 1142 outWidth, outHeight, srcX, srcY, 1143 fileWidth, fileHeight, 1144 nchan, sh.ras_type, matte)) 1145 retCode = TCL_ERROR; 1146 break; 1147 1148 default: 1149 sprintf(errMsg, "Image has invalid pixel depth: %d", sh.ras_depth); 1150 Tcl_AppendResult(interp, errMsg, (char *)NULL); 1151 retCode = TCL_ERROR; 1152 break; 1153 } 1154 if (suncolmap) 1155 ckfree ((char *)suncolmap); 1156 tkimg_ReadBuffer (0); 1157 return retCode; 1158} 1159 1160static int ChnWrite (interp, filename, format, blockPtr) 1161 Tcl_Interp *interp; 1162 const char *filename; 1163 Tcl_Obj *format; 1164 Tk_PhotoImageBlock *blockPtr; 1165{ 1166 Tcl_Channel chan; 1167 tkimg_MFile handle; 1168 int result; 1169 1170 chan = tkimg_OpenFileChannel (interp, filename, 0644); 1171 if (!chan) { 1172 return TCL_ERROR; 1173 } 1174 1175 handle.data = (char *) chan; 1176 handle.state = IMG_CHAN; 1177 1178 result = CommonWrite (interp, filename, format, &handle, blockPtr); 1179 if (Tcl_Close(interp, chan) == TCL_ERROR) { 1180 return TCL_ERROR; 1181 } 1182 return result; 1183} 1184 1185static int StringWrite( 1186 Tcl_Interp *interp, 1187 Tcl_Obj *format, 1188 Tk_PhotoImageBlock *blockPtr 1189) { 1190 tkimg_MFile handle; 1191 int result; 1192 Tcl_DString data; 1193 1194 Tcl_DStringInit(&data); 1195 tkimg_WriteInit(&data, &handle); 1196 result = CommonWrite(interp, "InlineData", format, &handle, blockPtr); 1197 tkimg_Putc(IMG_DONE, &handle); 1198 1199 if (result == TCL_OK) { 1200 Tcl_DStringResult(interp, &data); 1201 } else { 1202 Tcl_DStringFree(&data); 1203 } 1204 return result; 1205} 1206 1207static int CommonWrite (interp, filename, format, handle, blockPtr) 1208 Tcl_Interp *interp; 1209 const char *filename; 1210 Tcl_Obj *format; 1211 tkimg_MFile *handle; 1212 Tk_PhotoImageBlock *blockPtr; 1213{ 1214 int x, y, nchan, nBytes, linepad; 1215 int redOffset, greenOffset, blueOffset, alphaOffset; 1216 UByte *pixelPtr, *pixRowPtr; 1217 SUNHEADER sh; 1218 UByte *row, *rowPtr; 1219 int compr, verbose, matte; /* Format options */ 1220 char errMsg[200]; 1221 1222 if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) { 1223 return TCL_ERROR; 1224 } 1225 1226 redOffset = 0; 1227 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 1228 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 1229 alphaOffset = blockPtr->offset[0]; 1230 1231 if (alphaOffset < blockPtr->offset[2]) { 1232 alphaOffset = blockPtr->offset[2]; 1233 } 1234 if (++alphaOffset < blockPtr->pixelSize) { 1235 alphaOffset -= blockPtr->offset[0]; 1236 } else { 1237 alphaOffset = 0; 1238 } 1239 1240 nchan = ((matte && alphaOffset)? 4: 3); 1241 nBytes = blockPtr->width * nchan; 1242 linepad = nBytes % 2; 1243 1244 /* Fill the Sun header struct and write the header to the channel. */ 1245 sh.ras_magic = RAS_MAGIC; 1246 sh.ras_width = blockPtr->width; 1247 sh.ras_height = blockPtr->height; 1248 sh.ras_depth = 8 * nchan; 1249 sh.ras_length = (nBytes + linepad) * blockPtr->height; 1250 sh.ras_type = (compr) ? RAS_TYPE_RLE : RAS_TYPE_STD; 1251 sh.ras_maptype = 0; /* No colourmap */ 1252 sh.ras_maplength = 0; /* Length of colourmap */ 1253 1254 write_sun_header (handle, &sh); 1255 1256 /* Now write out the image data. */ 1257 pixRowPtr = blockPtr->pixelPtr + blockPtr->offset[0]; 1258 if (!compr) { 1259 row = (UByte *) ckalloc (nBytes); 1260 if (!row) { 1261 sprintf(errMsg, "Can't allocate memory of size %d", nBytes); 1262 Tcl_AppendResult(interp, errMsg, (char *)NULL); 1263 return TCL_ERROR; 1264 } 1265 for (y=0; y<blockPtr->height; y++) { 1266 rowPtr = row; 1267 pixelPtr = pixRowPtr; 1268 for (x=0; x<blockPtr->width; x++) { 1269 if (nchan == 4) { 1270 /* Have a matte channel and write it. */ 1271 *(rowPtr++) = pixelPtr[alphaOffset]; 1272 } 1273 *(rowPtr++) = pixelPtr[blueOffset]; 1274 *(rowPtr++) = pixelPtr[greenOffset]; 1275 *(rowPtr++) = pixelPtr[redOffset]; 1276 pixelPtr += blockPtr->pixelSize; 1277 } 1278 if (nBytes != tkimg_Write(handle, (const char *)row, nBytes)) { 1279 sprintf(errMsg, "Can't write %d bytes to image file", nBytes); 1280 Tcl_AppendResult(interp, errMsg, (char *)NULL); 1281 ckfree ((char *)row); 1282 return TCL_ERROR; 1283 } 1284 for (x=0; x<linepad; x++) { 1285 writeUByte (handle, 0); 1286 } 1287 pixRowPtr += blockPtr->pitch; 1288 } 1289 ckfree ((char *)row); 1290 } else { /* RLE compression */ 1291 rle_startwrite (handle); 1292 for (y = 0; y < blockPtr->height; y++) { 1293 pixelPtr = pixRowPtr; 1294 for (x = 0; x < blockPtr->width; x++) { 1295 if (nchan == 4) { 1296 /* Have a matte channel and write it. */ 1297 rle_fputc (pixelPtr[alphaOffset], handle); 1298 } 1299 rle_fputc (pixelPtr[blueOffset], handle); 1300 rle_fputc (pixelPtr[greenOffset], handle); 1301 rle_fputc (pixelPtr[redOffset], handle); 1302 pixelPtr += blockPtr->pixelSize; 1303 } 1304 for (x=0; x<linepad; x++) { 1305 rle_fputc (0, handle); 1306 } 1307 pixRowPtr += blockPtr->pitch; 1308 } 1309 rle_endwrite (handle); 1310 } 1311 if (verbose) 1312 printImgInfo (&sh, filename, "Saving image:"); 1313 return TCL_OK; 1314} 1315