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, &parallel)) {
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 *) &parallel;
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