diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 20:28:26 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 20:28:26 (GMT) |
commit | b184baa1234c2143e488d1796ae98afab118b891 (patch) | |
tree | 686b0a47b272296205c7fa2bc789f62a03d20df6 /tkimg/raw/raw.c | |
parent | f7560d0a451a793441216d76eb4d9475aab61740 (diff) | |
parent | 5aad878400425d3af44433a47c13824385689e1d (diff) | |
download | blt-b184baa1234c2143e488d1796ae98afab118b891.zip blt-b184baa1234c2143e488d1796ae98afab118b891.tar.gz blt-b184baa1234c2143e488d1796ae98afab118b891.tar.bz2 |
Merge commit '5aad878400425d3af44433a47c13824385689e1d' as 'tkimg'
Diffstat (limited to 'tkimg/raw/raw.c')
-rw-r--r-- | tkimg/raw/raw.c | 1494 |
1 files changed, 1494 insertions, 0 deletions
diff --git a/tkimg/raw/raw.c b/tkimg/raw/raw.c new file mode 100644 index 0000000..ceb5e71 --- /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: raw.c,v 1.1.1.1 2016/01/25 21:20:47 joye Exp $ + * + */ + +#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; +} + |