summaryrefslogtreecommitdiffstats
path: root/tkimg/dted/dted.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkimg/dted/dted.c')
-rw-r--r--tkimg/dted/dted.c1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/tkimg/dted/dted.c b/tkimg/dted/dted.c
new file mode 100644
index 0000000..efe93ad
--- /dev/null
+++ b/tkimg/dted/dted.c
@@ -0,0 +1,1040 @@
+/* STARTHEADER
+ *
+ * File : dted.c
+ *
+ * Author : Paul Obermeier (paul@poSoft.de)
+ *
+ * Date : Tue Nov 20 21:24:26 CET 2001
+ *
+ * Copyright : (C) 2001-2003 Paul Obermeier
+ *
+ * Description :
+ *
+ * A photo image handler for DTED elevation data interpreted as image files.
+ *
+ * The following image types are supported:
+ *
+ * Grayscale image: Load DTED data as grayscale image.
+ *
+ * List of currently supported features:
+ *
+ * Type | Read | Write |
+ * | -file | -data | -file | -data |
+ * ----------------------------------------
+ * Gray | Yes | Yes | No | No |
+ *
+ * The following format options are available:
+ *
+ * Read DTED image: "dted -verbose <bool> -nchan <int> -nomap <bool>
+ * -gamma <float> -min <float> -max <float>"
+ *
+ * -verbose <bool>: If set to true, additional information about the file
+ * format is printed to stdout. Default is false.
+ * -nchan <int>: Specify the number of channels of the generated image.
+ * Default is 1, i.e. generated a grayscale image.
+ * -gamma <float>: Specify a gamma correction to be applied when mapping
+ * the input data to 8-bit image values.
+ * Default is 1.0.
+ * -nomap <bool>: If set to true, no mapping of input values is done.
+ * Use this option, if your image already contains RGB
+ * values in the range of 0 ..255.
+ * Default is false.
+ * -min <short>: Specify the minimum pixel value to be used for mapping
+ * the input data to 8-bit image values.
+ * Default is the minimum value found in the image data.
+ * -max <short>: Specify the maximum pixel value to be used for mapping
+ * the input data to 8-bit image values.
+ * Default is the maximum value found in the image data.
+ *
+ * Notes:
+ * Currently only reading DTED files as grayscale images
+ * is implemented. Color mapped images and writing will be
+ * implemented when needed.
+ * Syntax checking of DTED files is rudimentary, too.
+ * Only file reading tested right now.
+ *
+ * ENDHEADER
+ *
+ * $Id: dted.c,v 1.1.1.1 2016/01/25 21:20:46 joye Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+/*
+ * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
+ */
+
+#include "init.c"
+
+
+/* #define DEBUG_LOCAL */
+
+#define strIntel "Intel"
+#define strMotorola "Motorola"
+
+#define MAXCHANS 4
+
+/* Some general defines and typedefs. */
+#define TRUE 1
+#define FALSE 0
+#define MIN(a,b) ((a)<(b)? (a): (b))
+#define MAX(a,b) ((a)>(b)? (a): (b))
+
+typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */
+typedef unsigned char UByte; /* Unsigned 8 bit integer */
+typedef char Byte; /* Signed 8 bit integer */
+typedef unsigned short UShort; /* Unsigned 16 bit integer */
+typedef short Short; /* Signed 16 bit integer */
+typedef int UInt; /* Unsigned 32 bit integer */
+typedef int Int; /* Signed 32 bit integer */
+typedef float Float; /* IEEE 32 bit floating point */
+typedef double Double; /* IEEE 64 bit floating point */
+
+#define MAX_SHORT 32767
+#define MIN_SHORT -32768
+
+#define ELEV_UNDEFINED -32000 /* All elevations smaller than this value are
+ considered undefined, and are set to the
+ minimum value. */
+
+/* DTED file header structures */
+
+typedef struct {
+ Byte uhl_tag[3]; /* 'UHL' sentinel tag */
+ Byte reserved1[1];
+ Byte origin_long[8]; /* Longitude of origin */
+ Byte origin_lat[8]; /* Latitude of origin */
+ Byte ew_interval[4]; /* East-west data interval (tenths second) */
+ Byte ns_interval[4]; /* North-south data interval (tenths second) */
+ Byte accuracy[4]; /* Absolute vertical accuracy (meters) */
+ Byte security[3];
+ Byte reserved2[45];
+} UHL_STRUCT;
+
+typedef struct {
+ Byte dsi_tag[3]; /* 'DSI' sentinel tag */
+ Byte security_class[1]; /* Security classification */
+ Byte security_mark[2]; /* Security control & release mark */
+ Byte security_desc[27]; /* Security handling description */
+ Byte reserved1[26];
+ Byte level[5]; /* DMA series designator for level */
+ Byte ref_num[15]; /* Reference number */
+ Byte reserved2[8];
+ Byte edition[2]; /* Data edition */
+ Byte merge_version[1]; /* Match/merge version */
+ Byte maintenance_date[4]; /* Maintenance date (YYMM) */
+ Byte merge_date[4]; /* Match/Merge date (YYMM) */
+ Byte maintenance_desc[4]; /* Maintenance description */
+ Byte producer[8]; /* Producer */
+ Byte reserved3[16];
+ Byte product_num[9]; /* Product specification stock number */
+ Byte product_change[2]; /* Product specification change number */
+ Byte product_date[4]; /* Product specification date (YYMM) */
+ Byte vertical_datum[3]; /* Vertical datum */
+ Byte horizontal_datum[5]; /* Horizontal datum */
+ Byte collection_sys[10]; /* Digitizing collection system */
+ Byte compilation_date[4]; /* Compilation date (YYMM) */
+ Byte reserved4[22];
+ Byte origin_lat[9]; /* Latitude of data origin */
+ Byte origin_long[10]; /* Longitude of data origin */
+ Byte sw_corner_lat[7]; /* Latitude of SW corner */
+ Byte sw_corner_long[8]; /* Longitude of SW corner */
+ Byte nw_corner_lat[7]; /* Latitude of NW corner */
+ Byte nw_corner_long[8]; /* Longitude of NW corner */
+ Byte ne_corner_lat[7]; /* Latitude of NE corner */
+ Byte ne_corner_long[8]; /* Longitude of NE corner */
+ Byte se_corner_lat[7]; /* Latitude of SE corner */
+ Byte se_corner_long[8]; /* Longitude of SE corner */
+ Byte orientation[9]; /* Orientation angle */
+ Byte ns_spacing[4]; /* North-south data spacing (tenths sec) */
+ Byte ew_spacing[4]; /* East-west data spacing (tenths sec) */
+ Byte rows[4]; /* Number of data rows */
+ Byte cols[4]; /* Number of data cols */
+ Byte cell_coverage[2]; /* Partial cell indicator */
+ Byte reserved5[357];
+} DSI_STRUCT;
+
+typedef struct {
+ Byte abs_horiz_acc[4]; /* Absolute horizontal accuracy (meters) */
+ Byte abs_vert_acc[4]; /* Absolute vertical accuracy (meters) */
+ Byte rel_horiz_acc[4]; /* Relative horizontal accuracy (meters) */
+ Byte rel_vert_acc[4]; /* Relative vertical accuracy (meters) */
+} ACCURACY_STRUCT;
+
+typedef struct {
+ Byte latitude[9]; /* Latitude */
+ Byte longitude[10]; /* Longitude */
+} COORD_STRUCT;
+
+typedef struct {
+ ACCURACY_STRUCT acc; /* Accuracy of subregion */
+ Byte no_coords[2]; /* Number of coordinates (03-14) */
+ COORD_STRUCT coords[14]; /* Outline of subregion */
+} SUBREGION_STRUCT;
+
+typedef struct {
+ Byte acc_tag[3]; /* 'ACC' sentinel tag */
+ ACCURACY_STRUCT global_acc; /* Accuracy of product */
+ Byte reserved1[36];
+ Byte no_acc_subregions[2]; /* Number of accuracy subregions
+ (00 = no, 02-09) */
+ SUBREGION_STRUCT subregions[9]; /* Accuracy subregions */
+ Byte reserved2[87];
+} ACC_STRUCT;
+
+typedef struct {
+ UHL_STRUCT uhl;
+ DSI_STRUCT dsi;
+ ACC_STRUCT acc;
+} DTEDHEADER;
+
+/* DTED file format options structure for use with ParseFormatOpts */
+typedef struct {
+ Int nchan;
+ Short minVal;
+ Short maxVal;
+ Float gamma;
+ Boln nomap;
+ Boln verbose;
+} FMTOPT;
+
+/* Structure to hold information about the DTED file being processed. */
+typedef struct {
+ DTEDHEADER th;
+ UByte *pixbuf;
+ Short *rawbuf;
+} DTEDFILE;
+
+#define MINGAMMA 0.01
+#define MAXGAMMA 100.0
+#define GTABSIZE 257
+
+/* Given a pixel value in Float format, "fin", and a gamma-correction
+lookup table, "ftab", macro "gcorrectFloat" returns the gamma-corrected pixel
+value in "fout". */
+
+#define gcorrectFloat(fin,ftab,fout) \
+ { \
+ Int gc_i; \
+ Float gc_t; \
+ gc_t = (fin) * (GTABSIZE - 2); \
+ gc_i = gc_t; \
+ gc_t -= gc_i; \
+ (fout) = (ftab)[gc_i] * (1.0-gc_t) + (ftab)[gc_i+1] * gc_t; \
+ }
+
+static Boln gtableFloat (Float gamma, Float table[])
+{
+ Int i;
+
+ if (gamma < MINGAMMA || gamma > MAXGAMMA) {
+ printf ("Invalid gamma value %f\n", gamma);
+ return FALSE;
+ }
+ for (i = 0; i < GTABSIZE - 1; ++i) {
+ table[i] = pow ((Float) i / (Float) (GTABSIZE - 2), 1.0 / gamma);
+#ifdef DEBUG_LOCAL
+ printf ("gammatable[%d] = %f\n", i, table[i]);
+#endif
+ }
+ table[GTABSIZE - 1] = 1.0;
+#ifdef DEBUG_LOCAL
+ printf ("gammatable[%d] = %f\n", GTABSIZE-1, table[GTABSIZE-1]);
+#endif
+ return TRUE;
+}
+
+static void gammaShortUByte (Int n, const Short s_in[],
+ const Float gtable[], UByte ub_out[])
+{
+ const Short *ssrc, *sstop;
+ Float ftmp;
+ Int itmp;
+ UByte *ubdest;
+
+ ssrc = s_in;
+ sstop = s_in + n;
+ ubdest = ub_out;
+
+ /* Handle a gamma value of 1.0 (gtable == NULL) as a special case.
+ Quite nice speed improvement for the maybe most used case. */
+ if (gtable) {
+ while (ssrc < sstop) {
+ /* Map short values from the range [MIN_SHORT .. MAX_SHORT] to
+ the range [0.0 .. 1.0], do gamma correction and then map into
+ the displayable range [0 .. 255]. */
+ ftmp = (Float)(*ssrc * 1.0 / 65535.0 + 0.5);
+ gcorrectFloat (ftmp, gtable, ftmp);
+ itmp = (Int)(ftmp * 255.0 + 0.5);
+ *ubdest = MAX (0, MIN (itmp, 255));
+#ifdef DEBUG_LOCAL
+ printf ("Gamma %d --> %f --> %d --> %d\n",
+ *ssrc, ftmp, itmp, *ubdest);
+#endif
+ ++ubdest;
+ ++ssrc;
+ }
+ } else {
+ while (ssrc < sstop) {
+ /* Map short values from the range [MIN_SHORT .. MAX_SHORT] to
+ the displayable range [0 .. 255]. */
+ itmp = (Int)(*ssrc * 255.0 / 65535.0 + 128);
+ *ubdest = MAX (0, MIN (itmp, 255));
+#ifdef DEBUG_LOCAL
+ printf ("NoGamma %d --> %d --> %d\n", *ssrc, itmp, *ubdest);
+#endif
+ ++ubdest;
+ ++ssrc;
+ }
+ }
+ return;
+}
+
+/* This function determines at runtime, whether we are on an Intel system. */
+
+static int isIntel (void)
+{
+ unsigned long val = 513;
+ /* On Intel (little-endian) systems this value is equal to "\01\02\00\00".
+ On big-endian systems this value equals "\00\00\02\01" */
+ return memcmp(&val, "\01\02", 2) == 0;
+}
+
+static void dtedClose (DTEDFILE *tf)
+{
+ if (tf->pixbuf) ckfree ((char *)tf->pixbuf);
+ if (tf->rawbuf) ckfree ((char *)tf->rawbuf);
+ return;
+}
+
+/* Read 1 byte, representing an unsigned integer number. */
+
+#if 0 /* unused */
+static Boln readUByte (tkimg_MFile *handle, UByte *b)
+{
+ char buf[1];
+ if (1 != tkimg_Read (handle, buf, 1))
+ return FALSE;
+ *b = buf[0];
+ return TRUE;
+}
+#endif /* unused */
+
+/* Read 2 bytes, representing a signed 16 bit integer in the form
+ <LowByte, HighByte>, from a file and convert them into the current
+ machine's format. */
+
+static Boln readShort (tkimg_MFile *handle, Short *s)
+{
+ char buf[2];
+ if (2 != tkimg_Read (handle, buf, 2))
+ return FALSE;
+ *s = (buf[0] & 0xFF) | (buf[1] << 8);
+ return TRUE;
+}
+
+/* Read 4 bytes, representing a signed 32 bit integer in the form
+ <LowByte, HighByte>, from a file and convert them into the current
+ machine's format. */
+
+static Boln readInt (tkimg_MFile *handle, Int *i)
+{
+ char buf[4];
+ if (4 != tkimg_Read (handle, buf, 4))
+ return FALSE;
+ *i = ((((Int)buf[0] & 0x000000FFU) << 24) | \
+ (((Int)buf[1] & 0x0000FF00U) << 8) | \
+ (((Int)buf[2] & 0x00FF0000U) >> 8) | \
+ (((Int)buf[3] & 0x0000FF00U) >> 24));
+ return TRUE;
+}
+
+/* Write a byte, representing an unsigned integer to a file. */
+
+#if 0 /* unused */
+static Boln writeUByte (tkimg_MFile *handle, UByte b)
+{
+ UByte buf[1];
+ buf[0] = b;
+ if (1 != tkimg_Write (handle, (const char *)buf, 1))
+ return FALSE;
+ return TRUE;
+}
+#endif /* unused */
+
+/* Write a byte, representing a signed integer to a file. */
+
+#if 0 /* unused */
+static Boln writeByte (tkimg_MFile *handle, Byte b)
+{
+ Byte buf[1];
+ buf[0] = b;
+ if (1 != tkimg_Write (handle, buf, 1))
+ return FALSE;
+ return TRUE;
+}
+#endif /* unused */
+
+/* Convert a signed 16 bit integer number into the format
+ <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */
+
+#if 0 /* unused */
+static Boln writeShort (tkimg_MFile *handle, Short s)
+{
+ Byte buf[2];
+ buf[0] = s;
+ buf[1] = s >> 8;
+ if (2 != tkimg_Write (handle, buf, 2))
+ return FALSE;
+ return TRUE;
+}
+#endif /* unused */
+
+/* Convert a unsigned 16 bit integer number into the format
+ <LowByte, HighByte> (an array of 2 bytes) and write the array to a file. */
+
+#if 0 /* unused */
+static Boln writeUShort (tkimg_MFile *handle, UShort s)
+{
+ Byte buf[2];
+ buf[0] = s;
+ buf[1] = s >> 8;
+ if (2 != tkimg_Write (handle, buf, 2))
+ return FALSE;
+ return TRUE;
+}
+#endif /* unused */
+
+#define OUT Tcl_WriteChars (outChan, str, -1)
+static void printImgInfo (DTEDHEADER *th, FMTOPT *opts,
+ const char *filename, const char *msg)
+{
+ Tcl_Channel outChan;
+ char str[256];
+
+ outChan = Tcl_GetStdChannel (TCL_STDOUT);
+ if (!outChan) {
+ return;
+ }
+ sprintf (str, "%s\n", msg); OUT;
+ sprintf (str, "\tLongitude of origin : %.8s\n", th->uhl.origin_long); OUT;
+ sprintf (str, "\tLatitude of origin : %.8s\n", th->uhl.origin_lat); OUT;
+ sprintf (str, "\tEast-West interval : %.4s\n", th->uhl.ew_interval); OUT;
+ sprintf (str, "\tNorth-South interval : %.4s\n", th->uhl.ns_interval); OUT;
+ sprintf (str, "\tVertical accuracy : %.4s\n", th->uhl.accuracy); OUT;
+ sprintf (str, "\tSecurity Code : %.3s\n", th->uhl.security); OUT;
+ sprintf (str, "\tDTED level : %.5s\n", th->dsi.level); OUT;
+ sprintf (str, "\tNumber of rows : %.4s\n", th->dsi.rows); OUT;
+ sprintf (str, "\tNumber of columns : %.4s\n", th->dsi.cols); OUT;
+ sprintf (str, "\tCell coverage : %.2s\n", th->dsi.cell_coverage); OUT;
+ sprintf (str, "\tNo. of channels : %d\n", opts->nchan); OUT;
+ sprintf (str, "\tGamma correction : %f\n", opts->gamma); OUT;
+ sprintf (str, "\tMinimum map value : %d\n", opts->minVal); OUT;
+ sprintf (str, "\tMaximum map value : %d\n", opts->maxVal); OUT;
+ sprintf (str, "\tHost byte order : %s\n", isIntel ()?
+ strIntel: strMotorola); OUT;
+ Tcl_Flush (outChan);
+}
+#undef OUT
+static Boln readHeader (tkimg_MFile *handle, DTEDHEADER *th)
+{
+ if (sizeof (DTEDHEADER) != tkimg_Read (handle, (char *)th, sizeof(DTEDHEADER))) {
+ return FALSE;
+ }
+ if (strncmp ((char *)th->uhl.uhl_tag, "UHL", 3) != 0) {
+ return FALSE;
+ }
+
+ /* OPA: More tests to follow. */
+ return TRUE;
+}
+
+#if 0 /* unused */
+static Boln writeHeader (tkimg_MFile *handle, DTEDHEADER *th)
+{
+ return TRUE;
+}
+#endif /* unused */
+
+#if 0 /* unused */
+static void initHeader (DTEDHEADER *th)
+{
+ th->uhl.uhl_tag[0] = 'U';
+ th->uhl.uhl_tag[1] = 'H';
+ th->uhl.uhl_tag[2] = 'L';
+ /* OPA: More to follow for DTED writing */
+ return;
+}
+#endif /* unused */
+
+static Boln readDtedColumn (tkimg_MFile *handle, Short *pixels, Int nRows,
+ Int nCols, Int curCol, char *buf, Boln hostIsIntel)
+{
+ Int i, nBytes;
+ Short *mPtr;
+ char *bufPtr = buf;
+ Short meridian, parallel;
+ Int block_count;
+ UByte *cp;
+ Int checksum, checksum1 = 0;
+
+ /* Read data column header. */
+ if (!readInt (handle, &block_count) ||
+ !readShort (handle, &meridian) ||
+ !readShort (handle, &parallel)) {
+ printf ("Error reading column header\n");
+ return FALSE;
+ }
+
+ /* Calculate checksum, part 1 */
+ cp = (UByte *) &block_count;
+ checksum1 += cp[0] + cp[1] + cp[2] + cp[3];
+ cp = (UByte *) &meridian;
+ checksum1 += cp[0] + cp[1];
+ cp = (UByte *) &parallel;
+ checksum1 += cp[0] + cp[1];
+
+ if (hostIsIntel) {
+ block_count = block_count & 0x00ffffff;
+ } else {
+ block_count = (block_count & 0xffffff00) >> 8;
+ }
+
+ /* Read the elevation data into the supplied column buffer "buf". */
+ nBytes = sizeof (Short) * nRows;
+ if (nBytes != tkimg_Read (handle, buf, nBytes)) {
+ printf ("Error reading elevation data\n");
+ return FALSE;
+ }
+
+ /* Copy (and swap bytes, if needed) from the column buffer into the
+ pixel array (shorts) . */
+ if (hostIsIntel) {
+ for (i=0; i<nRows; i++) {
+ mPtr = pixels + (i * nCols) + curCol;
+ ((char *)mPtr)[0] = bufPtr[1];
+ ((char *)mPtr)[1] = bufPtr[0];
+ bufPtr += sizeof (Short);
+ }
+ } else {
+ for (i=0; i<nRows; i++) {
+ mPtr = pixels + (i * nCols) + curCol;
+ ((char *)mPtr)[0] = bufPtr[0];
+ ((char *)mPtr)[1] = bufPtr[1];
+ bufPtr += sizeof (Short);
+ }
+ }
+
+ /* Read the checksum */
+ if (!readInt (handle, &checksum)) {
+ printf ("Error reading checksum\n");
+ return FALSE;
+ }
+
+ /* Calculate checksum, part 2. OPA TODO Incorrect */
+ cp = (UByte *) pixels;
+ for (i=0; i<nRows*2; i++, cp++) {
+ checksum1 += *cp;
+ }
+
+ if (checksum != checksum1) {
+ /* printf ("DTED Checksum Error (%d vs. %d).\n", checksum, checksum1); */
+ /* return FALSE; */
+ }
+ return TRUE;
+}
+
+static Boln readDtedFile (tkimg_MFile *handle, Short *buf, Int width, Int height,
+ Int nchan, Boln hostIsIntel, Boln verbose,
+ Short minVals[], Short maxVals[])
+{
+ Int x, y, c;
+ Short *bufPtr = buf;
+ char *colBuf;
+
+#ifdef DEBUG_LOCAL
+ printf ("readDtedFile: Width=%d Height=%d nchan=%d hostIsIntel=%s\n",
+ width, height, nchan, hostIsIntel? "yes": "no");
+#endif
+ for (c=0; c<nchan; c++) {
+ minVals[c] = MAX_SHORT;
+ maxVals[c] = MIN_SHORT;
+ }
+ colBuf = ckalloc (sizeof (Short) * nchan * height);
+
+ /* Read the elevation data column by column. */
+ for (x=0; x<width; x++) {
+ if (!readDtedColumn (handle, buf, height, width,
+ x, colBuf, hostIsIntel)) {
+ return FALSE;
+ }
+ }
+
+ /* Loop through the elevation data and find minimum and maximum values.
+ Ignore elevation values equal to -32767, because these indicate, no
+ elevation data available. See also function remapShortValues.
+ Note: We extend the range of undefined elevations to all values
+ smaller than ELEV_UNDEFINED, because of DTED files not fully
+ compliant to the specification. */
+ bufPtr = buf;
+ for (x=0; x<width; x++) {
+ for (y=0; y<height; y++) {
+ for (c=0; c<nchan; c++) {
+ if ( *bufPtr >= ELEV_UNDEFINED ) {
+ if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
+ if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
+ }
+ bufPtr++;
+ }
+ }
+ }
+ if (verbose) {
+ printf ("\tMinimum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", minVals[c]);
+ }
+ printf ("\n");
+ printf ("\tMaximum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", maxVals[c]);
+ }
+ printf ("\n");
+ fflush (stdout);
+ }
+ ckfree (colBuf);
+ return TRUE;
+}
+
+/* Map the original short values into the range [MIN_SHORT .. MAX_SHORT].
+ We must take care of values equal to -32767, which indicate that no
+ elevation data is available. So we map this value to the minimum value.
+ See also function readDtedFile. */
+
+static Boln remapShortValues (Short *buf, Int width, Int height, Int nchan,
+ Short minVals[], Short maxVals[])
+{
+ Int x, y, c;
+ Int tmpInt;
+ Short tmpShort;
+ Short *bufPtr = buf;
+ Float m[MAXCHANS], t[MAXCHANS];
+
+ for (c=0; c<nchan; c++) {
+ m[c] = (Float)(MAX_SHORT - MIN_SHORT) /
+ (Float)(maxVals[c] - minVals[c]);
+ t[c] = MIN_SHORT - m[c] * minVals[c];
+ }
+ for (y=0; y<height; y++) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ tmpShort = (*bufPtr >= ELEV_UNDEFINED? *bufPtr: minVals[c]);
+ tmpInt = (Int)(tmpShort * m[c] + t[c] + 0.5);
+#ifdef DEBUG_LOCAL
+ printf ("Remap %d --> %d --> %d --> ",
+ *bufPtr, tmpShort, tmpInt);
+#endif
+ if (tmpInt < MIN_SHORT) {
+ *bufPtr = MIN_SHORT;
+ } else if (tmpInt > MAX_SHORT) {
+ *bufPtr = MAX_SHORT;
+ } else {
+ *bufPtr = tmpInt;
+ }
+#ifdef DEBUG_LOCAL
+ printf ("%d (%f %f)\n", *bufPtr, m[c], t[c]);
+#endif
+ bufPtr++;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Here is the start of the standard functions needed for every image format.
+ */
+
+/*
+ * Prototypes for local procedures defined in this file:
+ */
+
+static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
+ FMTOPT *opts);
+static int CommonMatch(Tcl_Interp *interp, tkimg_MFile *handle,
+ Tcl_Obj *format, int *widthPtr, int *heightPtr,
+ DTEDHEADER *dtedHeaderPtr);
+static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
+ const char *filename, Tcl_Obj *format,
+ Tk_PhotoHandle imageHandle, int destX, int destY,
+ int width, int height, int srcX, int srcY);
+static int CommonWrite(Tcl_Interp *interp,
+ const char *filename, Tcl_Obj *format,
+ tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
+
+static int ParseFormatOpts (interp, format, opts)
+ Tcl_Interp *interp;
+ Tcl_Obj *format;
+ FMTOPT *opts;
+{
+ static const char *const dtedOptions[] = {
+ "-verbose", "-nchan", "-min", "-max", "-gamma", "-nomap", NULL
+ };
+ int objc, length, i, index;
+ Tcl_Obj **objv;
+ const char *nchanStr, *verboseStr, *minStr, *maxStr, *gammaStr, *nomapStr;
+
+ /* Initialize format options with default values. */
+ verboseStr = "0";
+ nchanStr = "1";
+ minStr = "0.0";
+ maxStr = "0.0";
+ gammaStr = "1.0";
+ nomapStr = "0";
+
+ if (tkimg_ListObjGetElements (interp, format, &objc, &objv) != TCL_OK)
+ return TCL_ERROR;
+ if (objc) {
+ for (i=1; i<objc; i++) {
+ if (Tcl_GetIndexFromObj (interp, objv[i], (CONST84 char *CONST86 *)dtedOptions,
+ "format option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (++i >= objc) {
+ Tcl_AppendResult (interp, "No value for option \"",
+ Tcl_GetStringFromObj (objv[--i], (int *) NULL),
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ switch(index) {
+ case 0:
+ verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 1:
+ nchanStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 2:
+ minStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 3:
+ maxStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 4:
+ gammaStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 5:
+ nomapStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ }
+ }
+ }
+
+ /* OPA TODO: Check for valid integer and float strings. */
+ opts->nchan = atoi (nchanStr);
+ opts->minVal = atoi (minStr);
+ opts->maxVal = atoi (maxStr);
+ opts->gamma = atof (gammaStr);
+
+ length = strlen (verboseStr);
+ if (!strncmp (verboseStr, "1", length) || \
+ !strncmp (verboseStr, "true", length) || \
+ !strncmp (verboseStr, "on", length)) {
+ opts->verbose = 1;
+ } else if (!strncmp (verboseStr, "0", length) || \
+ !strncmp (verboseStr, "false", length) || \
+ !strncmp (verboseStr, "off", length)) {
+ opts->verbose = 0;
+ } else {
+ Tcl_AppendResult (interp, "invalid verbose mode \"", verboseStr,
+ "\": should be 1 or 0, on or off, true or false",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ length = strlen (nomapStr);
+ if (!strncmp (nomapStr, "1", length) || \
+ !strncmp (nomapStr, "true", length) || \
+ !strncmp (nomapStr, "on", length)) {
+ opts->nomap = 1;
+ } else if (!strncmp (nomapStr, "0", length) || \
+ !strncmp (nomapStr, "false", length) || \
+ !strncmp (nomapStr, "off", length)) {
+ opts->nomap = 0;
+ } else {
+ Tcl_AppendResult (interp, "invalid nomap mode \"", nomapStr,
+ "\": should be 1 or 0, on or off, true or false",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+static int ChnMatch(
+ Tcl_Channel chan,
+ const char *filename,
+ Tcl_Obj *format,
+ int *widthPtr,
+ int *heightPtr,
+ Tcl_Interp *interp
+) {
+ tkimg_MFile handle;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ return CommonMatch(interp, &handle, format, widthPtr, heightPtr, NULL);
+}
+
+static int ObjMatch(
+ Tcl_Obj *data,
+ Tcl_Obj *format,
+ int *widthPtr,
+ int *heightPtr,
+ Tcl_Interp *interp
+) {
+ tkimg_MFile handle;
+
+ tkimg_ReadInit(data, 'U', &handle);
+ return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
+}
+
+static int CommonMatch (interp, handle, format,
+ widthPtr, heightPtr, dtedHeaderPtr)
+ Tcl_Interp *interp;
+ tkimg_MFile *handle;
+ Tcl_Obj *format;
+ int *widthPtr;
+ int *heightPtr;
+ DTEDHEADER *dtedHeaderPtr;
+{
+ DTEDHEADER th;
+ FMTOPT opts;
+ Int nRows, nCols;
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (!readHeader (handle, &th)) {
+ return 0;
+ }
+ sscanf (th.dsi.rows, "%4d", &nRows);
+ sscanf (th.dsi.cols, "%4d", &nCols);
+ *widthPtr = nCols;
+ *heightPtr = nRows;
+ if (dtedHeaderPtr)
+ *dtedHeaderPtr = th;
+ return 1;
+}
+
+static int ChnRead (interp, chan, filename, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
+ Tcl_Channel chan; /* The image channel, open for reading. */
+ const char *filename; /* The name of the image file. */
+ Tcl_Obj *format; /* User-specified format object, or NULL. */
+ Tk_PhotoHandle imageHandle; /* The photo image to write into. */
+ int destX, destY; /* Coordinates of top-left pixel in
+ * photo image to be written to. */
+ int width, height; /* Dimensions of block of photo image to
+ * be written to. */
+ int srcX, srcY; /* Coordinates of top-left pixel to be used
+ * in image being read. */
+{
+ tkimg_MFile handle;
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ return CommonRead (interp, &handle, filename, format, imageHandle,
+ destX, destY, width, height, srcX, srcY);
+}
+
+static int ObjRead (interp, data, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp;
+ Tcl_Obj *data;
+ Tcl_Obj *format;
+ Tk_PhotoHandle imageHandle;
+ int destX, destY;
+ int width, height;
+ int srcX, srcY;
+{
+ tkimg_MFile handle;
+
+ tkimg_ReadInit (data, 'U', &handle);
+ return CommonRead (interp, &handle, "InlineData", format, imageHandle,
+ destX, destY, width, height, srcX, srcY);
+}
+
+static int CommonRead (interp, handle, filename, format, imageHandle,
+ destX, destY, width, height, srcX, srcY)
+ Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
+ tkimg_MFile *handle; /* The image file, open for reading. */
+ const char *filename; /* The name of the image file. */
+ Tcl_Obj *format; /* User-specified format object, or NULL. */
+ Tk_PhotoHandle imageHandle; /* The photo image to write into. */
+ int destX, destY; /* Coordinates of top-left pixel in
+ * photo image to be written to. */
+ int width, height; /* Dimensions of block of photo image to
+ * be written to. */
+ int srcX, srcY; /* Coordinates of top-left pixel to be used
+ * in image being read. */
+{
+ Tk_PhotoImageBlock block;
+ Int y, c;
+ Int fileWidth, fileHeight;
+ Short minVals[MAXCHANS], maxVals[MAXCHANS];
+ int stopY, outY, outWidth, outHeight;
+ DTEDFILE tf;
+ FMTOPT opts;
+ Boln hostIsIntel;
+ Int matte = 0;
+ UByte *pixbufPtr;
+ Short *rawbufPtr;
+ Float gtable[GTABSIZE];
+
+ memset (&tf, 0, sizeof (DTEDFILE));
+ CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th);
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ gtableFloat (opts.gamma, gtable);
+
+ if (opts.verbose)
+ printImgInfo (&tf.th, &opts, filename, "Reading image:");
+
+ if ((srcX + width) > fileWidth) {
+ outWidth = fileWidth - srcX;
+ } else {
+ outWidth = width;
+ }
+ if ((srcY + height) > fileHeight) {
+ outHeight = fileHeight - srcY;
+ } else {
+ outHeight = height;
+ }
+ if ((outWidth <= 0) || (outHeight <= 0)
+ || (srcX >= fileWidth) || (srcY >= fileHeight)) {
+ return TCL_OK;
+ }
+
+ hostIsIntel = isIntel ();
+
+ tf.rawbuf = (Short *)ckalloc (fileWidth * fileHeight *
+ opts.nchan * sizeof (Short));
+ readDtedFile (handle, tf.rawbuf, fileWidth, fileHeight, opts.nchan,
+ hostIsIntel, opts.verbose, minVals, maxVals);
+ if (opts.nomap) {
+ for (c=0; c<opts.nchan; c++) {
+ minVals[c] = 0;
+ maxVals[c] = 255;
+ }
+ } else if (opts.minVal != 0 || opts.maxVal != 0) {
+ for (c=0; c<opts.nchan; c++) {
+ minVals[c] = opts.minVal;
+ maxVals[c] = opts.maxVal;
+ }
+ }
+ remapShortValues (tf.rawbuf, fileWidth, fileHeight, opts.nchan,
+ minVals, maxVals);
+
+ if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
+ dtedClose(&tf);
+ return TCL_ERROR;
+ }
+
+ tf.pixbuf = (UByte *) ckalloc (fileWidth * opts.nchan);
+
+ block.pixelSize = opts.nchan;
+ block.pitch = fileWidth * opts.nchan;
+ block.width = outWidth;
+ block.height = 1;
+ block.offset[0] = 0;
+ block.offset[1] = (opts.nchan > 1? 1: 0);
+ block.offset[2] = (opts.nchan > 1? 2: 0);
+ block.offset[3] = (opts.nchan == 4 && matte? 3: 0);
+ block.pixelPtr = tf.pixbuf + srcX * opts.nchan;
+
+ stopY = srcY + outHeight;
+ outY = destY;
+
+ for (y=0; y<stopY; y++) {
+ pixbufPtr = tf.pixbuf;
+ rawbufPtr = tf.rawbuf + (fileHeight - 1 - y) * fileWidth * opts.nchan;
+ gammaShortUByte (fileWidth * opts.nchan, rawbufPtr,
+ opts.gamma != 1.0? gtable: NULL, pixbufPtr);
+ rawbufPtr += fileWidth * opts.nchan;
+ if (y >= srcY) {
+ if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
+ width, 1, TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
+ dtedClose(&tf);
+ return TCL_ERROR;
+ }
+ outY++;
+ }
+ }
+ dtedClose(&tf);
+ return TCL_OK;
+}
+
+static int ChnWrite (interp, filename, format, blockPtr)
+ Tcl_Interp *interp;
+ const char *filename;
+ Tcl_Obj *format;
+ Tk_PhotoImageBlock *blockPtr;
+{
+ Tcl_Channel chan;
+ tkimg_MFile handle;
+ int result;
+
+ chan = tkimg_OpenFileChannel (interp, filename, 0644);
+ if (!chan) {
+ return TCL_ERROR;
+ }
+
+ handle.data = (char *) chan;
+ handle.state = IMG_CHAN;
+
+ result = CommonWrite (interp, filename, format, &handle, blockPtr);
+ if (Tcl_Close(interp, chan) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ return result;
+}
+
+static int StringWrite(
+ Tcl_Interp *interp,
+ Tcl_Obj *format,
+ Tk_PhotoImageBlock *blockPtr
+) {
+ tkimg_MFile handle;
+ int result;
+ Tcl_DString data;
+
+ Tcl_DStringInit(&data);
+ tkimg_WriteInit (&data, &handle);
+ result = CommonWrite (interp, "InlineData", format, &handle, blockPtr);
+ tkimg_Putc(IMG_DONE, &handle);
+
+ if (result == TCL_OK) {
+ Tcl_DStringResult(interp, &data);
+ } else {
+ Tcl_DStringFree(&data);
+ }
+ return result;
+}
+
+static int CommonWrite (interp, filename, format, handle, blockPtr)
+ Tcl_Interp *interp;
+ const char *filename;
+ Tcl_Obj *format;
+ tkimg_MFile *handle;
+ Tk_PhotoImageBlock *blockPtr;
+{
+ return TCL_OK;
+}