1/* STARTHEADER 2 * 3 * File : dted.c 4 * 5 * Author : Paul Obermeier (paul@poSoft.de) 6 * 7 * Date : Tue Nov 20 21:24:26 CET 2001 8 * 9 * Copyright : (C) 2001-2003 Paul Obermeier 10 * 11 * Description : 12 * 13 * A photo image handler for DTED elevation data interpreted as image files. 14 * 15 * The following image types are supported: 16 * 17 * Grayscale image: Load DTED data as grayscale image. 18 * 19 * List of currently supported features: 20 * 21 * Type | Read | Write | 22 * | -file | -data | -file | -data | 23 * ---------------------------------------- 24 * Gray | Yes | Yes | No | No | 25 * 26 * The following format options are available: 27 * 28 * Read DTED image: "dted -verbose <bool> -nchan <int> -nomap <bool> 29 * -gamma <float> -min <float> -max <float>" 30 * 31 * -verbose <bool>: If set to true, additional information about the file 32 * format is printed to stdout. Default is false. 33 * -nchan <int>: Specify the number of channels of the generated image. 34 * Default is 1, i.e. generated a grayscale image. 35 * -gamma <float>: Specify a gamma correction to be applied when mapping 36 * the input data to 8-bit image values. 37 * Default is 1.0. 38 * -nomap <bool>: If set to true, no mapping of input values is done. 39 * Use this option, if your image already contains RGB 40 * values in the range of 0 ..255. 41 * Default is false. 42 * -min <short>: Specify the minimum pixel value to be used for mapping 43 * the input data to 8-bit image values. 44 * Default is the minimum value found in the image data. 45 * -max <short>: Specify the maximum pixel value to be used for mapping 46 * the input data to 8-bit image values. 47 * Default is the maximum value found in the image data. 48 * 49 * Notes: 50 * Currently only reading DTED files as grayscale images 51 * is implemented. Color mapped images and writing will be 52 * implemented when needed. 53 * Syntax checking of DTED files is rudimentary, too. 54 * Only file reading tested right now. 55 * 56 * ENDHEADER 57 * 58 * $Id$ 59 * 60 */ 61 62#include <stdlib.h> 63#include <math.h> 64 65/* 66 * Generic initialization code, parameterized via CPACKAGE and PACKAGE. 67 */ 68 69#include "init.c" 70 71 72/* #define DEBUG_LOCAL */ 73 74#define strIntel "Intel" 75#define strMotorola "Motorola" 76 77#define MAXCHANS 4 78 79/* Some general defines and typedefs. */ 80#define TRUE 1 81#define FALSE 0 82#define MIN(a,b) ((a)<(b)? (a): (b)) 83#define MAX(a,b) ((a)>(b)? (a): (b)) 84 85typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ 86typedef unsigned char UByte; /* Unsigned 8 bit integer */ 87typedef char Byte; /* Signed 8 bit integer */ 88typedef unsigned short UShort; /* Unsigned 16 bit integer */ 89typedef short Short; /* Signed 16 bit integer */ 90typedef int UInt; /* Unsigned 32 bit integer */ 91typedef int Int; /* Signed 32 bit integer */ 92typedef float Float; /* IEEE 32 bit floating point */ 93typedef double Double; /* IEEE 64 bit floating point */ 94 95#define MAX_SHORT 32767 96#define MIN_SHORT -32768 97 98#define ELEV_UNDEFINED -32000 /* All elevations smaller than this value are 99 considered undefined, and are set to the 100 minimum value. */ 101 102/* DTED file header structures */ 103 104typedef struct { 105 Byte uhl_tag[3]; /* 'UHL' sentinel tag */ 106 Byte reserved1[1]; 107 Byte origin_long[8]; /* Longitude of origin */ 108 Byte origin_lat[8]; /* Latitude of origin */ 109 Byte ew_interval[4]; /* East-west data interval (tenths second) */ 110 Byte ns_interval[4]; /* North-south data interval (tenths second) */ 111 Byte accuracy[4]; /* Absolute vertical accuracy (meters) */ 112 Byte security[3]; 113 Byte reserved2[45]; 114} UHL_STRUCT; 115 116typedef struct { 117 Byte dsi_tag[3]; /* 'DSI' sentinel tag */ 118 Byte security_class[1]; /* Security classification */ 119 Byte security_mark[2]; /* Security control & release mark */ 120 Byte security_desc[27]; /* Security handling description */ 121 Byte reserved1[26]; 122 Byte level[5]; /* DMA series designator for level */ 123 Byte ref_num[15]; /* Reference number */ 124 Byte reserved2[8]; 125 Byte edition[2]; /* Data edition */ 126 Byte merge_version[1]; /* Match/merge version */ 127 Byte maintenance_date[4]; /* Maintenance date (YYMM) */ 128 Byte merge_date[4]; /* Match/Merge date (YYMM) */ 129 Byte maintenance_desc[4]; /* Maintenance description */ 130 Byte producer[8]; /* Producer */ 131 Byte reserved3[16]; 132 Byte product_num[9]; /* Product specification stock number */ 133 Byte product_change[2]; /* Product specification change number */ 134 Byte product_date[4]; /* Product specification date (YYMM) */ 135 Byte vertical_datum[3]; /* Vertical datum */ 136 Byte horizontal_datum[5]; /* Horizontal datum */ 137 Byte collection_sys[10]; /* Digitizing collection system */ 138 Byte compilation_date[4]; /* Compilation date (YYMM) */ 139 Byte reserved4[22]; 140 Byte origin_lat[9]; /* Latitude of data origin */ 141 Byte origin_long[10]; /* Longitude of data origin */ 142 Byte sw_corner_lat[7]; /* Latitude of SW corner */ 143 Byte sw_corner_long[8]; /* Longitude of SW corner */ 144 Byte nw_corner_lat[7]; /* Latitude of NW corner */ 145 Byte nw_corner_long[8]; /* Longitude of NW corner */ 146 Byte ne_corner_lat[7]; /* Latitude of NE corner */ 147 Byte ne_corner_long[8]; /* Longitude of NE corner */ 148 Byte se_corner_lat[7]; /* Latitude of SE corner */ 149 Byte se_corner_long[8]; /* Longitude of SE corner */ 150 Byte orientation[9]; /* Orientation angle */ 151 Byte ns_spacing[4]; /* North-south data spacing (tenths sec) */ 152 Byte ew_spacing[4]; /* East-west data spacing (tenths sec) */ 153 Byte rows[4]; /* Number of data rows */ 154 Byte cols[4]; /* Number of data cols */ 155 Byte cell_coverage[2]; /* Partial cell indicator */ 156 Byte reserved5[357]; 157} DSI_STRUCT; 158 159typedef struct { 160 Byte abs_horiz_acc[4]; /* Absolute horizontal accuracy (meters) */ 161 Byte abs_vert_acc[4]; /* Absolute vertical accuracy (meters) */ 162 Byte rel_horiz_acc[4]; /* Relative horizontal accuracy (meters) */ 163 Byte rel_vert_acc[4]; /* Relative vertical accuracy (meters) */ 164} ACCURACY_STRUCT; 165 166typedef struct { 167 Byte latitude[9]; /* Latitude */ 168 Byte longitude[10]; /* Longitude */ 169} COORD_STRUCT; 170 171typedef struct { 172 ACCURACY_STRUCT acc; /* Accuracy of subregion */ 173 Byte no_coords[2]; /* Number of coordinates (03-14) */ 174 COORD_STRUCT coords[14]; /* Outline of subregion */ 175} SUBREGION_STRUCT; 176 177typedef struct { 178 Byte acc_tag[3]; /* 'ACC' sentinel tag */ 179 ACCURACY_STRUCT global_acc; /* Accuracy of product */ 180 Byte reserved1[36]; 181 Byte no_acc_subregions[2]; /* Number of accuracy subregions 182 (00 = no, 02-09) */ 183 SUBREGION_STRUCT subregions[9]; /* Accuracy subregions */ 184 Byte reserved2[87]; 185} ACC_STRUCT; 186 187typedef struct { 188 UHL_STRUCT uhl; 189 DSI_STRUCT dsi; 190 ACC_STRUCT acc; 191} DTEDHEADER; 192 193/* DTED file format options structure for use with ParseFormatOpts */ 194typedef struct { 195 Int nchan; 196 Short minVal; 197 Short maxVal; 198 Float gamma; 199 Boln nomap; 200 Boln verbose; 201} FMTOPT; 202 203/* Structure to hold information about the DTED file being processed. */ 204typedef struct { 205 DTEDHEADER th; 206 UByte *pixbuf; 207 Short *rawbuf; 208} DTEDFILE; 209 210#define MINGAMMA 0.01 211#define MAXGAMMA 100.0 212#define GTABSIZE 257 213 214/* Given a pixel value in Float format, "fin", and a gamma-correction 215lookup table, "ftab", macro "gcorrectFloat" returns the gamma-corrected pixel 216value in "fout". */ 217 218#define gcorrectFloat(fin,ftab,fout) \ 219 { \ 220 Int gc_i; \ 221 Float gc_t; \ 222 gc_t = (fin) * (GTABSIZE - 2); \ 223 gc_i = gc_t; \ 224 gc_t -= gc_i; \ 225 (fout) = (ftab)[gc_i] * (1.0-gc_t) + (ftab)[gc_i+1] * gc_t; \ 226 } 227 228static Boln gtableFloat (Float gamma, Float table[]) 229{ 230 Int i; 231 232 if (gamma < MINGAMMA || gamma > MAXGAMMA) { 233 printf ("Invalid gamma value %f\n", gamma); 234 return FALSE; 235 } 236 for (i = 0; i < GTABSIZE - 1; ++i) { 237 table[i] = pow ((Float) i / (Float) (GTABSIZE - 2), 1.0 / gamma); 238#ifdef DEBUG_LOCAL 239 printf ("gammatable[%d] = %f\n", i, table[i]); 240#endif 241 } 242 table[GTABSIZE - 1] = 1.0; 243#ifdef DEBUG_LOCAL 244 printf ("gammatable[%d] = %f\n", GTABSIZE-1, table[GTABSIZE-1]); 245#endif 246 return TRUE; 247} 248 249static void gammaShortUByte (Int n, const Short s_in[], 250 const Float gtable[], UByte ub_out[]) 251{ 252 const Short *ssrc, *sstop; 253 Float ftmp; 254 Int itmp; 255 UByte *ubdest; 256 257 ssrc = s_in; 258 sstop = s_in + n; 259 ubdest = ub_out; 260 261 /* Handle a gamma value of 1.0 (gtable == NULL) as a special case. 262 Quite nice speed improvement for the maybe most used case. */ 263 if (gtable) { 264 while (ssrc < sstop) { 265 /* Map short values from the range [MIN_SHORT .. MAX_SHORT] to 266 the range [0.0 .. 1.0], do gamma correction and then map into 267 the displayable range [0 .. 255]. */ 268 ftmp = (Float)(*ssrc * 1.0 / 65535.0 + 0.5); 269 gcorrectFloat (ftmp, gtable, ftmp); 270 itmp = (Int)(ftmp * 255.0 + 0.5); 271 *ubdest = MAX (0, MIN (itmp, 255)); 272#ifdef DEBUG_LOCAL 273 printf ("Gamma %d --> %f --> %d --> %d\n", 274 *ssrc, ftmp, itmp, *ubdest); 275#endif 276 ++ubdest; 277 ++ssrc; 278 } 279 } else { 280 while (ssrc < sstop) { 281 /* Map short values from the range [MIN_SHORT .. MAX_SHORT] to 282 the displayable range [0 .. 255]. */ 283 itmp = (Int)(*ssrc * 255.0 / 65535.0 + 128); 284 *ubdest = MAX (0, MIN (itmp, 255)); 285#ifdef DEBUG_LOCAL 286 printf ("NoGamma %d --> %d --> %d\n", *ssrc, itmp, *ubdest); 287#endif 288 ++ubdest; 289 ++ssrc; 290 } 291 } 292 return; 293} 294 295/* This function determines at runtime, whether we are on an Intel system. */ 296 297static int isIntel (void) 298{ 299 unsigned long val = 513; 300 /* On Intel (little-endian) systems this value is equal to "\01\02\00\00". 301 On big-endian systems this value equals "\00\00\02\01" */ 302 return memcmp(&val, "\01\02", 2) == 0; 303} 304 305static void dtedClose (DTEDFILE *tf) 306{ 307 if (tf->pixbuf) ckfree ((char *)tf->pixbuf); 308 if (tf->rawbuf) ckfree ((char *)tf->rawbuf); 309 return; 310} 311 312/* Read 1 byte, representing an unsigned integer number. */ 313 314#if 0 /* unused */ 315static Boln readUByte (tkimg_MFile *handle, UByte *b) 316{ 317 char buf[1]; 318 if (1 != tkimg_Read (handle, buf, 1)) 319 return FALSE; 320 *b = buf[0]; 321 return TRUE; 322} 323#endif /* unused */ 324 325/* Read 2 bytes, representing a signed 16 bit integer in the form 326 <LowByte, HighByte>, from a file and convert them into the current 327 machine's format. */ 328 329static Boln readShort (tkimg_MFile *handle, Short *s) 330{ 331 char buf[2]; 332 if (2 != tkimg_Read (handle, buf, 2)) 333 return FALSE; 334 *s = (buf[0] & 0xFF) | (buf[1] << 8); 335 return TRUE; 336} 337 338/* Read 4 bytes, representing a signed 32 bit integer in the form 339 <LowByte, HighByte>, from a file and convert them into the current 340 machine's format. */ 341 342static Boln readInt (tkimg_MFile *handle, Int *i) 343{ 344 char buf[4]; 345 if (4 != tkimg_Read (handle, buf, 4)) 346 return FALSE; 347 *i = ((((Int)buf[0] & 0x000000FFU) << 24) | \ 348 (((Int)buf[1] & 0x0000FF00U) << 8) | \ 349 (((Int)buf[2] & 0x00FF0000U) >> 8) | \ 350 (((Int)buf[3] & 0x0000FF00U) >> 24)); 351 return TRUE; 352} 353 354/* Write a byte, representing an unsigned integer to a file. */ 355 356#if 0 /* unused */ 357static Boln writeUByte (tkimg_MFile *handle, UByte b) 358{ 359 UByte buf[1]; 360 buf[0] = b; 361 if (1 != tkimg_Write (handle, (const char *)buf, 1)) 362 return FALSE; 363 return TRUE; 364} 365#endif /* unused */ 366 367/* Write a byte, representing a signed integer to a file. */ 368 369#if 0 /* unused */ 370static Boln writeByte (tkimg_MFile *handle, Byte b) 371{ 372 Byte buf[1]; 373 buf[0] = b; 374 if (1 != tkimg_Write (handle, buf, 1)) 375 return FALSE; 376 return TRUE; 377} 378#endif /* unused */ 379 380/* Convert a signed 16 bit integer number into the format 381 <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */ 382 383#if 0 /* unused */ 384static Boln writeShort (tkimg_MFile *handle, Short s) 385{ 386 Byte buf[2]; 387 buf[0] = s; 388 buf[1] = s >> 8; 389 if (2 != tkimg_Write (handle, buf, 2)) 390 return FALSE; 391 return TRUE; 392} 393#endif /* unused */ 394 395/* Convert a unsigned 16 bit integer number into the format 396 <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */ 397 398#if 0 /* unused */ 399static Boln writeUShort (tkimg_MFile *handle, UShort s) 400{ 401 Byte buf[2]; 402 buf[0] = s; 403 buf[1] = s >> 8; 404 if (2 != tkimg_Write (handle, buf, 2)) 405 return FALSE; 406 return TRUE; 407} 408#endif /* unused */ 409 410#define OUT Tcl_WriteChars (outChan, str, -1) 411static void printImgInfo (DTEDHEADER *th, FMTOPT *opts, 412 const char *filename, const char *msg) 413{ 414 Tcl_Channel outChan; 415 char str[256]; 416 417 outChan = Tcl_GetStdChannel (TCL_STDOUT); 418 if (!outChan) { 419 return; 420 } 421 sprintf (str, "%s\n", msg); OUT; 422 sprintf (str, "\tLongitude of origin : %.8s\n", th->uhl.origin_long); OUT; 423 sprintf (str, "\tLatitude of origin : %.8s\n", th->uhl.origin_lat); OUT; 424 sprintf (str, "\tEast-West interval : %.4s\n", th->uhl.ew_interval); OUT; 425 sprintf (str, "\tNorth-South interval : %.4s\n", th->uhl.ns_interval); OUT; 426 sprintf (str, "\tVertical accuracy : %.4s\n", th->uhl.accuracy); OUT; 427 sprintf (str, "\tSecurity Code : %.3s\n", th->uhl.security); OUT; 428 sprintf (str, "\tDTED level : %.5s\n", th->dsi.level); OUT; 429 sprintf (str, "\tNumber of rows : %.4s\n", th->dsi.rows); OUT; 430 sprintf (str, "\tNumber of columns : %.4s\n", th->dsi.cols); OUT; 431 sprintf (str, "\tCell coverage : %.2s\n", th->dsi.cell_coverage); OUT; 432 sprintf (str, "\tNo. of channels : %d\n", opts->nchan); OUT; 433 sprintf (str, "\tGamma correction : %f\n", opts->gamma); OUT; 434 sprintf (str, "\tMinimum map value : %d\n", opts->minVal); OUT; 435 sprintf (str, "\tMaximum map value : %d\n", opts->maxVal); OUT; 436 sprintf (str, "\tHost byte order : %s\n", isIntel ()? 437 strIntel: strMotorola); OUT; 438 Tcl_Flush (outChan); 439} 440#undef OUT 441static Boln readHeader (tkimg_MFile *handle, DTEDHEADER *th) 442{ 443 if (sizeof (DTEDHEADER) != tkimg_Read (handle, (char *)th, sizeof(DTEDHEADER))) { 444 return FALSE; 445 } 446 if (strncmp ((char *)th->uhl.uhl_tag, "UHL", 3) != 0) { 447 return FALSE; 448 } 449 450 /* OPA: More tests to follow. */ 451 return TRUE; 452} 453 454#if 0 /* unused */ 455static Boln writeHeader (tkimg_MFile *handle, DTEDHEADER *th) 456{ 457 return TRUE; 458} 459#endif /* unused */ 460 461#if 0 /* unused */ 462static void initHeader (DTEDHEADER *th) 463{ 464 th->uhl.uhl_tag[0] = 'U'; 465 th->uhl.uhl_tag[1] = 'H'; 466 th->uhl.uhl_tag[2] = 'L'; 467 /* OPA: More to follow for DTED writing */ 468 return; 469} 470#endif /* unused */ 471 472static Boln readDtedColumn (tkimg_MFile *handle, Short *pixels, Int nRows, 473 Int nCols, Int curCol, char *buf, Boln hostIsIntel) 474{ 475 Int i, nBytes; 476 Short *mPtr; 477 char *bufPtr = buf; 478 Short meridian, parallel; 479 Int block_count; 480 UByte *cp; 481 Int checksum, checksum1 = 0; 482 UByte sentinel; 483 484 /* Read data column header. */ 485 if (!readInt (handle, &block_count) || 486 !readShort (handle, &meridian) || 487 !readShort (handle, ¶llel)) { 488 printf ("Error reading column header\n"); 489 return FALSE; 490 } 491 492 /* Calculate checksum, part 1 */ 493 cp = (UByte *) &block_count; 494 checksum1 += cp[0] + cp[1] + cp[2] + cp[3]; 495 cp = (UByte *) &meridian; 496 checksum1 += cp[0] + cp[1]; 497 cp = (UByte *) ∥ 498 checksum1 += cp[0] + cp[1]; 499 500 if (hostIsIntel) { 501 sentinel = (UByte) ((block_count & 0xff000000) >> 24); 502 block_count = block_count & 0x00ffffff; 503 } else { 504 sentinel = (UByte) (block_count & 0x000000ff); 505 block_count = (block_count & 0xffffff00) >> 8; 506 } 507 508 /* Read the elevation data into the supplied column buffer "buf". */ 509 nBytes = sizeof (Short) * nRows; 510 if (nBytes != tkimg_Read (handle, buf, nBytes)) { 511 printf ("Error reading elevation data\n"); 512 return FALSE; 513 } 514 515 /* Copy (and swap bytes, if needed) from the column buffer into the 516 pixel array (shorts) . */ 517 if (hostIsIntel) { 518 for (i=0; i<nRows; i++) { 519 mPtr = pixels + (i * nCols) + curCol; 520 ((char *)mPtr)[0] = bufPtr[1]; 521 ((char *)mPtr)[1] = bufPtr[0]; 522 bufPtr += sizeof (Short); 523 } 524 } else { 525 for (i=0; i<nRows; i++) { 526 mPtr = pixels + (i * nCols) + curCol; 527 ((char *)mPtr)[0] = bufPtr[0]; 528 ((char *)mPtr)[1] = bufPtr[1]; 529 bufPtr += sizeof (Short); 530 } 531 } 532 533 /* Read the checksum */ 534 if (!readInt (handle, &checksum)) { 535 printf ("Error reading checksum\n"); 536 return FALSE; 537 } 538 539 /* Calculate checksum, part 2. OPA TODO Incorrect */ 540 cp = (UByte *) pixels; 541 for (i=0; i<nRows*2; i++, cp++) { 542 checksum1 += *cp; 543 } 544 545 if (checksum != checksum1) { 546 /* printf ("DTED Checksum Error (%d vs. %d).\n", checksum, checksum1); */ 547 /* return FALSE; */ 548 } 549 return TRUE; 550} 551 552static Boln readDtedFile (tkimg_MFile *handle, Short *buf, Int width, Int height, 553 Int nchan, Boln hostIsIntel, Boln verbose, 554 Short minVals[], Short maxVals[]) 555{ 556 Int x, y, c; 557 Short *bufPtr = buf; 558 char *colBuf; 559 560#ifdef DEBUG_LOCAL 561 printf ("readDtedFile: Width=%d Height=%d nchan=%d hostIsIntel=%s\n", 562 width, height, nchan, hostIsIntel? "yes": "no"); 563#endif 564 for (c=0; c<nchan; c++) { 565 minVals[c] = MAX_SHORT; 566 maxVals[c] = MIN_SHORT; 567 } 568 colBuf = ckalloc (sizeof (Short) * nchan * height); 569 570 /* Read the elevation data column by column. */ 571 for (x=0; x<width; x++) { 572 if (!readDtedColumn (handle, buf, height, width, 573 x, colBuf, hostIsIntel)) { 574 return FALSE; 575 } 576 } 577 578 /* Loop through the elevation data and find minimum and maximum values. 579 Ignore elevation values equal to -32767, because these indicate, no 580 elevation data available. See also function remapShortValues. 581 Note: We extend the range of undefined elevations to all values 582 smaller than ELEV_UNDEFINED, because of DTED files not fully 583 compliant to the specification. */ 584 bufPtr = buf; 585 for (x=0; x<width; x++) { 586 for (y=0; y<height; y++) { 587 for (c=0; c<nchan; c++) { 588 if ( *bufPtr >= ELEV_UNDEFINED ) { 589 if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr; 590 if (*bufPtr < minVals[c]) minVals[c] = *bufPtr; 591 } 592 bufPtr++; 593 } 594 } 595 } 596 if (verbose) { 597 printf ("\tMinimum pixel values :"); 598 for (c=0; c<nchan; c++) { 599 printf (" %d", minVals[c]); 600 } 601 printf ("\n"); 602 printf ("\tMaximum pixel values :"); 603 for (c=0; c<nchan; c++) { 604 printf (" %d", maxVals[c]); 605 } 606 printf ("\n"); 607 fflush (stdout); 608 } 609 ckfree (colBuf); 610 return TRUE; 611} 612 613/* Map the original short values into the range [MIN_SHORT .. MAX_SHORT]. 614 We must take care of values equal to -32767, which indicate that no 615 elevation data is available. So we map this value to the minimum value. 616 See also function readDtedFile. */ 617 618static Boln remapShortValues (Short *buf, Int width, Int height, Int nchan, 619 Short minVals[], Short maxVals[]) 620{ 621 Int x, y, c; 622 Int tmpInt; 623 Short tmpShort; 624 Short *bufPtr = buf; 625 Float m[MAXCHANS], t[MAXCHANS]; 626 627 for (c=0; c<nchan; c++) { 628 m[c] = (Float)(MAX_SHORT - MIN_SHORT) / 629 (Float)(maxVals[c] - minVals[c]); 630 t[c] = MIN_SHORT - m[c] * minVals[c]; 631 } 632 for (y=0; y<height; y++) { 633 for (x=0; x<width; x++) { 634 for (c=0; c<nchan; c++) { 635 tmpShort = (*bufPtr >= ELEV_UNDEFINED? *bufPtr: minVals[c]); 636 tmpInt = (Int)(tmpShort * m[c] + t[c] + 0.5); 637#ifdef DEBUG_LOCAL 638 printf ("Remap %d --> %d --> %d --> ", 639 *bufPtr, tmpShort, tmpInt); 640#endif 641 if (tmpInt < MIN_SHORT) { 642 *bufPtr = MIN_SHORT; 643 } else if (tmpInt > MAX_SHORT) { 644 *bufPtr = MAX_SHORT; 645 } else { 646 *bufPtr = tmpInt; 647 } 648#ifdef DEBUG_LOCAL 649 printf ("%d (%f %f)\n", *bufPtr, m[c], t[c]); 650#endif 651 bufPtr++; 652 } 653 } 654 } 655 return TRUE; 656} 657 658/* 659 * Here is the start of the standard functions needed for every image format. 660 */ 661 662/* 663 * Prototypes for local procedures defined in this file: 664 */ 665 666static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, 667 FMTOPT *opts); 668static int CommonMatch(Tcl_Interp *interp, tkimg_MFile *handle, 669 Tcl_Obj *format, int *widthPtr, int *heightPtr, 670 DTEDHEADER *dtedHeaderPtr); 671static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, 672 const char *filename, Tcl_Obj *format, 673 Tk_PhotoHandle imageHandle, int destX, int destY, 674 int width, int height, int srcX, int srcY); 675static int CommonWrite(Tcl_Interp *interp, 676 const char *filename, Tcl_Obj *format, 677 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr); 678 679static int ParseFormatOpts (interp, format, opts) 680 Tcl_Interp *interp; 681 Tcl_Obj *format; 682 FMTOPT *opts; 683{ 684 static const char *const dtedOptions[] = { 685 "-verbose", "-nchan", "-min", "-max", "-gamma", "-nomap" 686 }; 687 int objc, length, c, i, index; 688 Tcl_Obj **objv; 689 const char *nchanStr, *verboseStr, *minStr, *maxStr, *gammaStr, *nomapStr; 690 691 /* Initialize format options with default values. */ 692 verboseStr = "0"; 693 nchanStr = "1"; 694 minStr = "0.0"; 695 maxStr = "0.0"; 696 gammaStr = "1.0"; 697 nomapStr = "0"; 698 699 if (tkimg_ListObjGetElements (interp, format, &objc, &objv) != TCL_OK) 700 return TCL_ERROR; 701 if (objc) { 702 for (i=1; i<objc; i++) { 703 if (Tcl_GetIndexFromObj (interp, objv[i], (CONST84 char *CONST86 *)dtedOptions, 704 "format option", 0, &index) != TCL_OK) { 705 return TCL_ERROR; 706 } 707 if (++i >= objc) { 708 Tcl_AppendResult (interp, "No value for option \"", 709 Tcl_GetStringFromObj (objv[--i], (int *) NULL), 710 "\"", (char *) NULL); 711 return TCL_ERROR; 712 } 713 switch(index) { 714 case 0: 715 verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 716 break; 717 case 1: 718 nchanStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 719 break; 720 case 2: 721 minStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 722 break; 723 case 3: 724 maxStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 725 break; 726 case 4: 727 gammaStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 728 break; 729 case 5: 730 nomapStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); 731 break; 732 } 733 } 734 } 735 736 /* OPA TODO: Check for valid integer and float strings. */ 737 opts->nchan = atoi (nchanStr); 738 opts->minVal = atoi (minStr); 739 opts->maxVal = atoi (maxStr); 740 opts->gamma = atof (gammaStr); 741 742 c = verboseStr[0]; length = strlen (verboseStr); 743 if (!strncmp (verboseStr, "1", length) || \ 744 !strncmp (verboseStr, "true", length) || \ 745 !strncmp (verboseStr, "on", length)) { 746 opts->verbose = 1; 747 } else if (!strncmp (verboseStr, "0", length) || \ 748 !strncmp (verboseStr, "false", length) || \ 749 !strncmp (verboseStr, "off", length)) { 750 opts->verbose = 0; 751 } else { 752 Tcl_AppendResult (interp, "invalid verbose mode \"", verboseStr, 753 "\": should be 1 or 0, on or off, true or false", 754 (char *) NULL); 755 return TCL_ERROR; 756 } 757 758 c = nomapStr[0]; length = strlen (nomapStr); 759 if (!strncmp (nomapStr, "1", length) || \ 760 !strncmp (nomapStr, "true", length) || \ 761 !strncmp (nomapStr, "on", length)) { 762 opts->nomap = 1; 763 } else if (!strncmp (nomapStr, "0", length) || \ 764 !strncmp (nomapStr, "false", length) || \ 765 !strncmp (nomapStr, "off", length)) { 766 opts->nomap = 0; 767 } else { 768 Tcl_AppendResult (interp, "invalid nomap mode \"", nomapStr, 769 "\": should be 1 or 0, on or off, true or false", 770 (char *) NULL); 771 return TCL_ERROR; 772 } 773 774 return TCL_OK; 775} 776 777static int ChnMatch( 778 Tcl_Channel chan, 779 const char *filename, 780 Tcl_Obj *format, 781 int *widthPtr, 782 int *heightPtr, 783 Tcl_Interp *interp 784) { 785 tkimg_MFile handle; 786 787 handle.data = (char *) chan; 788 handle.state = IMG_CHAN; 789 790 return CommonMatch(interp, &handle, format, widthPtr, heightPtr, NULL); 791} 792 793static int ObjMatch( 794 Tcl_Obj *data, 795 Tcl_Obj *format, 796 int *widthPtr, 797 int *heightPtr, 798 Tcl_Interp *interp 799) { 800 tkimg_MFile handle; 801 802 tkimg_ReadInit(data, 'U', &handle); 803 return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL); 804} 805 806static int CommonMatch (interp, handle, format, 807 widthPtr, heightPtr, dtedHeaderPtr) 808 Tcl_Interp *interp; 809 tkimg_MFile *handle; 810 Tcl_Obj *format; 811 int *widthPtr; 812 int *heightPtr; 813 DTEDHEADER *dtedHeaderPtr; 814{ 815 DTEDHEADER th; 816 FMTOPT opts; 817 Int nRows, nCols; 818 819 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 820 return TCL_ERROR; 821 } 822 823 if (!readHeader (handle, &th)) { 824 return 0; 825 } 826 sscanf (th.dsi.rows, "%4d", &nRows); 827 sscanf (th.dsi.cols, "%4d", &nCols); 828 *widthPtr = nCols; 829 *heightPtr = nRows; 830 if (dtedHeaderPtr) 831 *dtedHeaderPtr = th; 832 return 1; 833} 834 835static int ChnRead (interp, chan, filename, format, imageHandle, 836 destX, destY, width, height, srcX, srcY) 837 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 838 Tcl_Channel chan; /* The image channel, open for reading. */ 839 const char *filename; /* The name of the image file. */ 840 Tcl_Obj *format; /* User-specified format object, or NULL. */ 841 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 842 int destX, destY; /* Coordinates of top-left pixel in 843 * photo image to be written to. */ 844 int width, height; /* Dimensions of block of photo image to 845 * be written to. */ 846 int srcX, srcY; /* Coordinates of top-left pixel to be used 847 * in image being read. */ 848{ 849 tkimg_MFile handle; 850 851 handle.data = (char *) chan; 852 handle.state = IMG_CHAN; 853 854 return CommonRead (interp, &handle, filename, format, imageHandle, 855 destX, destY, width, height, srcX, srcY); 856} 857 858static int ObjRead (interp, data, format, imageHandle, 859 destX, destY, width, height, srcX, srcY) 860 Tcl_Interp *interp; 861 Tcl_Obj *data; 862 Tcl_Obj *format; 863 Tk_PhotoHandle imageHandle; 864 int destX, destY; 865 int width, height; 866 int srcX, srcY; 867{ 868 tkimg_MFile handle; 869 870 tkimg_ReadInit (data, 'U', &handle); 871 return CommonRead (interp, &handle, "InlineData", format, imageHandle, 872 destX, destY, width, height, srcX, srcY); 873} 874 875static int CommonRead (interp, handle, filename, format, imageHandle, 876 destX, destY, width, height, srcX, srcY) 877 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ 878 tkimg_MFile *handle; /* The image file, open for reading. */ 879 const char *filename; /* The name of the image file. */ 880 Tcl_Obj *format; /* User-specified format object, or NULL. */ 881 Tk_PhotoHandle imageHandle; /* The photo image to write into. */ 882 int destX, destY; /* Coordinates of top-left pixel in 883 * photo image to be written to. */ 884 int width, height; /* Dimensions of block of photo image to 885 * be written to. */ 886 int srcX, srcY; /* Coordinates of top-left pixel to be used 887 * in image being read. */ 888{ 889 Tk_PhotoImageBlock block; 890 Int y, c; 891 Int fileWidth, fileHeight; 892 Short minVals[MAXCHANS], maxVals[MAXCHANS]; 893 int stopY, outY, outWidth, outHeight; 894 DTEDFILE tf; 895 FMTOPT opts; 896 Boln hostIsIntel; 897 Int matte = 0; 898 UByte *pixbufPtr; 899 Short *rawbufPtr; 900 Float gtable[GTABSIZE]; 901 902 memset (&tf, 0, sizeof (DTEDFILE)); 903 CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th); 904 905 if (ParseFormatOpts (interp, format, &opts) != TCL_OK) { 906 return TCL_ERROR; 907 } 908 909 gtableFloat (opts.gamma, gtable); 910 911 if (opts.verbose) 912 printImgInfo (&tf.th, &opts, filename, "Reading image:"); 913 914 if ((srcX + width) > fileWidth) { 915 outWidth = fileWidth - srcX; 916 } else { 917 outWidth = width; 918 } 919 if ((srcY + height) > fileHeight) { 920 outHeight = fileHeight - srcY; 921 } else { 922 outHeight = height; 923 } 924 if ((outWidth <= 0) || (outHeight <= 0) 925 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 926 return TCL_OK; 927 } 928 929 hostIsIntel = isIntel (); 930 931 tf.rawbuf = (Short *)ckalloc (fileWidth * fileHeight * 932 opts.nchan * sizeof (Short)); 933 readDtedFile (handle, tf.rawbuf, fileWidth, fileHeight, opts.nchan, 934 hostIsIntel, opts.verbose, minVals, maxVals); 935 if (opts.nomap) { 936 for (c=0; c<opts.nchan; c++) { 937 minVals[c] = 0; 938 maxVals[c] = 255; 939 } 940 } else if (opts.minVal != 0 || opts.maxVal != 0) { 941 for (c=0; c<opts.nchan; c++) { 942 minVals[c] = opts.minVal; 943 maxVals[c] = opts.maxVal; 944 } 945 } 946 remapShortValues (tf.rawbuf, fileWidth, fileHeight, opts.nchan, 947 minVals, maxVals); 948 949 if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { 950 dtedClose(&tf); 951 return TCL_ERROR; 952 } 953 954 tf.pixbuf = (UByte *) ckalloc (fileWidth * opts.nchan); 955 956 block.pixelSize = opts.nchan; 957 block.pitch = fileWidth * opts.nchan; 958 block.width = outWidth; 959 block.height = 1; 960 block.offset[0] = 0; 961 block.offset[1] = (opts.nchan > 1? 1: 0); 962 block.offset[2] = (opts.nchan > 1? 2: 0); 963 block.offset[3] = (opts.nchan == 4 && matte? 3: 0); 964 block.pixelPtr = tf.pixbuf + srcX * opts.nchan; 965 966 stopY = srcY + outHeight; 967 outY = destY; 968 969 for (y=0; y<stopY; y++) { 970 pixbufPtr = tf.pixbuf; 971 rawbufPtr = tf.rawbuf + (fileHeight - 1 - y) * fileWidth * opts.nchan; 972 gammaShortUByte (fileWidth * opts.nchan, rawbufPtr, 973 opts.gamma != 1.0? gtable: NULL, pixbufPtr); 974 rawbufPtr += fileWidth * opts.nchan; 975 if (y >= srcY) { 976 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, 977 width, 1, TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) { 978 dtedClose(&tf); 979 return TCL_ERROR; 980 } 981 outY++; 982 } 983 } 984 dtedClose(&tf); 985 return TCL_OK; 986} 987 988static int ChnWrite (interp, filename, format, blockPtr) 989 Tcl_Interp *interp; 990 const char *filename; 991 Tcl_Obj *format; 992 Tk_PhotoImageBlock *blockPtr; 993{ 994 Tcl_Channel chan; 995 tkimg_MFile handle; 996 int result; 997 998 chan = tkimg_OpenFileChannel (interp, filename, 0644); 999 if (!chan) { 1000 return TCL_ERROR; 1001 } 1002 1003 handle.data = (char *) chan; 1004 handle.state = IMG_CHAN; 1005 1006 result = CommonWrite (interp, filename, format, &handle, blockPtr); 1007 if (Tcl_Close(interp, chan) == TCL_ERROR) { 1008 return TCL_ERROR; 1009 } 1010 return result; 1011} 1012 1013static int StringWrite( 1014 Tcl_Interp *interp, 1015 Tcl_Obj *format, 1016 Tk_PhotoImageBlock *blockPtr 1017) { 1018 tkimg_MFile handle; 1019 int result; 1020 Tcl_DString data; 1021 1022 Tcl_DStringInit(&data); 1023 tkimg_WriteInit (&data, &handle); 1024 result = CommonWrite (interp, "InlineData", format, &handle, blockPtr); 1025 tkimg_Putc(IMG_DONE, &handle); 1026 1027 if (result == TCL_OK) { 1028 Tcl_DStringResult(interp, &data); 1029 } else { 1030 Tcl_DStringFree(&data); 1031 } 1032 return result; 1033} 1034 1035static int CommonWrite (interp, filename, format, handle, blockPtr) 1036 Tcl_Interp *interp; 1037 const char *filename; 1038 Tcl_Obj *format; 1039 tkimg_MFile *handle; 1040 Tk_PhotoImageBlock *blockPtr; 1041{ 1042 return TCL_OK; 1043} 1044