summaryrefslogtreecommitdiffstats
path: root/tkimg/raw/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkimg/raw/raw.c')
-rwxr-xr-xtkimg/raw/raw.c1494
1 files changed, 1494 insertions, 0 deletions
diff --git a/tkimg/raw/raw.c b/tkimg/raw/raw.c
new file mode 100755
index 0000000..e3d36d2
--- /dev/null
+++ b/tkimg/raw/raw.c
@@ -0,0 +1,1494 @@
+/* STARTHEADER
+ *
+ * File : raw.c
+ *
+ * Author : Paul Obermeier (paul@poSoft.de)
+ *
+ * Date : Wed Feb 21 12:45:08 CET 2001
+ *
+ * Copyright : (C) 2001-2004 Paul Obermeier
+ *
+ * Description :
+ *
+ * A photo image handler for raw data interpreted as image files.
+ *
+ * The following image types are currently supported:
+ *
+ * Grayscale image: 1 channel of 32-bit floating point values.
+ * 1 channel of 16-bit unsigned integer values.
+ * 1 channel of 8-bit unsigned integer values.
+ * True-color image: 3 channels of 32-bit floating point values.
+ * 3 channels of 16-bit unsigned integer values.
+ * 3 channels of 8-bit unsigned integer values.
+ *
+ * List of currently supported features:
+ *
+ * Type | Read | Write |
+ * | -file | -data | -file | -data |
+ * ----------------------------------------
+ * Gray | Yes | Yes | Yes | Yes |
+ * RGB | Yes | Yes | Yes | Yes |
+ *
+ * There are 2 supported file formats:
+ * One with the pure raw data only, the other with a 7 line ASCII header of the
+ * following form:
+ *
+ * Magic=RAW\n File format identifier. Fixed value.
+ * Width=128\n Image width in pixels.
+ * Height=128\n Image height in pixels.
+ * NumChan=1\n Possible values: 1 or 3.
+ * ByteOrder=Intel\n Possible values: "Intel" or "Motorola".
+ * ScanOrder=TopDown\n Possible values: "TopDown" or "BottomUp".
+ * PixelType=byte\n Possible values: "float", "short" or "byte".
+ *
+ * The following format options are available:
+ *
+ * For raw images with header:
+ * Read RAW image: "raw -useheader true -verbose <bool> -gamma <float>
+ * -min <float> -max <float>"
+ * Write RAW image: "raw -useheader true -verbose <bool> -nchan <int>
+ * -scanorder <string>"
+ *
+ * For raw images without header:
+ * Read RAW image: "raw -useheader false -verbose <bool> -nchan <int>
+ * -scanorder <string> -byteorder <string>
+ * -width <int> -height <int> -gamma <float>
+ * -pixeltype <string> -min <float> -max <float>
+ * -uuencode <bool>"
+ * Write RAW image: "raw -useheader false -verbose <bool> -nchan <int>
+ * -scanorder <string>"
+ *
+ * -verbose <bool>: If set to true, additional information about the file
+ * format is printed to stdout. Default is false.
+ * -useheader <bool>: If set to true, use file header information for reading
+ * and writing. Default is true.
+ * -nchan <int>: Specify the number of channels of the input image.
+ * Only valid, if reading image data without header.
+ * Default is 1.
+ * -width <int>: Specify the width of the input image. Only valid, if
+ * reading image data without header. Default is 128.
+ * -height <int>: Specify the height of the input image. Only valid, if
+ * reading image data without header. Default is 128.
+ * -byteorder <string>: Specify the byteorder of the input image. Only valid, if
+ * reading image data without header.
+ * Possible values: "Intel" or "Motorola".
+ * Default is assuming the same byteorder as that of the
+ * host computer.
+ * -scanorder <string>: Specify the scanline order of the input image. Only
+ * valid, if reading image data without header.
+ * Possible values: "TopDown" or "BottomUp".
+ * Default is "TopDown".
+ * -pixeltype <string>: Specify the type of the pixel values.
+ * Only valid, if reading image data without header.
+ * Possible values: "float", "short" or "byte".
+ * Default is "byte".
+ * -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.
+ * When using the nomap mode, no information about the
+ * minimum and maximum pixel values is gathered during
+ * reading and therefore no verbose output is printed.
+ * On the other hand reading the image is faster.
+ * Default is false.
+ * -uuencode <bool>: If set to false, do not assume, that the image data stored in a
+ * variable is uuencoded. Default is true, i.e. the image data is
+ * assumed to be uuencoded.
+ * -min <float>: 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 <float>: 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 RAW files are only written in "byte" pixel format.
+ *
+ * ENDHEADER
+ *
+ * $Id$
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+/*
+ * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
+ */
+
+#include "init.c"
+
+/*
+#define DEBUG_LOCAL
+*/
+
+/* Maximum length of a header line. */
+#define HEADLEN 100
+
+/* Header fields. */
+#define strMagic "Magic=%s\n"
+#define strWidth "Width=%d\n"
+#define strHeight "Height=%d\n"
+#define strNumChan "NumChan=%d\n"
+#define strByteOrder "ByteOrder=%s\n"
+#define strScanOrder "ScanOrder=%s\n"
+#define strPixelType "PixelType=%s\n"
+
+/* Header fields possible values. */
+#define strIntel "Intel"
+#define strMotorola "Motorola"
+#define strTopDown "TopDown"
+#define strBottomUp "BottomUp"
+#define strFloat "float"
+#define strUShort "short"
+#define strUByte "byte"
+
+#define strUnknown "Unknown"
+
+#define BOTTOM_UP 0
+#define TOP_DOWN 1
+#define INTEL 0
+#define MOTOROLA 1
+#define TYPE_FLOAT 0
+#define TYPE_USHORT 1
+#define TYPE_UBYTE 2
+
+#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 */
+
+/* RAW file header structure */
+typedef struct {
+ char id[3];
+ Int nChans;
+ Int width;
+ Int height;
+ Int scanOrder;
+ Int byteOrder;
+ Int pixelType;
+} RAWHEADER;
+
+/* RAW file format options structure for use with ParseFormatOpts */
+typedef struct {
+ Int width;
+ Int height;
+ Int nchan;
+ Int scanOrder;
+ Int byteOrder;
+ Int pixelType;
+ Float minVal;
+ Float maxVal;
+ Float gamma;
+ Boln nomap;
+ Boln verbose;
+ Boln uuencode;
+ Boln useHeader;
+} FMTOPT;
+
+/* Structure to hold information about the Targa file being processed. */
+typedef struct {
+ RAWHEADER th;
+ UByte *pixbuf;
+ Float *floatBuf;
+ UShort *ushortBuf;
+ UByte *ubyteBuf;
+} RAWFILE;
+
+#define MINGAMMA 0.01
+#define MAXGAMMA 100.0
+#define GTABSIZE 257
+
+/* Given a pixel value in Float format, "valIn", and a gamma-correction
+ * lookup table, "tab", macro "gcorrectFloat" returns the gamma-corrected
+ * pixel value in "valOut".
+ */
+
+#define gcorrectFloat(valIn,tab,valOut) \
+ { \
+ Int gc_i; \
+ Float gc_t; \
+ gc_t = (valIn) * (GTABSIZE - 2); \
+ gc_i = gc_t; \
+ gc_t -= gc_i; \
+ (valOut) = (tab)[gc_i] * (1.0-gc_t) + (tab)[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);
+ }
+ table[GTABSIZE - 1] = 1.0;
+ return TRUE;
+}
+
+/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for
+ * parameter gtable.
+ */
+static void FloatGammaUByte (Int n, const Float floatIn[],
+ const Float gtable[], UByte ubOut[])
+{
+ const Float *src, *stop;
+ Float ftmp;
+ Int itmp;
+ UByte *ubdest;
+
+ src = floatIn;
+ stop = floatIn + n;
+ ubdest = ubOut;
+
+ /* 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 (src < stop) {
+ ftmp = MAX (0.0, MIN (*src, 1.0));
+ gcorrectFloat (ftmp, gtable, ftmp);
+ itmp = (Int)(ftmp * 255.0 + 0.5);
+ *ubdest = MAX (0, MIN (itmp, 255));
+ ++ubdest;
+ ++src;
+ }
+ } else {
+ while (src < stop) {
+ itmp = (Int)(*src * 255.0 + 0.5);
+ *ubdest = MAX (0, MIN (itmp, 255));
+ ++ubdest;
+ ++src;
+ }
+ }
+ return;
+}
+
+/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for
+ * parameter gtable.
+ */
+static void UShortGammaUByte (Int n, const UShort shortIn[],
+ const Float gtable[], UByte ubOut[])
+{
+ const UShort *src, *stop;
+ Float ftmp;
+ Int itmp;
+ UByte *ubdest;
+
+ src = shortIn;
+ stop = shortIn + n;
+ ubdest = ubOut;
+
+ /* 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 (src < stop) {
+ ftmp = *src / 65535.0;
+ ftmp = MAX (0.0, MIN (ftmp, 1.0));
+ gcorrectFloat (ftmp, gtable, ftmp);
+ itmp = (Int)(ftmp * 255.0 + 0.5);
+ *ubdest = MAX (0, MIN (itmp, 255));
+ ++ubdest;
+ ++src;
+ }
+ } else {
+ while (src < stop) {
+ itmp = (Int)(*src / 256);
+ *ubdest = MAX (0, MIN (itmp, 255));
+ ++ubdest;
+ ++src;
+ }
+ }
+ 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 rawClose (RAWFILE *tf, Boln fastMode)
+{
+ if (!fastMode) {
+ if (tf->pixbuf) ckfree ((char *)tf->pixbuf);
+ }
+ if (tf->floatBuf) ckfree ((char *)tf->floatBuf);
+ if (tf->ushortBuf) ckfree ((char *)tf->ushortBuf);
+ if (tf->ubyteBuf) ckfree ((char *)tf->ubyteBuf);
+ return;
+}
+
+static Boln readFloatRow (tkimg_MFile *handle, Float *pixels, Int nFloats,
+ char *buf, Boln swapBytes)
+{
+ int i;
+ Float *mPtr = pixels;
+ char *bufPtr = buf;
+
+#ifdef DEBUG_LOCAL
+ printf ("Reading %d floats\n", nFloats); fflush (stdout);
+#endif
+ if (4 * nFloats != tkimg_Read (handle, buf, 4 * nFloats))
+ return FALSE;
+
+ if (swapBytes) {
+ for (i=0; i<nFloats; i++) {
+ ((char *)mPtr)[0] = bufPtr[3];
+ ((char *)mPtr)[1] = bufPtr[2];
+ ((char *)mPtr)[2] = bufPtr[1];
+ ((char *)mPtr)[3] = bufPtr[0];
+ mPtr++;
+ bufPtr += 4;
+ }
+ } else {
+ for (i=0; i<nFloats; i++) {
+ ((char *)mPtr)[0] = bufPtr[0];
+ ((char *)mPtr)[1] = bufPtr[1];
+ ((char *)mPtr)[2] = bufPtr[2];
+ ((char *)mPtr)[3] = bufPtr[3];
+ mPtr++;
+ bufPtr += 4;
+ }
+ }
+ return TRUE;
+}
+
+static Boln readUShortRow (tkimg_MFile *handle, UShort *pixels, Int nShorts,
+ char *buf, Boln swapBytes)
+{
+ int i;
+ UShort *mPtr = pixels;
+ char *bufPtr = buf;
+
+#ifdef DEBUG_LOCAL
+ printf ("Reading %d UShorts\n", nShorts); fflush (stdout);
+#endif
+ if (2 * nShorts != tkimg_Read (handle, buf, 2 * nShorts))
+ return FALSE;
+
+ if (swapBytes) {
+ for (i=0; i<nShorts; i++) {
+ ((char *)mPtr)[0] = bufPtr[1];
+ ((char *)mPtr)[1] = bufPtr[0];
+ mPtr++;
+ bufPtr += 2;
+ }
+ } else {
+ for (i=0; i<nShorts; i++) {
+ ((char *)mPtr)[0] = bufPtr[0];
+ ((char *)mPtr)[1] = bufPtr[1];
+ mPtr++;
+ bufPtr += 2;
+ }
+ }
+ return TRUE;
+}
+
+static Boln readUByteRow (tkimg_MFile *handle, UByte *pixels, Int nBytes)
+{
+#ifdef DEBUG_LOCAL
+ printf ("Reading %d UBytes\n", nBytes); fflush (stdout);
+#endif
+ if (nBytes != tkimg_Read (handle, (char *)pixels, nBytes))
+ return FALSE;
+
+ return TRUE;
+}
+
+#define OUT Tcl_WriteChars (outChan, str, -1)
+static void printImgInfo (RAWHEADER *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 %s\n", msg, filename); OUT;
+ sprintf (str, "\tSize in pixel : %d x %d\n", th->width, th->height); OUT;
+ sprintf (str, "\tNo. of channels : %d\n", th->nChans); OUT;
+ sprintf (str, "\tPixel type : %s\n", (th->pixelType == TYPE_FLOAT? strFloat:
+ (th->pixelType == TYPE_USHORT? strUShort:
+ (th->pixelType == TYPE_UBYTE? strUByte:
+ strUnknown)))); OUT;
+ sprintf (str, "\tVertical encoding: %s\n", th->scanOrder == TOP_DOWN?
+ strTopDown: strBottomUp); OUT;
+ sprintf (str, "\tGamma correction : %f\n", opts->gamma); OUT;
+ sprintf (str, "\tMinimum map value: %f\n", opts->minVal); OUT;
+ sprintf (str, "\tMaximum map value: %f\n", opts->maxVal); OUT;
+ sprintf (str, "\tHost byte order : %s\n", isIntel ()? strIntel: strMotorola); OUT;
+ sprintf (str, "\tFile byte order : %s\n", th->byteOrder == INTEL?
+ strIntel: strMotorola); OUT;
+ Tcl_Flush (outChan);
+}
+#undef OUT
+
+static Boln readHeaderLine (Tcl_Interp *interp, tkimg_MFile *handle, char *buf)
+{
+ char c, *bufPtr, *bufEndPtr;
+ Boln failure;
+
+ bufPtr = buf;
+ bufEndPtr = buf + HEADLEN;
+ failure = TRUE;
+
+#ifdef DEBUG_LOCAL
+ printf ("readHeaderLine\n"); fflush (stdout);
+#endif
+
+ while (tkimg_Read (handle, &c, 1) == 1 && bufPtr < bufEndPtr) {
+ if (c == '\n') {
+ *bufPtr = '\0';
+ failure = FALSE;
+ break;
+ }
+ *bufPtr = c;
+ bufPtr++;
+ }
+ if (failure) {
+ Tcl_AppendResult (interp, "RAW handler: Error reading header line (",
+ buf, ")\n", NULL);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static Boln readHeader (Tcl_Interp *interp, tkimg_MFile *handle, RAWHEADER *th)
+{
+ char buf[HEADLEN];
+ char tmpStr[HEADLEN];
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strMagic, th->id))) {
+ Tcl_AppendResult (interp, "Unable to parse header field Magic\n", NULL);
+ return FALSE;
+ }
+ if (strcmp (th->id, "RAW") != 0) {
+ Tcl_AppendResult (interp, "Invalid value for header field Magic:",
+ "Must be \"RAW\"\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strWidth, &th->width))) {
+ Tcl_AppendResult (interp, "Unable to parse header field Width\n", NULL);
+ return FALSE;
+ }
+ if (th->width < 1) {
+ Tcl_AppendResult (interp, "Invalid value for header field Width:",
+ "Must be greater than zero\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strHeight, &th->height))) {
+ Tcl_AppendResult (interp, "Unable to parse header field Height\n", NULL);
+ return FALSE;
+ }
+ if (th->height < 1) {
+ Tcl_AppendResult (interp, "Invalid value for header field Height:",
+ "Must be greater than zero\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strNumChan, &th->nChans))) {
+ Tcl_AppendResult (interp, "Unable to parse header field NumChan\n", NULL);
+ return FALSE;
+ }
+ if (th->nChans != 1 && th->nChans != 3) {
+ Tcl_AppendResult (interp, "Invalid value for header field NumChan:",
+ "Must be 1 or 3\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strByteOrder, tmpStr))) {
+ Tcl_AppendResult (interp, "Unable to parse header field ByteOrder\n", NULL);
+ return FALSE;
+ }
+
+ if (strcmp (tmpStr, strIntel) == 0) {
+ th->byteOrder = INTEL;
+ } else if (strcmp (tmpStr, strMotorola) == 0) {
+ th->byteOrder = MOTOROLA;
+ } else {
+ Tcl_AppendResult (interp, "Invalid value for header field ByteOrder:",
+ "Must be ", strIntel, " or ", strMotorola,
+ "\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strScanOrder, tmpStr))) {
+ Tcl_AppendResult (interp, "Unable to parse header field ScanOrder\n", NULL);
+ return FALSE;
+ }
+ if (strcmp (tmpStr, strTopDown) == 0) {
+ th->scanOrder = TOP_DOWN;
+ } else if (strcmp (tmpStr, strBottomUp) == 0) {
+ th->scanOrder = BOTTOM_UP;
+ } else {
+ Tcl_AppendResult (interp, "Invalid value for header field ScanOrder:",
+ "Must be ", strTopDown, " or ", strBottomUp,
+ "\n", NULL);
+ return FALSE;
+ }
+
+ if (!readHeaderLine (interp, handle, buf) ||
+ (1 != sscanf (buf, strPixelType, tmpStr))) {
+ Tcl_AppendResult (interp, "Unable to parse header field PixelType\n", NULL);
+ return FALSE;
+ }
+ if (strcmp (tmpStr, strFloat) == 0) {
+ th->pixelType = TYPE_FLOAT;
+ } else if (strcmp (tmpStr, strUShort) == 0) {
+ th->pixelType = TYPE_USHORT;
+ } else if (strcmp (tmpStr, strUByte) == 0) {
+ th->pixelType = TYPE_UBYTE;
+ } else {
+ Tcl_AppendResult (interp, "Invalid value for header field PixelType:",
+ "Must be ", strFloat, ",", strUShort, " or ", strUByte,
+ "\n", NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static Boln writeHeader (tkimg_MFile *handle, RAWHEADER *th)
+{
+ char buf[1024];
+
+ sprintf (buf, strMagic, "RAW");
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strWidth, th->width);
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strHeight, th->height);
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strNumChan, th->nChans);
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strByteOrder, isIntel()? strIntel: strMotorola);
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strScanOrder, th->scanOrder == TOP_DOWN?
+ strTopDown: strBottomUp);
+ tkimg_Write (handle, buf, strlen (buf));
+ sprintf (buf, strPixelType, (th->pixelType == TYPE_FLOAT? strFloat:
+ (th->pixelType == TYPE_USHORT? strUShort:
+ (th->pixelType == TYPE_UBYTE? strUByte:
+ strUnknown))));
+ tkimg_Write (handle, buf, strlen (buf));
+ return TRUE;
+}
+
+static void initHeader (RAWHEADER *th)
+{
+ th->id[0] = 'R';
+ th->id[1] = 'A';
+ th->id[2] = 'W';
+ th->nChans = 1;
+ th->width = 128;
+ th->height = 128;
+ th->scanOrder = TOP_DOWN;
+ th->byteOrder = INTEL;
+ th->pixelType = TYPE_UBYTE;
+ return;
+}
+
+static Boln readFloatFile (tkimg_MFile *handle, Float *buf, Int width, Int height,
+ Int nchan, Boln swapBytes, Boln verbose, Boln findMinMax,
+ Float minVals[], Float maxVals[])
+{
+ Int x, y, c;
+ Float *bufPtr = buf;
+ char *line;
+
+#ifdef DEBUG_LOCAL
+ printf ("readFloatFile: Width=%d Height=%d nchan=%d swapBytes=%s\n",
+ width, height, nchan, swapBytes? "yes": "no"); fflush (stdout);
+#endif
+ for (c=0; c<nchan; c++) {
+ minVals[c] = (Float) 1.0E30;
+ maxVals[c] = (Float)-1.0E30;
+ }
+ line = ckalloc (sizeof (Float) * nchan * width);
+
+ for (y=0; y<height; y++) {
+ if (!readFloatRow (handle, bufPtr, nchan * width, line, swapBytes))
+ return FALSE;
+ if (findMinMax) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
+ if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
+ bufPtr++;
+ }
+ }
+ } else {
+ bufPtr += nchan * width;
+ }
+ }
+ if (verbose && findMinMax) {
+ printf ("\tMinimum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %f", minVals[c]);
+ }
+ printf ("\n");
+ printf ("\tMaximum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %f", maxVals[c]);
+ }
+ printf ("\n");
+ fflush (stdout);
+ }
+ ckfree (line);
+ return TRUE;
+}
+
+static Boln readUShortFile (tkimg_MFile *handle, UShort *buf, Int width, Int height,
+ Int nchan, Boln swapBytes, Boln verbose, Boln findMinMax,
+ Float minVals[], Float maxVals[])
+{
+ Int x, y, c;
+ UShort *bufPtr = buf;
+ char *line;
+
+#ifdef DEBUG_LOCAL
+ printf ("readUShortFile: Width=%d Height=%d nchan=%d swapBytes=%s\n",
+ width, height, nchan, swapBytes? "yes": "no"); fflush (stdout);
+#endif
+ for (c=0; c<nchan; c++) {
+ minVals[c] = (Float) 1.0E30;
+ maxVals[c] = (Float)-1.0E30;
+ }
+ line = ckalloc (sizeof (UShort) * nchan * width);
+
+ for (y=0; y<height; y++) {
+ if (!readUShortRow (handle, bufPtr, nchan * width, line, swapBytes))
+ return FALSE;
+ if (findMinMax) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
+ if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
+ bufPtr++;
+ }
+ }
+ } else {
+ bufPtr += nchan * width;
+ }
+ }
+ if (verbose && findMinMax) {
+ printf ("\tMinimum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", (UShort)minVals[c]);
+ }
+ printf ("\n");
+ printf ("\tMaximum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", (UShort)maxVals[c]);
+ }
+ printf ("\n");
+ fflush (stdout);
+ }
+ ckfree (line);
+ return TRUE;
+}
+
+static Boln readUByteFile (tkimg_MFile *handle, UByte *buf, Int width, Int height,
+ Int nchan, Boln verbose, Boln findMinMax,
+ Float minVals[], Float maxVals[])
+{
+ Int x, y, c;
+ UByte *bufPtr = buf;
+
+#ifdef DEBUG_LOCAL
+ printf ("readUByteFile: Width=%d Height=%d nchan=%d swapBytes=%s findMinMax=%s\n",
+ width, height, nchan, findMinMax? "yes": "no");
+ fflush (stdout);
+#endif
+ for (c=0; c<nchan; c++) {
+ minVals[c] = (Float) 1.0E30;
+ maxVals[c] = (Float)-1.0E30;
+ }
+ for (y=0; y<height; y++) {
+ if (!readUByteRow (handle, bufPtr, nchan * width))
+ return FALSE;
+ if (findMinMax) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
+ if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
+ bufPtr++;
+ }
+ }
+ } else {
+ bufPtr += nchan * width;
+ }
+ }
+ if (verbose && findMinMax) {
+ printf ("\tMinimum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", (UByte)minVals[c]);
+ }
+ printf ("\n");
+ printf ("\tMaximum pixel values :");
+ for (c=0; c<nchan; c++) {
+ printf (" %d", (UByte)maxVals[c]);
+ }
+ printf ("\n");
+ fflush (stdout);
+ }
+ return TRUE;
+}
+
+static Boln remapFloatValues (Float *buf, Int width, Int height, Int nchan,
+ Float minVals[], Float maxVals[])
+{
+ Int x, y, c;
+ Float *bufPtr = buf;
+ Float m[MAXCHANS], t[MAXCHANS];
+
+ for (c=0; c<nchan; c++) {
+ m[c] = (1.0 - 0.0) / (maxVals[c] - minVals[c]);
+ t[c] = 0.0 - m[c] * minVals[c];
+ }
+ for (y=0; y<height; y++) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ *bufPtr = *bufPtr * m[c] + t[c];
+ if (*bufPtr < 0.0) *bufPtr = 0.0;
+ if (*bufPtr > 1.0) *bufPtr = 1.0;
+ bufPtr++;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static Boln remapUShortValues (UShort *buf, Int width, Int height, Int nchan,
+ Float minVals[], Float maxVals[])
+{
+ Int x, y, c;
+ UShort *bufPtr = buf;
+ Float m[MAXCHANS], t[MAXCHANS];
+
+ for (c=0; c<nchan; c++) {
+ m[c] = (65535.0 - 0.0) / (maxVals[c] - minVals[c]);
+ t[c] = 0.0 - m[c] * minVals[c];
+ }
+ for (y=0; y<height; y++) {
+ for (x=0; x<width; x++) {
+ for (c=0; c<nchan; c++) {
+ *bufPtr = *bufPtr * m[c] + t[c];
+ 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,
+ RAWHEADER *rawHeaderPtr);
+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 rawOptions[] = {
+ "-verbose", "-width", "-height", "-nchan", "-byteorder",
+ "-scanorder", "-pixeltype", "-min", "-max", "-gamma",
+ "-useheader", "-nomap", "-uuencode", NULL
+ };
+ int objc, length, i, index;
+ Tcl_Obj **objv;
+ const char *widthStr, *heightStr, *nchanStr, *verboseStr, *useheaderStr,
+ *byteOrderStr, *scanorderStr, *pixelTypeStr,
+ *minStr, *maxStr, *gammaStr, *nomapStr, *uuencodeStr;
+
+ /* Initialize format options with default values. */
+ verboseStr = "0";
+ byteOrderStr = isIntel ()? strIntel: strMotorola;
+ widthStr = "128";
+ heightStr = "128";
+ nchanStr = "1";
+ scanorderStr = strTopDown;
+ pixelTypeStr = strUByte;
+ minStr = "0.0";
+ maxStr = "0.0";
+ gammaStr = "1.0";
+ useheaderStr = "1";
+ nomapStr = "0";
+ uuencodeStr = "1";
+
+ 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 *)rawOptions,
+ "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:
+ widthStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 2:
+ heightStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 3:
+ nchanStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 4:
+ byteOrderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 5:
+ scanorderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 6:
+ pixelTypeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 7:
+ minStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 8:
+ maxStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 9:
+ gammaStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 10:
+ useheaderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 11:
+ nomapStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ case 12:
+ uuencodeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
+ break;
+ }
+ }
+ }
+
+ /* OPA TODO: Check for valid integer and float strings. */
+ opts->width = atoi (widthStr);
+ opts->height = atoi (heightStr);
+ opts->nchan = atoi (nchanStr);
+
+ opts->minVal = atof (minStr);
+ opts->maxVal = atof (maxStr);
+ opts->gamma = atof (gammaStr);
+
+ length = strlen (byteOrderStr);
+ if (!strncmp (byteOrderStr, strIntel, length)) {
+ opts->byteOrder = INTEL;
+ } else if (!strncmp (byteOrderStr, strMotorola, length)) {
+ opts->byteOrder = MOTOROLA;
+ } else {
+ Tcl_AppendResult (interp, "Invalid byteorder mode \"", byteOrderStr,
+ "\": Should be ", strIntel, " or ", strMotorola,
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ 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 (useheaderStr);
+ if (!strncmp (useheaderStr, "1", length) || \
+ !strncmp (useheaderStr, "true", length) || \
+ !strncmp (useheaderStr, "on", length)) {
+ opts->useHeader = 1;
+ } else if (!strncmp (useheaderStr, "0", length) || \
+ !strncmp (useheaderStr, "false", length) || \
+ !strncmp (useheaderStr, "off", length)) {
+ opts->useHeader = 0;
+ } else {
+ Tcl_AppendResult (interp, "invalid useheader mode \"", useheaderStr,
+ "\": 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;
+ }
+
+ length = strlen (scanorderStr);
+ if (!strncmp (scanorderStr, strTopDown, length)) {
+ opts->scanOrder = TOP_DOWN;
+ } else if (!strncmp (scanorderStr, strBottomUp, length)) {
+ opts->scanOrder = BOTTOM_UP;
+ } else {
+ Tcl_AppendResult (interp, "invalid scanline order \"", scanorderStr,
+ "\": should be TopDown or BottomUp",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ length = strlen (pixelTypeStr);
+ if (!strncmp (pixelTypeStr, strFloat, length)) {
+ opts->pixelType = TYPE_FLOAT;
+ } else if (!strncmp (pixelTypeStr, strUShort, length)) {
+ opts->pixelType = TYPE_USHORT;
+ } else if (!strncmp (pixelTypeStr, strUByte, length)) {
+ opts->pixelType = TYPE_UBYTE;
+ } else {
+ Tcl_AppendResult (interp, "invalid pixel type \"", pixelTypeStr,
+ "\": should be float, short or byte",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ length = strlen (uuencodeStr);
+ if (!strncmp (uuencodeStr, "1", length) || \
+ !strncmp (uuencodeStr, "true", length) || \
+ !strncmp (uuencodeStr, "on", length)) {
+ opts->uuencode = 1;
+ } else if (!strncmp (uuencodeStr, "0", length) || \
+ !strncmp (uuencodeStr, "false", length) || \
+ !strncmp (uuencodeStr, "off", length)) {
+ opts->uuencode = 0;
+ } else {
+ Tcl_AppendResult (interp, "invalid uuencode mode \"", uuencodeStr,
+ "\": 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;
+ FMTOPT opts;
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!opts.uuencode) {
+ handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length);
+ handle.state = IMG_STRING;
+ } else {
+ tkimg_ReadInit(data, 'M', &handle);
+ }
+ return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
+}
+
+static int CommonMatch (interp, handle, format, widthPtr, heightPtr, rawHeaderPtr)
+ Tcl_Interp *interp;
+ tkimg_MFile *handle;
+ Tcl_Obj *format;
+ int *widthPtr;
+ int *heightPtr;
+ RAWHEADER *rawHeaderPtr;
+{
+ RAWHEADER th;
+ FMTOPT opts;
+
+ initHeader (&th);
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (opts.useHeader) {
+ if (!readHeader (interp, handle, &th))
+ return 0;
+ } else {
+ th.width = opts.width;
+ th.height = opts.height;
+ th.nChans = opts.nchan;
+ th.pixelType = opts.pixelType;
+ th.scanOrder = opts.scanOrder;
+ th.byteOrder = opts.byteOrder;
+ }
+ *widthPtr = th.width;
+ *heightPtr = th.height;
+ if (rawHeaderPtr) {
+ *rawHeaderPtr = 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;
+ FMTOPT opts;
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!opts.uuencode) {
+ handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length);
+ handle.state = IMG_STRING;
+ } else {
+ tkimg_ReadInit(data, 'M', &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 x, y, c;
+ Int fileWidth = 0, fileHeight = 0;
+ Float minVals[MAXCHANS], maxVals[MAXCHANS];
+ int stopY, outY, outWidth, outHeight;
+ RAWFILE tf;
+ FMTOPT opts;
+ Boln swapBytes;
+ Boln fastMode;
+ Int byteOrder;
+ Int scanOrder;
+ Int pixelType;
+ Int matte = 0;
+ UByte *pixbufPtr;
+ Float *floatBufPtr;
+ UShort *ushortBufPtr;
+ UByte *ubyteBufPtr;
+ Float gtable[GTABSIZE];
+ int result = TCL_OK;
+
+ memset (&tf, 0, sizeof (RAWFILE));
+ initHeader (&tf.th);
+
+ CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th);
+
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ 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;
+ }
+
+ byteOrder = opts.useHeader? tf.th.byteOrder: opts.byteOrder;
+ scanOrder = opts.useHeader? tf.th.scanOrder: opts.scanOrder;
+ pixelType = opts.useHeader? tf.th.pixelType: opts.pixelType;
+ swapBytes = (( isIntel () && (byteOrder != INTEL)) ||
+ (!isIntel () && (byteOrder == INTEL)));
+ fastMode = (opts.nomap &&
+ pixelType == TYPE_UBYTE && scanOrder == TOP_DOWN &&
+ fileWidth == width && fileHeight == height);
+
+ if (!fastMode) {
+ gtableFloat (opts.gamma, gtable);
+ }
+
+ switch (pixelType) {
+ case TYPE_FLOAT: {
+ tf.floatBuf = (Float *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (Float));
+ readFloatFile (handle, tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
+ swapBytes, opts.verbose, !opts.nomap, minVals, maxVals);
+ break;
+ }
+ case TYPE_USHORT: {
+ tf.ushortBuf = (UShort *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UShort));
+ readUShortFile (handle, tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
+ swapBytes, opts.verbose, !opts.nomap, minVals, maxVals);
+ break;
+ }
+ case TYPE_UBYTE: {
+ tf.ubyteBuf = (UByte *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UByte));
+ readUByteFile (handle, tf.ubyteBuf, fileWidth, fileHeight, tf.th.nChans,
+ opts.verbose, !opts.nomap, minVals, maxVals);
+ break;
+ }
+ }
+ if (opts.nomap) {
+ for (c=0; c<tf.th.nChans; c++) {
+ minVals[c] = 0.0;
+ maxVals[c] = 255.0;
+ }
+ } else if (opts.minVal != 0.0 || opts.maxVal != 0.0) {
+ for (c=0; c<tf.th.nChans; c++) {
+ minVals[c] = opts.minVal;
+ maxVals[c] = opts.maxVal;
+ }
+ }
+ switch (pixelType) {
+ case TYPE_FLOAT: {
+ remapFloatValues (tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
+ minVals, maxVals);
+ break;
+ }
+ case TYPE_USHORT: {
+ remapUShortValues (tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
+ minVals, maxVals);
+ break;
+ }
+ }
+
+ if (tkimg_PhotoExpand (interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
+ rawClose (&tf, fastMode);
+ return TCL_ERROR;
+ }
+
+ if (fastMode) {
+ tf.pixbuf = tf.ubyteBuf;
+ } else {
+ tf.pixbuf = (UByte *) ckalloc (fileWidth * tf.th.nChans);
+ }
+
+ block.pixelSize = tf.th.nChans;
+ block.pitch = fileWidth * tf.th.nChans;
+ block.width = outWidth;
+ block.height = fastMode? outHeight: 1;
+ block.offset[0] = 0;
+ block.offset[1] = (tf.th.nChans > 1? 1: 0);
+ block.offset[2] = (tf.th.nChans > 1? 2: 0);
+ block.offset[3] = (tf.th.nChans == 4 && matte? 3: 0);
+ block.pixelPtr = tf.pixbuf + srcX * tf.th.nChans;
+
+ stopY = srcY + outHeight;
+ outY = destY;
+
+ if (fastMode) {
+ if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
+ width, height,
+ block.offset[3]?
+ TK_PHOTO_COMPOSITE_SET:
+ TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
+ result = TCL_ERROR;
+ }
+ } else {
+ for (y=0; y<stopY; y++) {
+ pixbufPtr = tf.pixbuf;
+ switch (pixelType) {
+ case TYPE_FLOAT: {
+ if (scanOrder == BOTTOM_UP) {
+ floatBufPtr = tf.floatBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
+ } else {
+ floatBufPtr = tf.floatBuf + y * fileWidth * tf.th.nChans;
+ }
+ FloatGammaUByte (fileWidth * tf.th.nChans, floatBufPtr,
+ opts.gamma != 1.0? gtable: NULL, pixbufPtr);
+ floatBufPtr += fileWidth * tf.th.nChans;
+ break;
+ }
+ case TYPE_USHORT: {
+ if (scanOrder == BOTTOM_UP) {
+ ushortBufPtr = tf.ushortBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
+ } else {
+ ushortBufPtr = tf.ushortBuf + y * fileWidth * tf.th.nChans;
+ }
+ UShortGammaUByte (fileWidth * tf.th.nChans, ushortBufPtr,
+ opts.gamma != 1.0? gtable: NULL, pixbufPtr);
+ ushortBufPtr += fileWidth * tf.th.nChans;
+ break;
+ }
+ case TYPE_UBYTE: {
+ if (scanOrder == BOTTOM_UP) {
+ ubyteBufPtr = tf.ubyteBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
+ } else {
+ ubyteBufPtr = tf.ubyteBuf + y * fileWidth * tf.th.nChans;
+ }
+ for (x=0; x<fileWidth * tf.th.nChans; x++) {
+ pixbufPtr[x] = ubyteBufPtr[x];
+ }
+ ubyteBufPtr += fileWidth * tf.th.nChans;
+ break;
+ }
+ }
+ if (y >= srcY) {
+ if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
+ width, 1,
+ block.offset[3]?
+ TK_PHOTO_COMPOSITE_SET:
+ TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
+ result = TCL_ERROR;
+ break;
+ }
+ outY++;
+ }
+ }
+ }
+ rawClose (&tf, fastMode);
+ return result;
+}
+
+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;
+{
+ Int x, y;
+ Int redOffset, greenOffset, blueOffset, alphaOffset;
+ UByte *pixelPtr, *rowPixPtr;
+ RAWFILE tf;
+ FMTOPT opts;
+ UByte *ubyteBufPtr;
+ Int bytesPerLine;
+
+ memset (&tf, 0, sizeof (RAWFILE));
+ if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ redOffset = 0;
+ greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
+ blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
+ alphaOffset = blockPtr->offset[0];
+
+ if (alphaOffset < blockPtr->offset[2]) {
+ alphaOffset = blockPtr->offset[2];
+ }
+ if (++alphaOffset < blockPtr->pixelSize) {
+ alphaOffset -= blockPtr->offset[0];
+ } else {
+ alphaOffset = 0;
+ }
+
+ initHeader (&tf.th);
+ tf.th.width = blockPtr->width;
+ tf.th.height = blockPtr->height;
+ tf.th.nChans = opts.nchan;
+ tf.th.scanOrder = opts.scanOrder;
+ tf.th.pixelType = TYPE_UBYTE;
+
+ writeHeader (handle, &tf.th);
+ bytesPerLine = blockPtr->width * tf.th.nChans * sizeof (UByte);
+ tf.ubyteBuf = (UByte *)ckalloc (bytesPerLine);
+
+ rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0];
+ for (y = 0; y < blockPtr->height; y++) {
+ ubyteBufPtr = tf.ubyteBuf;
+ pixelPtr = rowPixPtr;
+ if (tf.th.nChans == 1) {
+ for (x=0; x<blockPtr->width; x++) {
+ *ubyteBufPtr = pixelPtr[redOffset];
+ ubyteBufPtr++;
+ pixelPtr += blockPtr->pixelSize;
+ }
+ } else {
+ for (x=0; x<blockPtr->width; x++) {
+ *(ubyteBufPtr++) = pixelPtr[redOffset];
+ *(ubyteBufPtr++) = pixelPtr[greenOffset];
+ *(ubyteBufPtr++) = pixelPtr[blueOffset];
+ if (tf.th.nChans == 4) {
+ /* Have a matte channel and write it. */
+ *(ubyteBufPtr++) = pixelPtr[alphaOffset];
+ }
+ pixelPtr += blockPtr->pixelSize;
+ }
+ }
+ if (tkimg_Write (handle, (char *)tf.ubyteBuf, bytesPerLine) != bytesPerLine) {
+ rawClose (&tf, FALSE);
+ return TCL_ERROR;
+ }
+ rowPixPtr += blockPtr->pitch;
+ }
+ if (opts.verbose)
+ printImgInfo (&tf.th, &opts, filename, "Saving image:");
+ rawClose (&tf, FALSE);
+ return TCL_OK;
+}
+