1/* STARTHEADER 2 * 3 * File : raw.c 4 * 5 * Author : Paul Obermeier (paul@poSoft.de) 6 * 7 * Date : Wed Feb 21 12:45:08 CET 2001 8 * 9 * Copyright : (C) 2001-2004 Paul Obermeier 10 * 11 * Description : 12 * 13 * A photo image handler for raw data interpreted as image files. 14 * 15 * The following image types are currently supported: 16 * 17 * Grayscale image: 1 channel of 32-bit floating point values. 18 * 1 channel of 16-bit unsigned integer values. 19 * 1 channel of 8-bit unsigned integer values. 20 * True-color image: 3 channels of 32-bit floating point values. 21 * 3 channels of 16-bit unsigned integer values. 22 * 3 channels of 8-bit unsigned integer values. 23 * 24 * List of currently supported features: 25 * 26 * Type | Read | Write | 27 * | -file | -data | -file | -data | 28 * ---------------------------------------- 29 * Gray | Yes | Yes | Yes | Yes | 30 * RGB | Yes | Yes | Yes | Yes | 31 * 32 * There are 2 supported file formats: 33 * One with the pure raw data only, the other with a 7 line ASCII header of the 34 * following form: 35 * 36 * Magic=RAW\n File format identifier. Fixed value. 37 * Width=128\n Image width in pixels. 38 * Height=128\n Image height in pixels. 39 * NumChan=1\n Possible values: 1 or 3. 40 * ByteOrder=Intel\n Possible values: "Intel" or "Motorola". 41 * ScanOrder=TopDown\n Possible values: "TopDown" or "BottomUp". 42 * PixelType=byte\n Possible values: "float", "short" or "byte". 43 * 44 * The following format options are available: 45 * 46 * For raw images with header: 47 * Read RAW image: "raw -useheader true -verbose <bool> -gamma <float> 48 * -min <float> -max <float>" 49 * Write RAW image: "raw -useheader true -verbose <bool> -nchan <int> 50 * -scanorder <string>" 51 * 52 * For raw images without header: 53 * Read RAW image: "raw -useheader false -verbose <bool> -nchan <int> 54 * -scanorder <string> -byteorder <string> 55 * -width <int> -height <int> -gamma <float> 56 * -pixeltype <string> -min <float> -max <float> 57 * -uuencode <bool>" 58 * Write RAW image: "raw -useheader false -verbose <bool> -nchan <int> 59 * -scanorder <string>" 60 * 61 * -verbose <bool>: If set to true, additional information about the file 62 * format is printed to stdout. Default is false. 63 * -useheader <bool>: If set to true, use file header information for reading 64 * and writing. Default is true. 65 * -nchan <int>: Specify the number of channels of the input image. 66 * Only valid, if reading image data without header. 67 * Default is 1. 68 * -width <int>: Specify the width of the input image. Only valid, if 69 * reading image data without header. Default is 128. 70 * -height <int>: Specify the height of the input image. Only valid, if 71 * reading image data without header. Default is 128. 72 * -byteorder <string>: Specify the byteorder of the input image. Only valid, if 73 * reading image data without header. 74 * Possible values: "Intel" or "Motorola". 75 * Default is assuming the same byteorder as that of the 76 * host computer. 77 * -scanorder <string>: Specify the scanline order of the input image. Only 78 * valid, if reading image data without header. 79 * Possible values: "TopDown" or "BottomUp". 80 * Default is "TopDown". 81 * -pixeltype <string>: Specify the type of the pixel values. 82 * Only valid, if reading image data without header. 83 * Possible values: "float", "short" or "byte". 84 * Default is "byte". 85 * -gamma <float>: Specify a gamma correction to be applied when mapping 86 * the input data to 8-bit image values. 87 * Default is 1.0. 88 * -nomap <bool>: If set to true, no mapping of input values is done. 89 * Use this option, if your image already contains RGB 90 * values in the range of 0 ..255. 91 * Default is false. 92 * -uuencode <bool>: If set to false, do not assume, that the image data stored in a 93 * variable is uuencoded. Default is true, i.e. the image data is 94 * assumed to be uuencoded. 95 * -min <float>: Specify the minimum pixel value to be used for mapping 96 * the input data to 8-bit image values. 97 * Default is the minimum value found in the image data. 98 * -max <float>: Specify the maximum pixel value to be used for mapping 99 * the input data to 8-bit image values. 100 * Default is the maximum value found in the image data. 101 * 102 * Notes: 103 * Currently RAW files are only written in "byte" pixel format. 104 * 105 * ENDHEADER 106 * 107 * $Id$ 108 * 109 */ 110 111#include <stdlib.h> 112#include <math.h> 113 114/* 115 * Generic initialization code, parameterized via CPACKAGE and PACKAGE. 116 */ 117 118#include "init.c" 119 120/* 121#define DEBUG_LOCAL 122*/ 123 124/* Maximum length of a header line. */ 125#define HEADLEN 100 126 127/* Header fields. */ 128#define strMagic "Magic=%s\n" 129#define strWidth "Width=%d\n" 130#define strHeight "Height=%d\n" 131#define strNumChan "NumChan=%d\n" 132#define strByteOrder "ByteOrder=%s\n" 133#define strScanOrder "ScanOrder=%s\n" 134#define strPixelType "PixelType=%s\n" 135 136/* Header fields possible values. */ 137#define strIntel "Intel" 138#define strMotorola "Motorola" 139#define strTopDown "TopDown" 140#define strBottomUp "BottomUp" 141#define strFloat "float" 142#define strUShort "short" 143#define strUByte "byte" 144 145#define strUnknown "Unknown" 146 147#define BOTTOM_UP 0 148#define TOP_DOWN 1 149#define INTEL 0 150#define MOTOROLA 1 151#define TYPE_FLOAT 0 152#define TYPE_USHORT 1 153#define TYPE_UBYTE 2 154 155#define MAXCHANS 4 156 157/* Some general defines and typedefs. */ 158#define TRUE 1 159#define FALSE 0 160#define MIN(a,b) ((a)<(b)? (a): (b)) 161#define MAX(a,b) ((a)>(b)? (a): (b)) 162 163typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ 164typedef unsigned char UByte; /* Unsigned 8 bit integer */ 165typedef char Byte; /* Signed 8 bit integer */ 166typedef unsigned short UShort; /* Unsigned 16 bit integer */ 167typedef short Short; /* Signed 16 bit integer */ 168typedef int UInt; /* Unsigned 32 bit integer */ 169typedef int Int; /* Signed 32 bit integer */ 170typedef float Float; /* IEEE 32 bit floating point */ 171typedef double Double; /* IEEE 64 bit floating point */ 172 173/* RAW file header structure */ 174typedef struct { 175 char id[3]; 176 Int nChans; 177 Int width; 178 Int height; 179 Int scanOrder; 180 Int byteOrder; 181 Int pixelType; 182} RAWHEADER; 183 184/* RAW file format options structure for use with ParseFormatOpts */ 185typedef struct { 186 Int width; 187 Int height; 188 Int nchan; 189 Int scanOrder; 190 Int byteOrder; 191 Int pixelType; 192 Float minVal; 193 Float maxVal; 194 Float gamma; 195 Boln nomap; 196 Boln verbose; 197 Boln uuencode; 198 Boln useHeader; 199} FMTOPT; 200 201/* Structure to hold information about the Targa file being processed. */ 202typedef struct { 203 RAWHEADER th; 204 UByte *pixbuf; 205 Float *floatBuf; 206 UShort *ushortBuf; 207 UByte *ubyteBuf; 208} RAWFILE; 209 210#define MINGAMMA 0.01 211#define MAXGAMMA 100.0 212#define GTABSIZE 257 213 214/* Given a pixel value in Float format, "valIn", and a gamma-correction 215 * lookup table, "tab", macro "gcorrectFloat" returns the gamma-corrected 216 * pixel value in "valOut". 217 */ 218 219#define gcorrectFloat(valIn,tab,valOut) \ 220 { \ 221 Int gc_i; \ 222 Float gc_t; \ 223 gc_t = (valIn) * (GTABSIZE - 2); \ 224 gc_i = gc_t; \ 225 gc_t -= gc_i; \ 226 (valOut) = (tab)[gc_i] * (1.0-gc_t) + (tab)[gc_i+1] * gc_t; \ 227 } 228 229static Boln gtableFloat (Float gamma, Float table[]) 230{ 231 Int i; 232 233 if (gamma < MINGAMMA || gamma > MAXGAMMA) { 234 printf ("Invalid gamma value %f\n", gamma); 235 return FALSE; 236 } 237 for (i = 0; i < GTABSIZE - 1; ++i) { 238 table[i] = pow ((Float) i / (Float) (GTABSIZE - 2), 1.0 / gamma); 239 } 240 table[GTABSIZE - 1] = 1.0; 241 return TRUE; 242} 243 244/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for 245 * parameter gtable. 246 */ 247static void FloatGammaUByte (Int n, const Float floatIn[], 248 const Float gtable[], UByte ubOut[]) 249{ 250 const Float *src, *stop; 251 Float ftmp; 252 Int itmp; 253 UByte *ubdest; 254 255 src = floatIn; 256 stop = floatIn + n; 257 ubdest = ubOut; 258 259 /* Handle a gamma value of 1.0 (gtable == NULL) as a special case. 260 Quite nice speed improvement for the maybe most used case. */ 261 if (gtable) { 262 while (src < stop) { 263 ftmp = MAX (0.0, MIN (*src, 1.0)); 264 gcorrectFloat (ftmp, gtable, ftmp); 265 itmp = (Int)(ftmp * 255.0 + 0.5); 266 *ubdest = MAX (0, MIN (itmp, 255)); 267 ++ubdest; 268 ++src; 269 } 270 } else { 271 while (src < stop) { 272 itmp = (Int)(*src * 255.0 + 0.5); 273 *ubdest = MAX (0, MIN (itmp, 255)); 274 ++ubdest; 275 ++src; 276 } 277 } 278 return; 279} 280 281/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for 282 * parameter gtable. 283 */ 284static void UShortGammaUByte (Int n, const UShort shortIn[], 285 const Float gtable[], UByte ubOut[]) 286{ 287 const UShort *src, *stop; 288 Float ftmp; 289 Int itmp; 290 UByte *ubdest; 291 292 src = shortIn; 293 stop = shortIn + n; 294 ubdest = ubOut; 295 296 /* Handle a gamma value of 1.0 (gtable == NULL) as a special case. 297 Quite nice speed improvement for the maybe most used case. */ 298 if (gtable) { 299 while (src < stop) { 300 ftmp = *src / 65535.0; 301 ftmp = MAX (0.0, MIN (ftmp, 1.0)); 302 gcorrectFloat (ftmp, gtable, ftmp); 303 itmp = (Int)(ftmp * 255.0 + 0.5); 304 *ubdest = MAX (0, MIN (itmp, 255)); 305 ++ubdest; 306 ++src; 307 } 308 } else { 309 while (src < stop) { 310 itmp = (Int)(*src / 256); 311 *ubdest = MAX (0, MIN (itmp, 255)); 312 ++ubdest; 313 ++src; 314 } 315 } 316 return; 317} 318 319/* This function determines at runtime, whether we are on an Intel system. */ 320 321static int isIntel (void) 322{ 323 unsigned long val = 513; 324 /* On Intel (little-endian) systems this value is equal to "\01\02\00\00". 325 On big-endian systems this value equals "\00\00\02\01" */ 326 return memcmp(&val, "\01\02", 2) == 0; 327} 328 329static void rawClose (RAWFILE *tf) 330{ 331 if (tf->pixbuf) ckfree ((char *)tf->pixbuf); 332 if (tf->floatBuf) ckfree ((char *)tf->floatBuf); 333 if (tf->ushortBuf) ckfree ((char *)tf->ushortBuf); 334 if (tf->ubyteBuf) ckfree ((char *)tf->ubyteBuf); 335 return; 336} 337 338static Boln readFloatRow (tkimg_MFile *handle, Float *pixels, Int nFloats, 339 char *buf, Boln swapBytes) 340{ 341 int i; 342 Float *mPtr = pixels; 343 char *bufPtr = buf; 344 345#ifdef DEBUG_LOCAL 346 printf ("Reading %d floats\n", nFloats); fflush (stdout); 347#endif 348 if (4 * nFloats != tkimg_Read (handle, buf, 4 * nFloats)) 349 return FALSE; 350 351 if (swapBytes) { 352 for (i=0; i<nFloats; i++) { 353 ((char *)mPtr)[0] = bufPtr[3]; 354 ((char *)mPtr)[1] = bufPtr[2]; 355 ((char *)mPtr)[2] = bufPtr[1]; 356 ((char *)mPtr)[3] = bufPtr[0]; 357 mPtr++; 358 bufPtr += 4; 359 } 360 } else { 361 for (i=0; i<nFloats; i++) { 362 ((char *)mPtr)[0] = bufPtr[0]; 363 ((char *)mPtr)[1] = bufPtr[1]; 364 ((char *)mPtr)[2] = bufPtr[2]; 365 ((char *)mPtr)[3] = bufPtr[3]; 366 mPtr++; 367 bufPtr += 4; 368 } 369 } 370 return TRUE; 371} 372 373static Boln readUShortRow (tkimg_MFile *handle, UShort *pixels, Int nShorts, 374 char *buf, Boln swapBytes) 375{ 376 int i; 377 UShort *mPtr = pixels; 378 char *bufPtr = buf; 379 380#ifdef DEBUG_LOCAL 381 printf ("Reading %d UShorts\n", nShorts); fflush (stdout); 382#endif 383 if (2 * nShorts != tkimg_Read (handle, buf, 2 * nShorts)) 384 return FALSE; 385 386 if (swapBytes) { 387 for (i=0; i<nShorts; i++) { 388 ((char *)mPtr)[0] = bufPtr[1]; 389 ((char *)mPtr)[1] = bufPtr[0]; 390 mPtr++; 391 bufPtr += 2; 392 } 393 } else { 394 for (i=0; i<nShorts; i++) { 395 ((char *)mPtr)[0] = bufPtr[0]; 396 ((char *)mPtr)[1] = bufPtr[1]; 397 mPtr++; 398 bufPtr += 2; 399 } 400 } 401 return TRUE; 402} 403 404static Boln readUByteRow (tkimg_MFile *handle, UByte *pixels, Int nBytes, 405 char *buf, Boln swapBytes) 406{ 407 int i; 408 UByte *mPtr = pixels; 409 char *bufPtr = buf; 410 411#ifdef DEBUG_LOCAL 412 printf ("Reading %d UBytes\n", nBytes); fflush (stdout); 413#endif 414 if (nBytes != tkimg_Read (handle, buf, nBytes)) 415 return FALSE; 416 417 for (i=0; i<nBytes; i++) { 418 ((char *)mPtr)[0] = bufPtr[0]; 419 mPtr++; 420 bufPtr += 1; 421 } 422 return TRUE; 423} 424 425#define OUT Tcl_WriteChars (outChan, str, -1) 426static void printImgInfo (RAWHEADER *th, FMTOPT *opts, 427 const char *filename, const char *msg) 428{ 429 Tcl_Channel outChan; 430 char str[256]; 431 432 outChan = Tcl_GetStdChannel (TCL_STDOUT); 433 if (!outChan) { 434 return; 435 } 436 sprintf (str, "%s %s\n", msg, filename); OUT; 437 sprintf (str, "\tSize in pixel : %d x %d\n", th->width, th->height); OUT; 438 sprintf (str, "\tNo. of channels : %d\n", th->nChans); OUT; 439 sprintf (str, "\tPixel type : %s\n", (th->pixelType == TYPE_FLOAT? strFloat: 440 (th->pixelType == TYPE_USHORT? strUShort: 441 (th->pixelType == TYPE_UBYTE? strUByte: 442 strUnknown)))); OUT; 443 sprintf (str, "\tVertical encoding: %s\n", th->scanOrder == TOP_DOWN? 444 strTopDown: strBottomUp); OUT; 445 sprintf (str, "\tGamma correction : %f\n", opts->gamma); OUT; 446 sprintf (str, "\tMinimum map value: %f\n", opts->minVal); OUT; 447 sprintf (str, "\tMaximum map value: %f\n", opts->maxVal); OUT; 448 sprintf (str, "\tHost byte order : %s\n", isIntel ()? strIntel: strMotorola); OUT; 449 sprintf (str, "\tFile byte order : %s\n", th->byteOrder == INTEL? 450 strIntel: strMotorola); OUT; 451 Tcl_Flush (outChan); 452} 453#undef OUT 454 455static Boln readHeaderLine (Tcl_Interp *interp, tkimg_MFile *handle, char *buf) 456{ 457 char c, *bufPtr, *bufEndPtr; 458 Boln failure; 459 460 bufPtr = buf; 461 bufEndPtr = buf + HEADLEN; 462 failure = TRUE; 463 464#ifdef DEBUG_LOCAL 465 printf ("readHeaderLine\n"); fflush (stdout); 466#endif 467 468 while (tkimg_Read (handle, &c, 1) == 1 && bufPtr < bufEndPtr) { 469 if (c == '\n') { 470 *bufPtr = '\0'; 471 failure = FALSE; 472 break; 473 } 474 *bufPtr = c; 475 bufPtr++; 476 } 477 if (failure) { 478 Tcl_AppendResult (interp, "RAW handler: Error reading header line (", 479 buf, ")\n", NULL); 480 return FALSE; 481 } 482 return TRUE; 483} 484 485static Boln readHeader (Tcl_Interp *interp, tkimg_MFile *handle, RAWHEADER *th) 486{ 487 char buf[HEADLEN]; 488 char tmpStr[HEADLEN]; 489 490 if (!readHeaderLine (interp, handle, buf) || 491 (1 != sscanf (buf, strMagic, th->id))) { 492 Tcl_AppendResult (interp, "Unable to parse header field Magic\n", NULL); 493 return FALSE; 494 } 495 if (strcmp (th->id, "RAW") != 0) { 496 Tcl_AppendResult (interp, "Invalid value for header field Magic:", 497 "Must be \"RAW\"\n", NULL); 498 return FALSE; 499 } 500 501 if (!readHeaderLine (interp, handle, buf) || 502 (1 != sscanf (buf, strWidth, &th->width))) { 503 Tcl_AppendResult (interp, "Unable to parse header field Width\n", NULL); 504 return FALSE; 505 } 506 if (th->width < 1) { 507 Tcl_AppendResult (interp, "Invalid value for header field Width:", 508 "Must be greater than zero\n", NULL); 509 return FALSE; 510 } 511 512 if (!readHeaderLine (interp, handle, buf) || 513 (1 != sscanf (buf, strHeight, &th->height))) { 514 Tcl_AppendResult (interp, "Unable to parse header field Height\n", NULL); 515 return FALSE; 516 } 517 if (th->height < 1) { 518 Tcl_AppendResult (interp, "Invalid value for header field Height:", 519 "Must be greater than zero\n", NULL); 520 return FALSE; 521 } 522 523 if (!readHeaderLine (interp, handle, buf) || 524 (1 != sscanf (buf, strNumChan, &th->nChans))) { 525 Tcl_AppendResult (interp, "Unable to parse header field NumChan\n", NULL); 526 return FALSE; 527 } 528 if (th->nChans != 1 && th->nChans != 3) { 529 Tcl_AppendResult (interp, "Invalid value for header field NumChan:", 530 "Must be 1 or 3\n", NULL); 531 return FALSE; 532 } 533 534 if (!readHeaderLine (interp, handle, buf) || 535 (1 != sscanf (buf, strByteOrder, tmpStr))) { 536 Tcl_AppendResult (interp, "Unable to parse header field ByteOrder\n", NULL); 537 return FALSE; 538 } 539 540 if (strcmp (tmpStr, strIntel) == 0) { 541 th->byteOrder = INTEL; 542 } else if (strcmp (tmpStr, strMotorola) == 0) { 543 th->byteOrder = MOTOROLA; 544 } else { 545 Tcl_AppendResult (interp, "Invalid value for header field ByteOrder:", 546 "Must be ", strIntel, " or ", strMotorola, 547 "\n", NULL); 548 return FALSE; 549 } 550 551 if (!readHeaderLine (interp, handle, buf) || 552 (1 != sscanf (buf, strScanOrder, tmpStr))) { 553 Tcl_AppendResult (interp, "Unable to parse header field ScanOrder\n", NULL); 554 return FALSE; 555 } 556 if (strcmp (tmpStr, strTopDown) == 0) { 557 th->scanOrder = TOP_DOWN; 558 } else if (strcmp (tmpStr, strBottomUp) == 0) { 559 th->scanOrder = BOTTOM_UP; 560 } else { 561 Tcl_AppendResult (interp, "Invalid value for header field ScanOrder:", 562 "Must be ", strTopDown, " or ", strBottomUp, 563 "\n", NULL); 564 return FALSE; 565 } 566 567 if (!readHeaderLine (interp, handle, buf) || 568 (1 != sscanf (buf, strPixelType, tmpStr))) { 569 Tcl_AppendResult (interp, "Unable to parse header field PixelType\n", NULL); 570 return FALSE; 571 } 572 if (strcmp (tmpStr, strFloat) == 0) { 573 th->pixelType = TYPE_FLOAT; 574 } else if (strcmp (tmpStr, strUShort) == 0) { 575 th->pixelType = TYPE_USHORT; 576 } else if (strcmp (tmpStr, strUByte) == 0) { 577 th->pixelType = TYPE_UBYTE; 578 } else { 579 Tcl_AppendResult (interp, "Invalid value for header field PixelType:", 580 "Must be ", strFloat, ",", strUShort, " or ", strUByte, 581 "\n", NULL); 582 return FALSE; 583 } 584 585 return TRUE; 586} 587 588static Boln writeHeader (tkimg_MFile *handle, RAWHEADER *th) 589{ 590 char buf[1024]; 591 592 sprintf (buf, strMagic, "RAW"); 593 tkimg_Write (handle, buf, strlen (buf)); 594 sprintf (buf, strWidth, th->width); 595 tkimg_Write (handle, buf, strlen (buf)); 596 sprintf (buf, strHeight, th->height); 597 tkimg_Write (handle, buf, strlen (buf)); 598 sprintf (buf, strNumChan, th->nChans); 599 tkimg_Write (handle, buf, strlen (buf)); 600 sprintf (buf, strByteOrder, isIntel()? strIntel: strMotorola); 601 tkimg_Write (handle, buf, strlen (buf)); 602 sprintf (buf, strScanOrder, th->scanOrder == TOP_DOWN? 603 strTopDown: strBottomUp); 604 tkimg_Write (handle, buf, strlen (buf)); 605 sprintf (buf, strPixelType, (th->pixelType == TYPE_FLOAT? strFloat: 606 (th->pixelType == TYPE_USHORT? strUShort: 607 (th->pixelType == TYPE_UBYTE? strUByte: 608 strUnknown)))); 609 tkimg_Write (handle, buf, strlen (buf)); 610 return TRUE; 611} 612 613static void initHeader (RAWHEADER *th) 614{ 615 th->id[0] = 'R'; 616 th->id[1] = 'A'; 617 th->id[2] = 'W'; 618 th->nChans = 1; 619 th->width = 128; 620 th->height = 128; 621 th->scanOrder = TOP_DOWN; 622 th->byteOrder = INTEL; 623 th->pixelType = TYPE_UBYTE; 624 return; 625} 626 627static Boln readFloatFile (tkimg_MFile *handle, Float *buf, Int width, Int height, 628 Int nchan, Boln swapBytes, Boln verbose, 629 Float minVals[], Float maxVals[]) 630{ 631 Int x, y, c; 632 Float *bufPtr = buf; 633 char *line; 634 635#ifdef DEBUG_LOCAL 636 printf ("readFloatFile: Width=%d Height=%d nchan=%d swapBytes=%s\n", 637 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout); 638#endif 639 for (c=0; c<nchan; c++) { 640 minVals[c] = (Float)1.0E30; 641 maxVals[c] = (Float)-1.0E30; 642 } 643 line = ckalloc (sizeof (Float) * nchan * width); 644 645 for (y=0; y<height; y++) { 646 if (!readFloatRow (handle, bufPtr, nchan * width, line, swapBytes)) 647 return FALSE; 648 for (x=0; x<width; x++) { 649 for (c=0; c<nchan; c++) { 650 if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr; 651 if (*bufPtr < minVals[c]) minVals[c] = *bufPtr; 652 bufPtr++; 653 } 654 } 655 } 656 if (verbose) { 657 printf ("\tMinimum pixel values :"); 658 for (c=0; c<nchan; c++) { 659 printf (" %f", minVals[c]); 660 } 661 printf ("\n"); 662 printf ("\tMaximum pixel values :"); 663 for (c=0; c<nchan; c++) { 664 printf (" %f", maxVals[c]); 665 } 666 printf ("\n"); 667 fflush (stdout); 668 } 669 ckfree (line); 670 return TRUE; 671} 672 673static Boln readUShortFile (tkimg_MFile *handle, UShort *buf, Int width, Int height, 674 Int nchan, Boln swapBytes, Boln verbose, 675 Float minVals[], Float maxVals[]) 676{ 677 Int x, y, c; 678 UShort *bufPtr = buf; 679 char *line; 680 681#ifdef DEBUG_LOCAL 682 printf ("readUShortFile: Width=%d Height=%d nchan=%d swapBytes=%s\n", 683 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout); 684#endif 685 for (c=0; c<nchan; c++) { 686 minVals[c] = (Float)1.0E30; 687 maxVals[c] = (Float)-1.0E30; 688 } 689 line = ckalloc (sizeof (UShort) * nchan * width); 690 691 for (y=0; y<height; y++) { 692 if (!readUShortRow (handle, bufPtr, nchan * width, line, swapBytes)) 693 return FALSE; 694 for (x=0; x<width; x++) { 695 for (c=0; c<nchan; c++) { 696 if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr; 697 if (*bufPtr < minVals[c]) minVals[c] = *bufPtr; 698 bufPtr++; 699 } 700 } 701 } 702 if (verbose) { 703 printf ("\tMinimum pixel values :"); 704 for (c=0; c<nchan; c++) { 705 printf (" %d", (UShort)minVals[c]); 706 } 707 printf ("\n"); 708 printf ("\tMaximum pixel values :"); 709 for (c=0; c<nchan; c++) { 710 printf (" %d", (UShort)maxVals[c]); 711 } 712 printf ("\n"); 713 fflush (stdout); 714 } 715 ckfree (line); 716 return TRUE; 717} 718 719static Boln readUByteFile (tkimg_MFile *handle, UByte *buf, Int width, Int height, 720 Int nchan, Boln swapBytes, Boln verbose, 721 Float minVals[], Float maxVals[]) 722{ 723 Int x, y, c; 724 UByte *bufPtr = buf; 725 char *line; 726 727#ifdef DEBUG_LOCAL 728 printf ("readUByteFile: Width=%d Height=%d nchan=%d swapBytes=%s\n", 729 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout); 730#endif 731 for (c=0; c<nchan; c++) { 732 minVals[c] = (Float)1.0E30; 733 maxVals[c] = (Float)-1.0E30; 734 } 735 line = ckalloc (sizeof (UByte) * nchan * width); 736 737 for (y=0; y<height; y++) { 738 if (!readUByteRow (handle, bufPtr, nchan * width, line, swapBytes)) 739 return FALSE; 740 for (x=0; x<width; x++) { 741 for (c=0; c<nchan; c++) { 742 if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr; 743 if (*bufPtr < minVals[c]) minVals[c] = *bufPtr; 744 bufPtr++; 745 } 746 } 747 } 748 if (verbose) { 749 printf ("\tMinimum pixel values :"); 750 for (c=0; c<nchan; c++) { 751 printf (" %d", (UByte)minVals[c]); 752 } 753 printf ("\n"); 754 printf ("\tMaximum pixel values :"); 755 for (c=0; c<nchan; c++) { 756 printf (" %d", (UByte)maxVals[c]); 757 } 758 printf ("\n"); 759 fflush (stdout); 760 } 761 ckfree (line); 762 return TRUE; 763} 764 765static Boln remapFloatValues (Float *buf, Int width, Int height, Int nchan, 766 Float minVals[], Float maxVals[]) 767{ 768 Int x, y, c; 769 Float *bufPtr = buf; 770 Float m[MAXCHANS], t[MAXCHANS]; 771 772 for (c=0; c<nchan; c++) { 773 m[c] = (1.0 - 0.0) / (maxVals[c] - minVals[c]); 774 t[c] = 0.0 - m[c] * minVals[c]; 775 } 776 for (y=0; y<height; y++) { 777 for (x=0; x<width; x++) { 778 for (c=0; c<nchan; c++) { 779 *bufPtr = *bufPtr * m[c] + t[c]; 780 if (*bufPtr < 0.0) *bufPtr = 0.0; 781 if (*bufPtr > 1.0) *bufPtr = 1.0; 782 bufPtr++; 783 } 784 } 785 } 786 return TRUE; 787} 788 789static Boln remapUShortValues (UShort *buf, Int width, Int height, Int nchan, 790 Float minVals[], Float maxVals[]) 791{ 792 Int x, y, c; 793 UShort *bufPtr = buf; 794 Float m[MAXCHANS], t[MAXCHANS]; 795 796 for (c=0; c<nchan; c++) { 797 m[c] = (65535.0 - 0.0) / (maxVals[c] - minVals[c]); 798 t[c] = 0.0 - m[c] * minVals[c]; 799 } 800 for (y=0; y<height; y++) { 801 for (x=0; x<width; x++) { 802 for (c=0; c<nchan; c++) { 803 *bufPtr = *bufPtr * m[c] + t[c]; 804 bufPtr++; 805 } 806 } 807 } 808 return TRUE; 809} 810 811/* 812 * Here is the start of the standard functions needed for every image format. 813 */ 814 815/* 816 * Prototypes for local procedures defined in this file: 817 */ 818 819static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, 820 FMTOPT *opts); 821static int CommonMatch(Tcl_Interp *interp, tkimg_MFile *handle, 822 Tcl_Obj *format, int *widthPtr, int *heightPtr, 823 RAWHEADER *rawHeaderPtr); 824static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, 825 const char *filename, Tcl_Obj *format, 826 Tk_PhotoHandle imageHandle, int destX, int destY, 827 int width, int height, int srcX, int srcY); 828static int CommonWrite(Tcl_Interp *interp, 829 const char *filename, Tcl_Obj *format, 830 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr); 831 832static int ParseFormatOpts (interp, format, opts) 833 Tcl_Interp *interp; 834 Tcl_Obj *format; 835 FMTOPT *opts; 836{ 837 static const char *const rawOptions[] = { 838 "-verbose", "-width", "-height", "-nchan", "-byteorder", 839 "-scanorder", "-pixeltype", "-min", "-max", "-gamma", 840 "-useheader", "-nomap", "-uuencode" 841 }; 842 int objc, length, c, i, index; 843 Tcl_Obj **objv; 844 const char *widthStr, *heightStr, *nchanStr, *verboseStr, *useheaderStr, 845 *byteOrderStr, *scanorderStr, *pixelTypeStr, 846 *minStr, *maxStr, *gammaStr, *nomapStr, *uuencodeStr; 847 848 /* Initialize format options with default values. */ 849 verboseStr = "0"; 850 byteOrderStr = isIntel ()? strIntel: strMotorola; 851 widthStr = "128"; 852 heightStr = "128"; 853 nchanStr = "1"; 854 scanorderStr = strTopDown; 855 pixelTypeStr = strUByte; 856 minStr = "0.0"; 857 maxStr = "0.0"; 858 gammaStr = "1.0"; 859 useheaderStr = "1"; 860 nomapStr = "0"; 861 uuencodeStr = "1"; 862 863 if (tkimg_ListObjGetElements (interp, format, &objc, &objv) != TCL_OK) 864 return TCL_ERROR; 865 if (objc) { 866 for (i=1; i<objc; i++) { 867 if (Tcl_GetIndexFromObj (interp, objv[i], (CONST84 char *CONST86 *)rawOptions, 868 "format option", 0, &index) != TCL_OK) { 869 return TCL_ERROR; 870 } 871 if (++i >= objc) { 872 Tcl_AppendResult (interp, "No value for option \"", 873 Tcl_GetStringFromObj (objv[--i], (int *) NULL), 874 "\"", (char *) NULL); 875 return TCL_ERROR; 876 } 877 switch(index) { 878 case 0: 879 verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 880 break; 881 case 1: 882 widthStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 883 break; 884 case 2: 885 heightStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 886 break; 887 case 3: 888 nchanStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 889 break; 890 case 4: 891 byteOrderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 892 break; 893 case 5: 894 scanorderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 895 break; 896 case 6: 897 pixelTypeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 898 break; 899 case 7: 900 minStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 901 break; 902 case 8: 903 maxStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 904 break; 905 case 9: 906 gammaStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 907 break; 908 case 10: 909 useheaderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 910 break; 911 case 11: 912 nomapStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 913 break; 914 case 12: 915 uuencodeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 916 break; 917 } 918 } 919 } 920 921 /* OPA TODO: Check for valid integer and float strings. */ 922 opts->width = atoi (widthStr); 923 opts->height = atoi (heightStr); 924 opts->nchan = atoi (nchanStr); 925 926 opts->minVal = atof (minStr); 927 opts->maxVal = atof (maxStr); 928 opts->gamma = atof (gammaStr); 929 930 c = byteOrderStr[0]; length = strlen (byteOrderStr); 931 if (!strncmp (byteOrderStr, strIntel, length)) { 932 opts->byteOrder = INTEL; 933 } else if (!strncmp (byteOrderStr, strMotorola, length)) { 934 opts->byteOrder = MOTOROLA; 935 } else { 936 Tcl_AppendResult (interp, "Invalid byteorder mode \"", byteOrderStr, 937 "\": Should be ", strIntel, " or ", strMotorola, 938 (char *) NULL); 939 return TCL_ERROR; 940 } 941 942 c = verboseStr[0]; length = strlen (verboseStr); 943 if (!strncmp (verboseStr, "1", length) || \ 944 !strncmp (verboseStr, "true", length) || \ 945 !strncmp (verboseStr, "on", length)) { 946 opts->verbose = 1; 947 } else if (!strncmp (verboseStr, "0", length) || \ 948 !strncmp (verboseStr, "false", length) || \ 949 !strncmp (verboseStr, "off", length)) { 950 opts->verbose = 0; 951 } else { 952 Tcl_AppendResult (interp, "invalid verbose mode \"", verboseStr, 953 "\": should be 1 or 0, on or off, true or false", 954 (char *) NULL); 955 return TCL_ERROR; 956 } 957 958 c = useheaderStr[0]; length = strlen (useheaderStr); 959 if (!strncmp (useheaderStr, "1", length) || \ 960 !strncmp (useheaderStr, "true", length) || \ 961 !strncmp (useheaderStr, "on", length)) { 962 opts->useHeader = 1; 963 } else if (!strncmp (useheaderStr, "0", length) || \ 964 !strncmp (useheaderStr, "false", length) || \ 965 !strncmp (useheaderStr, "off", length)) { 966 opts->useHeader = 0; 967 } else { 968 Tcl_AppendResult (interp, "invalid useheader mode \"", useheaderStr, 969 "\": should be 1 or 0, on or off, true or false", 970 (char *) NULL); 971 return TCL_ERROR; 972 } 973 974 c = nomapStr[0]; length = strlen (nomapStr); 975 if (!strncmp (nomapStr, "1", length) || \ 976 !strncmp (nomapStr, "true", length) || \ 977 !strncmp (nomapStr, "on", length)) { 978 opts->nomap = 1; 979 } else if (!strncmp (nomapStr, "0", length) || \ 980 !strncmp (nomapStr, "false", length) || \ 981 !strncmp (nomapStr, "off", length)) { 982 opts->nomap = 0; 983 } else { 984 Tcl_AppendResult (interp, "invalid nomap mode \"", nomapStr, 985 "\": should be 1 or 0, on or off, true or false", 986 (char *) NULL); 987 return TCL_ERROR; 988 } 989 990 c = scanorderStr[0]; length = strlen (scanorderStr); 991 if (!strncmp (scanorderStr, strTopDown, length)) { 992 opts->scanOrder = TOP_DOWN; 993 } else if (!strncmp (scanorderStr, strBottomUp, length)) { 994 opts->scanOrder = BOTTOM_UP; 995 } else { 996 Tcl_AppendResult (interp, "invalid scanline order \"", scanorderStr, 997 "\": should be TopDown or BottomUp", 998 (char *) NULL); 999 return TCL_ERROR; 1000 } 1001 1002 c = pixelTypeStr[0]; length = strlen (pixelTypeStr); 1003 if (!strncmp (pixelTypeStr, strFloat, length)) { 1004 opts->pixelType = TYPE_FLOAT; 1005 } else if (!strncmp (pixelTypeStr, strUShort, length)) { 1006 opts->pixelType = TYPE_USHORT; 1007 } else if (!strncmp (pixelTypeStr, strUByte, length)) { 1008 opts->pixelType = TYPE_UBYTE; 1009 } else { 1010 Tcl_AppendResult (interp, "invalid pixel type \"", pixelTypeStr, 1011 "\": should be float, short or byte", 1012 (char *) NULL); 1013 return TCL_ERROR; 1014 } 1015 1016 c = uuencodeStr[0]; length = strlen (uuencodeStr); 1017 if (!strncmp (uuencodeStr, "1", length) || \ 1018 !strncmp (uuencodeStr, "true", length) || \ 1019 !strncmp (uuencodeStr, "on", length)) { 1020 opts->uuencode = 1; 1021 } else if (!strncmp (uuencodeStr, "0", length) || \ 1022 !strncmp (uuencodeStr, "false", length) || \ 1023 !strncmp (uuencodeStr, "off", length)) { 1024 opts->uuencode = 0; 1025 } else { 1026 Tcl_AppendResult (interp, "invalid uuencode mode \"", uuencodeStr, 1027 "\": should be 1 or 0, on or off, true or false", 1028 (char *) NULL); 1029 return TCL_ERROR; 1030 } 1031 return TCL_OK; 1032} 1033 1034static int ChnMatch( 1035 Tcl_Channel chan, 1036 const char *filename, 1037 Tcl_Obj *format, 1038 int *widthPtr, 1039 int *heightPtr, 1040 Tcl_Interp *interp 1041) { 1042 tkimg_MFile handle; 1043 1044 handle.data = (char *) chan; 1045 handle.state = IMG_CHAN; 1046 1047 return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL); 1048} 1049 1050static int ObjMatch( 1051 Tcl_Obj *data, 1052 Tcl_Obj *format, 1053 int *widthPtr, 1054 int *heightPtr, 1055 Tcl_Interp *interp 1056) { 1057 tkimg_MFile handle; 1058 FMTOPT opts; 1059 1060 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 1061 return TCL_ERROR; 1062 } 1063 if (!opts.uuencode) { 1064 handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length); 1065 handle.state = IMG_STRING; 1066 } else { 1067 tkimg_ReadInit(data, 'M', &handle); 1068 } 1069 return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL); 1070} 1071 1072static int CommonMatch (interp, handle, format, widthPtr, heightPtr, rawHeaderPtr) 1073 Tcl_Interp *interp; 1074 tkimg_MFile *handle; 1075 Tcl_Obj *format; 1076 int *widthPtr; 1077 int *heightPtr; 1078 RAWHEADER *rawHeaderPtr; 1079{ 1080 RAWHEADER th; 1081 FMTOPT opts; 1082 1083 initHeader (&th); 1084 1085 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 1086 return TCL_ERROR; 1087 } 1088 if (opts.useHeader) { 1089 if (!readHeader (interp, handle, &th)) 1090 return 0; 1091 } else { 1092 th.width = opts.width; 1093 th.height = opts.height; 1094 th.nChans = opts.nchan; 1095 th.pixelType = opts.pixelType; 1096 th.scanOrder = opts.scanOrder; 1097 th.byteOrder = opts.byteOrder; 1098 } 1099 *widthPtr = th.width; 1100 *heightPtr = th.height; 1101 if (rawHeaderPtr) { 1102 *rawHeaderPtr = th; 1103 } 1104 return 1; 1105} 1106 1107static int ChnRead (interp, chan, filename, format, imageHandle, 1108 destX, destY, width, height, srcX, srcY) 1109 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 1110 Tcl_Channel chan; /* The image channel, open for reading. */ 1111 const char *filename; /* The name of the image file. */ 1112 Tcl_Obj *format; /* User-specified format object, or NULL. */ 1113 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 1114 int destX, destY; /* Coordinates of top-left pixel in 1115 * photo image to be written to. */ 1116 int width, height; /* Dimensions of block of photo image to 1117 * be written to. */ 1118 int srcX, srcY; /* Coordinates of top-left pixel to be used 1119 * in image being read. */ 1120{ 1121 tkimg_MFile handle; 1122 1123 handle.data = (char *) chan; 1124 handle.state = IMG_CHAN; 1125 1126 return CommonRead (interp, &handle, filename, format, imageHandle, 1127 destX, destY, width, height, srcX, srcY); 1128} 1129 1130static int ObjRead (interp, data, format, imageHandle, 1131 destX, destY, width, height, srcX, srcY) 1132 Tcl_Interp *interp; 1133 Tcl_Obj *data; 1134 Tcl_Obj *format; 1135 Tk_PhotoHandle imageHandle; 1136 int destX, destY; 1137 int width, height; 1138 int srcX, srcY; 1139{ 1140 tkimg_MFile handle; 1141 FMTOPT opts; 1142 1143 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 1144 return TCL_ERROR; 1145 } 1146 if (!opts.uuencode) { 1147 handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length); 1148 handle.state = IMG_STRING; 1149 } else { 1150 tkimg_ReadInit(data, 'M', &handle); 1151 } 1152 1153 return CommonRead (interp, &handle, "InlineData", format, imageHandle, 1154 destX, destY, width, height, srcX, srcY); 1155} 1156 1157static int CommonRead (interp, handle, filename, format, imageHandle, 1158 destX, destY, width, height, srcX, srcY) 1159 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 1160 tkimg_MFile *handle; /* The image file, open for reading. */ 1161 const char *filename; /* The name of the image file. */ 1162 Tcl_Obj *format; /* User-specified format object, or NULL. */ 1163 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 1164 int destX, destY; /* Coordinates of top-left pixel in 1165 * photo image to be written to. */ 1166 int width, height; /* Dimensions of block of photo image to 1167 * be written to. */ 1168 int srcX, srcY; /* Coordinates of top-left pixel to be used 1169 * in image being read. */ 1170{ 1171 Tk_PhotoImageBlock block; 1172 Int x, y, c; 1173 Int fileWidth, fileHeight; 1174 Float minVals[MAXCHANS], maxVals[MAXCHANS]; 1175 int stopY, outY, outWidth, outHeight; 1176 RAWFILE tf; 1177 FMTOPT opts; 1178 Boln swapBytes; 1179 Int byteOrder; 1180 Int scanOrder; 1181 Int pixelType; 1182 Int matte = 0; 1183 UByte *pixbufPtr; 1184 Float *floatBufPtr; 1185 UShort *ushortBufPtr; 1186 UByte *ubyteBufPtr; 1187 Float gtable[GTABSIZE]; 1188 int result = TCL_OK; 1189 1190 memset (&tf, 0, sizeof (RAWFILE)); 1191 initHeader (&tf.th); 1192 1193 CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th); 1194 1195 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 1196 return TCL_ERROR; 1197 } 1198 1199 gtableFloat (opts.gamma, gtable); 1200 1201 if (opts.verbose) 1202 printImgInfo (&tf.th, &opts, filename, "Reading image:"); 1203 1204 if ((srcX + width) > fileWidth) { 1205 outWidth = fileWidth - srcX; 1206 } else { 1207 outWidth = width; 1208 } 1209 if ((srcY + height) > fileHeight) { 1210 outHeight = fileHeight - srcY; 1211 } else { 1212 outHeight = height; 1213 } 1214 if ((outWidth <= 0) || (outHeight <= 0) 1215 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 1216 return TCL_OK; 1217 } 1218 1219 byteOrder = opts.useHeader? tf.th.byteOrder: opts.byteOrder; 1220 scanOrder = opts.useHeader? tf.th.scanOrder: opts.scanOrder; 1221 pixelType = opts.useHeader? tf.th.pixelType: opts.pixelType; 1222 swapBytes = (( isIntel () && (byteOrder != INTEL)) || 1223 (!isIntel () && (byteOrder == INTEL))); 1224 1225 switch (pixelType) { 1226 case TYPE_FLOAT: { 1227 tf.floatBuf = (Float *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (Float)); 1228 readFloatFile (handle, tf.floatBuf, fileWidth, fileHeight, tf.th.nChans, 1229 swapBytes, opts.verbose, minVals, maxVals); 1230 break; 1231 } 1232 case TYPE_USHORT: { 1233 tf.ushortBuf = (UShort *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UShort)); 1234 readUShortFile (handle, tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans, 1235 swapBytes, opts.verbose, minVals, maxVals); 1236 break; 1237 } 1238 case TYPE_UBYTE: { 1239 tf.ubyteBuf = (UByte *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UByte)); 1240 readUByteFile (handle, tf.ubyteBuf, fileWidth, fileHeight, tf.th.nChans, 1241 swapBytes, opts.verbose, minVals, maxVals); 1242 break; 1243 } 1244 } 1245 if (opts.nomap) { 1246 for (c=0; c<tf.th.nChans; c++) { 1247 minVals[c] = 0.0; 1248 maxVals[c] = 255.0; 1249 } 1250 } else if (opts.minVal != 0.0 || opts.maxVal != 0.0) { 1251 for (c=0; c<tf.th.nChans; c++) { 1252 minVals[c] = opts.minVal; 1253 maxVals[c] = opts.maxVal; 1254 } 1255 } 1256 switch (pixelType) { 1257 case TYPE_FLOAT: { 1258 remapFloatValues (tf.floatBuf, fileWidth, fileHeight, tf.th.nChans, 1259 minVals, maxVals); 1260 break; 1261 } 1262 case TYPE_USHORT: { 1263 remapUShortValues (tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans, 1264 minVals, maxVals); 1265 break; 1266 } 1267 } 1268 1269 if (tkimg_PhotoExpand (interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { 1270 rawClose (&tf); 1271 return TCL_ERROR; 1272 } 1273 1274 tf.pixbuf = (UByte *) ckalloc (fileWidth * tf.th.nChans); 1275 1276 block.pixelSize = tf.th.nChans; 1277 block.pitch = fileWidth * tf.th.nChans; 1278 block.width = outWidth; 1279 block.height = 1; 1280 block.offset[0] = 0; 1281 block.offset[1] = (tf.th.nChans > 1? 1: 0); 1282 block.offset[2] = (tf.th.nChans > 1? 2: 0); 1283 block.offset[3] = (tf.th.nChans == 4 && matte? 3: 0); 1284 block.pixelPtr = tf.pixbuf + srcX * tf.th.nChans; 1285 1286 stopY = srcY + outHeight; 1287 outY = destY; 1288 1289 for (y=0; y<stopY; y++) { 1290 pixbufPtr = tf.pixbuf; 1291 switch (pixelType) { 1292 case TYPE_FLOAT: { 1293 if (scanOrder == BOTTOM_UP) { 1294 floatBufPtr = tf.floatBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans; 1295 } else { 1296 floatBufPtr = tf.floatBuf + y * fileWidth * tf.th.nChans; 1297 } 1298 FloatGammaUByte (fileWidth * tf.th.nChans, floatBufPtr, 1299 opts.gamma != 1.0? gtable: NULL, pixbufPtr); 1300 floatBufPtr += fileWidth * tf.th.nChans; 1301 break; 1302 } 1303 case TYPE_USHORT: { 1304 if (scanOrder == BOTTOM_UP) { 1305 ushortBufPtr = tf.ushortBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans; 1306 } else { 1307 ushortBufPtr = tf.ushortBuf + y * fileWidth * tf.th.nChans; 1308 } 1309 UShortGammaUByte (fileWidth * tf.th.nChans, ushortBufPtr, 1310 opts.gamma != 1.0? gtable: NULL, pixbufPtr); 1311 ushortBufPtr += fileWidth * tf.th.nChans; 1312 break; 1313 } 1314 case TYPE_UBYTE: { 1315 if (scanOrder == BOTTOM_UP) { 1316 ubyteBufPtr = tf.ubyteBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans; 1317 } else { 1318 ubyteBufPtr = tf.ubyteBuf + y * fileWidth * tf.th.nChans; 1319 } 1320 for (x=0; x<fileWidth * tf.th.nChans; x++) { 1321 pixbufPtr[x] = ubyteBufPtr[x]; 1322 } 1323 ubyteBufPtr += fileWidth * tf.th.nChans; 1324 break; 1325 } 1326 } 1327 if (y >= srcY) { 1328 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, 1329 width, 1, 1330 block.offset[3]? 1331 TK_PHOTO_COMPOSITE_SET: 1332 TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) { 1333 result = TCL_ERROR; 1334 break; 1335 } 1336 outY++; 1337 } 1338 } 1339 rawClose (&tf); 1340 return result; 1341} 1342 1343static int ChnWrite (interp, filename, format, blockPtr) 1344 Tcl_Interp *interp; 1345 const char *filename; 1346 Tcl_Obj *format; 1347 Tk_PhotoImageBlock *blockPtr; 1348{ 1349 Tcl_Channel chan; 1350 tkimg_MFile handle; 1351 int result; 1352 1353 chan = tkimg_OpenFileChannel (interp, filename, 0644); 1354 if (!chan) { 1355 return TCL_ERROR; 1356 } 1357 1358 handle.data = (char *) chan; 1359 handle.state = IMG_CHAN; 1360 1361 result = CommonWrite (interp, filename, format, &handle, blockPtr); 1362 if (Tcl_Close(interp, chan) == TCL_ERROR) { 1363 return TCL_ERROR; 1364 } 1365 return result; 1366} 1367 1368static int StringWrite( 1369 Tcl_Interp *interp, 1370 Tcl_Obj *format, 1371 Tk_PhotoImageBlock *blockPtr 1372) { 1373 tkimg_MFile handle; 1374 int result; 1375 Tcl_DString data; 1376 1377 Tcl_DStringInit(&data); 1378 tkimg_WriteInit (&data, &handle); 1379 result = CommonWrite (interp, "InlineData", format, &handle, blockPtr); 1380 tkimg_Putc(IMG_DONE, &handle); 1381 1382 if (result == TCL_OK) { 1383 Tcl_DStringResult(interp, &data); 1384 } else { 1385 Tcl_DStringFree(&data); 1386 } 1387 return result; 1388} 1389 1390static int CommonWrite (interp, filename, format, handle, blockPtr) 1391 Tcl_Interp *interp; 1392 const char *filename; 1393 Tcl_Obj *format; 1394 tkimg_MFile *handle; 1395 Tk_PhotoImageBlock *blockPtr; 1396{ 1397 Int x, y; 1398 Int redOffset, greenOffset, blueOffset, alphaOffset; 1399 UByte *pixelPtr, *rowPixPtr; 1400 RAWFILE tf; 1401 FMTOPT opts; 1402 UByte *ubyteBufPtr; 1403 Int bytesPerLine; 1404 1405 memset (&tf, 0, sizeof (RAWFILE)); 1406 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 1407 return TCL_ERROR; 1408 } 1409 1410 redOffset = 0; 1411 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 1412 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 1413 alphaOffset = blockPtr->offset[0]; 1414 1415 if (alphaOffset < blockPtr->offset[2]) { 1416 alphaOffset = blockPtr->offset[2]; 1417 } 1418 if (++alphaOffset < blockPtr->pixelSize) { 1419 alphaOffset -= blockPtr->offset[0]; 1420 } else { 1421 alphaOffset = 0; 1422 } 1423 1424 initHeader (&tf.th); 1425 tf.th.width = blockPtr->width; 1426 tf.th.height = blockPtr->height; 1427 tf.th.nChans = opts.nchan; 1428 tf.th.scanOrder = opts.scanOrder; 1429 tf.th.pixelType = TYPE_UBYTE; 1430 1431 writeHeader (handle, &tf.th); 1432 bytesPerLine = blockPtr->width * tf.th.nChans * sizeof (UByte); 1433 tf.ubyteBuf = (UByte *)ckalloc (bytesPerLine); 1434 1435 rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0]; 1436 for (y = 0; y < blockPtr->height; y++) { 1437 ubyteBufPtr = tf.ubyteBuf; 1438 pixelPtr = rowPixPtr; 1439 if (tf.th.nChans == 1) { 1440 for (x=0; x<blockPtr->width; x++) { 1441 *ubyteBufPtr = pixelPtr[redOffset]; 1442 ubyteBufPtr++; 1443 pixelPtr += blockPtr->pixelSize; 1444 } 1445 } else { 1446 for (x=0; x<blockPtr->width; x++) { 1447 *(ubyteBufPtr++) = pixelPtr[redOffset]; 1448 *(ubyteBufPtr++) = pixelPtr[greenOffset]; 1449 *(ubyteBufPtr++) = pixelPtr[blueOffset]; 1450 if (tf.th.nChans == 4) { 1451 /* Have a matte channel and write it. */ 1452 *(ubyteBufPtr++) = pixelPtr[alphaOffset]; 1453 } 1454 pixelPtr += blockPtr->pixelSize; 1455 } 1456 } 1457 if (tkimg_Write (handle, (char *)tf.ubyteBuf, bytesPerLine) != bytesPerLine) { 1458 rawClose (&tf); 1459 return TCL_ERROR; 1460 } 1461 rowPixPtr += blockPtr->pitch; 1462 } 1463 if (opts.verbose) 1464 printImgInfo (&tf.th, &opts, filename, "Saving image:"); 1465 rawClose (&tf); 1466 return TCL_OK; 1467} 1468 1469