1/* STARTHEADER
2 *
3 * File :       raw.c
4 *
5 * Author :     Paul Obermeier (paul@poSoft.de)
6 *
7 * Date :       Wed Feb 21 12:45:08 CET 2001
8 *
9 * Copyright :  (C) 2001-2004 Paul Obermeier
10 *
11 * Description :
12 *
13 * A photo image handler for raw data interpreted as image files.
14 *
15 * The following image types are currently supported:
16 *
17 * Grayscale image:  1 channel  of 32-bit floating point   values.
18 * 		     1 channel  of 16-bit unsigned integer values.
19 * 		     1 channel  of  8-bit unsigned integer values.
20 * True-color image: 3 channels of 32-bit floating point   values.
21 * 		     3 channels of 16-bit unsigned integer values.
22 * 		     3 channels of  8-bit unsigned integer values.
23 *
24 * List of currently supported features:
25 *
26 * Type   |     Read      |     Write     |
27 *        | -file | -data | -file | -data |
28 * ----------------------------------------
29 * Gray   | Yes   | Yes   | Yes   | Yes   |
30 * RGB    | Yes   | Yes   | Yes   | Yes   |
31 *
32 * There are 2 supported file formats:
33 * One with the pure raw data only, the other with a 7 line ASCII header of the
34 * following form:
35 *
36 *     Magic=RAW\n		File format identifier. Fixed value.
37 *     Width=128\n		Image width in pixels.
38 *     Height=128\n		Image height in pixels.
39 *     NumChan=1\n		Possible values: 1 or 3.
40 *     ByteOrder=Intel\n	Possible values: "Intel" or "Motorola".
41 *     ScanOrder=TopDown\n	Possible values: "TopDown" or "BottomUp".
42 *     PixelType=byte\n		Possible values: "float", "short" or "byte".
43 *
44 * The following format options are available:
45 *
46 * For raw images with header:
47 * Read  RAW image: "raw -useheader true -verbose <bool> -gamma <float>
48 *			 -min <float> -max <float>"
49 * Write RAW image: "raw -useheader true -verbose <bool> -nchan <int>
50 *                       -scanorder <string>"
51 *
52 * For raw images without header:
53 * Read  RAW image: "raw -useheader false -verbose <bool> -nchan <int>
54 *                       -scanorder <string> -byteorder <string>
55 *                       -width <int> -height <int> -gamma <float>
56 *			 -pixeltype <string> -min <float> -max <float>
57 *                       -uuencode <bool>"
58 * Write RAW image: "raw -useheader false -verbose <bool> -nchan <int>
59 *                       -scanorder <string>"
60 *
61 * -verbose <bool>:     If set to true, additional information about the file
62 *                      format is printed to stdout. Default is false.
63 * -useheader <bool>:   If set to true, use file header information for reading
64 *                      and writing. Default is true.
65 * -nchan <int>:        Specify the number of channels of the input image.
66 *			Only valid, if reading image data without header.
67 *			Default is 1.
68 * -width <int>:        Specify the width of the input image. Only valid, if
69 *                      reading image data without header. Default is 128.
70 * -height <int>:       Specify the height of the input image. Only valid, if
71 *                      reading image data without header. Default is 128.
72 * -byteorder <string>: Specify the byteorder of the input image. Only valid, if
73 *                      reading image data without header.
74 *			Possible values: "Intel" or "Motorola".
75 *			Default is assuming the same byteorder as that of the
76 *			host computer.
77 * -scanorder <string>: Specify the scanline order of the input image. Only
78 *			valid, if reading image data without header.
79 *			Possible values: "TopDown" or "BottomUp".
80 *			Default is "TopDown".
81 * -pixeltype <string>: Specify the type of the pixel values.
82 *			Only valid, if reading image data without header.
83 *			Possible values: "float", "short" or "byte".
84 *			Default is "byte".
85 * -gamma <float>:      Specify a gamma correction to be applied when mapping
86 *			the input data to 8-bit image values.
87 *                      Default is 1.0.
88 * -nomap <bool>:       If set to true, no mapping of input values is done.
89 * 			Use this option, if your image already contains RGB
90 * 			values in the range of 0 ..255.
91 *                      Default is false.
92 * -uuencode <bool>:    If set to false, do not assume, that the image data stored in a
93 *                      variable is uuencoded. Default is true, i.e. the image data is
94 *                      assumed to be uuencoded.
95 * -min <float>:        Specify the minimum pixel value to be used for mapping
96 *			the input data to 8-bit image values.
97 *                      Default is the minimum value found in the image data.
98 * -max <float>:        Specify the maximum pixel value to be used for mapping
99 *			the input data to 8-bit image values.
100 *                      Default is the maximum value found in the image data.
101 *
102 * Notes:
103 * 			Currently RAW files are only written in "byte" pixel format.
104 *
105 * ENDHEADER
106 *
107 * $Id$
108 *
109 */
110
111#include <stdlib.h>
112#include <math.h>
113
114/*
115 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
116 */
117
118#include "init.c"
119
120/*
121#define DEBUG_LOCAL
122*/
123
124/* Maximum length of a header line. */
125#define HEADLEN 100
126
127/* Header fields. */
128#define strMagic     "Magic=%s\n"
129#define strWidth     "Width=%d\n"
130#define strHeight    "Height=%d\n"
131#define strNumChan   "NumChan=%d\n"
132#define strByteOrder "ByteOrder=%s\n"
133#define strScanOrder "ScanOrder=%s\n"
134#define strPixelType "PixelType=%s\n"
135
136/* Header fields possible values. */
137#define strIntel    "Intel"
138#define strMotorola "Motorola"
139#define strTopDown  "TopDown"
140#define strBottomUp "BottomUp"
141#define strFloat    "float"
142#define strUShort   "short"
143#define strUByte    "byte"
144
145#define strUnknown  "Unknown"
146
147#define BOTTOM_UP   0
148#define TOP_DOWN    1
149#define INTEL       0
150#define MOTOROLA    1
151#define TYPE_FLOAT  0
152#define TYPE_USHORT 1
153#define TYPE_UBYTE  2
154
155#define MAXCHANS  4
156
157/* Some general defines and typedefs. */
158#define TRUE  1
159#define FALSE 0
160#define MIN(a,b) ((a)<(b)? (a): (b))
161#define MAX(a,b) ((a)>(b)? (a): (b))
162
163typedef unsigned char Boln;	/* Boolean value: TRUE or FALSE */
164typedef unsigned char UByte;	/* Unsigned  8 bit integer */
165typedef char  Byte;		/* Signed    8 bit integer */
166typedef unsigned short UShort;	/* Unsigned 16 bit integer */
167typedef short Short;		/* Signed   16 bit integer */
168typedef int UInt;		/* Unsigned 32 bit integer */
169typedef int Int;		/* Signed   32 bit integer */
170typedef float Float;		/* IEEE     32 bit floating point */
171typedef double Double;		/* IEEE     64 bit floating point */
172
173/* RAW file header structure */
174typedef struct {
175    char  id[3];
176    Int   nChans;
177    Int   width;
178    Int   height;
179    Int   scanOrder;
180    Int   byteOrder;
181    Int   pixelType;
182} RAWHEADER;
183
184/* RAW file format options structure for use with ParseFormatOpts */
185typedef struct {
186    Int   width;
187    Int   height;
188    Int   nchan;
189    Int   scanOrder;
190    Int   byteOrder;
191    Int   pixelType;
192    Float minVal;
193    Float maxVal;
194    Float gamma;
195    Boln  nomap;
196    Boln  verbose;
197    Boln  uuencode;
198    Boln  useHeader;
199} FMTOPT;
200
201/* Structure to hold information about the Targa file being processed. */
202typedef struct {
203    RAWHEADER th;
204    UByte  *pixbuf;
205    Float  *floatBuf;
206    UShort *ushortBuf;
207    UByte  *ubyteBuf;
208} RAWFILE;
209
210#define MINGAMMA 0.01
211#define MAXGAMMA 100.0
212#define GTABSIZE 257
213
214/* Given a pixel value in Float format, "valIn", and a gamma-correction
215 * lookup table, "tab", macro "gcorrectFloat" returns the gamma-corrected
216 * pixel value in "valOut".
217 */
218
219#define gcorrectFloat(valIn,tab,valOut)                                 \
220    {                                                                   \
221        Int     gc_i;                                                   \
222        Float   gc_t;                                                   \
223        gc_t = (valIn) * (GTABSIZE - 2);                                \
224        gc_i = gc_t;                                                    \
225        gc_t -= gc_i;                                                   \
226        (valOut) = (tab)[gc_i] * (1.0-gc_t) + (tab)[gc_i+1] * gc_t;     \
227    }
228
229static Boln gtableFloat (Float gamma, Float table[])
230{
231    Int i;
232
233    if (gamma < MINGAMMA || gamma > MAXGAMMA) {
234        printf ("Invalid gamma value %f\n", gamma);
235        return FALSE;
236    }
237    for (i = 0; i < GTABSIZE - 1; ++i) {
238        table[i] = pow ((Float) i / (Float) (GTABSIZE - 2), 1.0 / gamma);
239    }
240    table[GTABSIZE - 1] = 1.0;
241    return TRUE;
242}
243
244/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for
245 * parameter gtable.
246 */
247static void FloatGammaUByte (Int n, const Float floatIn[],
248                             const Float gtable[], UByte ubOut[])
249{
250    const Float *src, *stop;
251    Float       ftmp;
252    Int         itmp;
253    UByte       *ubdest;
254
255    src = floatIn;
256    stop = floatIn + n;
257    ubdest = ubOut;
258
259    /* Handle a gamma value of 1.0 (gtable == NULL) as a special case.
260       Quite nice speed improvement for the maybe most used case. */
261    if (gtable) {
262	while (src < stop) {
263	    ftmp = MAX (0.0, MIN (*src, 1.0));
264	    gcorrectFloat (ftmp, gtable, ftmp);
265	    itmp = (Int)(ftmp * 255.0 + 0.5);
266	    *ubdest = MAX (0, MIN (itmp, 255));
267	    ++ubdest;
268	    ++src;
269	}
270    } else {
271	while (src < stop) {
272	    itmp = (Int)(*src * 255.0 + 0.5);
273	    *ubdest = MAX (0, MIN (itmp, 255));
274	    ++ubdest;
275	    ++src;
276	}
277    }
278    return;
279}
280
281/* If no gamma correction is needed (i.e. gamma == 1.0), specify NULL for
282 * parameter gtable.
283 */
284static void UShortGammaUByte (Int n, const UShort shortIn[],
285                              const Float gtable[], UByte ubOut[])
286{
287    const UShort *src, *stop;
288    Float        ftmp;
289    Int          itmp;
290    UByte        *ubdest;
291
292    src = shortIn;
293    stop = shortIn + n;
294    ubdest = ubOut;
295
296    /* Handle a gamma value of 1.0 (gtable == NULL) as a special case.
297       Quite nice speed improvement for the maybe most used case. */
298    if (gtable) {
299	while (src < stop) {
300	    ftmp = *src / 65535.0;
301	    ftmp = MAX (0.0, MIN (ftmp, 1.0));
302	    gcorrectFloat (ftmp, gtable, ftmp);
303	    itmp = (Int)(ftmp * 255.0 + 0.5);
304	    *ubdest = MAX (0, MIN (itmp, 255));
305	    ++ubdest;
306	    ++src;
307	}
308    } else {
309	while (src < stop) {
310	    itmp = (Int)(*src / 256);
311	    *ubdest = MAX (0, MIN (itmp, 255));
312	    ++ubdest;
313	    ++src;
314	}
315    }
316    return;
317}
318
319/* This function determines at runtime, whether we are on an Intel system. */
320
321static int isIntel (void)
322{
323    unsigned long val = 513;
324    /* On Intel (little-endian) systems this value is equal to "\01\02\00\00".
325       On big-endian systems this value equals "\00\00\02\01" */
326    return memcmp(&val, "\01\02", 2) == 0;
327}
328
329static void rawClose (RAWFILE *tf)
330{
331    if (tf->pixbuf)    ckfree ((char *)tf->pixbuf);
332    if (tf->floatBuf)  ckfree ((char *)tf->floatBuf);
333    if (tf->ushortBuf) ckfree ((char *)tf->ushortBuf);
334    if (tf->ubyteBuf)  ckfree ((char *)tf->ubyteBuf);
335    return;
336}
337
338static Boln readFloatRow (tkimg_MFile *handle, Float *pixels, Int nFloats,
339                          char *buf, Boln swapBytes)
340{
341    int   i;
342    Float *mPtr = pixels;
343    char  *bufPtr = buf;
344
345#ifdef DEBUG_LOCAL
346	printf ("Reading %d floats\n", nFloats); fflush (stdout);
347#endif
348    if (4 * nFloats != tkimg_Read (handle, buf, 4 * nFloats))
349        return FALSE;
350
351    if (swapBytes) {
352	for (i=0; i<nFloats; i++) {
353	    ((char *)mPtr)[0] = bufPtr[3];
354	    ((char *)mPtr)[1] = bufPtr[2];
355	    ((char *)mPtr)[2] = bufPtr[1];
356	    ((char *)mPtr)[3] = bufPtr[0];
357	    mPtr++;
358	    bufPtr += 4;
359	}
360    } else {
361	for (i=0; i<nFloats; i++) {
362	    ((char *)mPtr)[0] = bufPtr[0];
363	    ((char *)mPtr)[1] = bufPtr[1];
364	    ((char *)mPtr)[2] = bufPtr[2];
365	    ((char *)mPtr)[3] = bufPtr[3];
366	    mPtr++;
367	    bufPtr += 4;
368	}
369    }
370    return TRUE;
371}
372
373static Boln readUShortRow (tkimg_MFile *handle, UShort *pixels, Int nShorts,
374                           char *buf, Boln swapBytes)
375{
376    int    i;
377    UShort *mPtr = pixels;
378    char   *bufPtr = buf;
379
380#ifdef DEBUG_LOCAL
381	printf ("Reading %d UShorts\n", nShorts); fflush (stdout);
382#endif
383    if (2 * nShorts != tkimg_Read (handle, buf, 2 * nShorts))
384        return FALSE;
385
386    if (swapBytes) {
387	for (i=0; i<nShorts; i++) {
388	    ((char *)mPtr)[0] = bufPtr[1];
389	    ((char *)mPtr)[1] = bufPtr[0];
390	    mPtr++;
391	    bufPtr += 2;
392	}
393    } else {
394	for (i=0; i<nShorts; i++) {
395	    ((char *)mPtr)[0] = bufPtr[0];
396	    ((char *)mPtr)[1] = bufPtr[1];
397	    mPtr++;
398	    bufPtr += 2;
399	}
400    }
401    return TRUE;
402}
403
404static Boln readUByteRow (tkimg_MFile *handle, UByte *pixels, Int nBytes,
405                          char *buf, Boln swapBytes)
406{
407    int    i;
408    UByte *mPtr = pixels;
409    char   *bufPtr = buf;
410
411#ifdef DEBUG_LOCAL
412	printf ("Reading %d UBytes\n", nBytes); fflush (stdout);
413#endif
414    if (nBytes != tkimg_Read (handle, buf, nBytes))
415        return FALSE;
416
417    for (i=0; i<nBytes; i++) {
418	((char *)mPtr)[0] = bufPtr[0];
419	mPtr++;
420	bufPtr += 1;
421    }
422    return TRUE;
423}
424
425#define OUT Tcl_WriteChars (outChan, str, -1)
426static void printImgInfo (RAWHEADER *th, FMTOPT *opts,
427                          const char *filename, const char *msg)
428{
429    Tcl_Channel outChan;
430    char str[256];
431
432    outChan = Tcl_GetStdChannel (TCL_STDOUT);
433    if (!outChan) {
434        return;
435    }
436    sprintf (str, "%s %s\n", msg, filename);                                                  OUT;
437    sprintf (str, "\tSize in pixel    : %d x %d\n", th->width, th->height);                   OUT;
438    sprintf (str, "\tNo. of channels  : %d\n", th->nChans);                                   OUT;
439    sprintf (str, "\tPixel type       : %s\n", (th->pixelType == TYPE_FLOAT?  strFloat:
440                                               (th->pixelType == TYPE_USHORT? strUShort:
441                                               (th->pixelType == TYPE_UBYTE?  strUByte:
442                                                                              strUnknown)))); OUT;
443    sprintf (str, "\tVertical encoding: %s\n", th->scanOrder == TOP_DOWN?
444                                               strTopDown: strBottomUp);                      OUT;
445    sprintf (str, "\tGamma correction : %f\n", opts->gamma);                                  OUT;
446    sprintf (str, "\tMinimum map value: %f\n", opts->minVal);                                 OUT;
447    sprintf (str, "\tMaximum map value: %f\n", opts->maxVal);                                 OUT;
448    sprintf (str, "\tHost byte order  : %s\n", isIntel ()?  strIntel: strMotorola);           OUT;
449    sprintf (str, "\tFile byte order  : %s\n", th->byteOrder == INTEL?
450                                               strIntel: strMotorola);                        OUT;
451    Tcl_Flush (outChan);
452}
453#undef OUT
454
455static Boln readHeaderLine (Tcl_Interp *interp, tkimg_MFile *handle, char *buf)
456{
457    char c, *bufPtr, *bufEndPtr;
458    Boln failure;
459
460    bufPtr    = buf;
461    bufEndPtr = buf + HEADLEN;
462    failure   = TRUE;
463
464#ifdef DEBUG_LOCAL
465	printf ("readHeaderLine\n"); fflush (stdout);
466#endif
467
468    while (tkimg_Read (handle, &c, 1) == 1 && bufPtr < bufEndPtr) {
469	if (c == '\n') {
470	    *bufPtr = '\0';
471	    failure = FALSE;
472 	    break;
473        }
474	*bufPtr = c;
475	bufPtr++;
476    }
477    if (failure) {
478	Tcl_AppendResult (interp, "RAW handler: Error reading header line (",
479                          buf, ")\n", NULL);
480 	return FALSE;
481    }
482    return TRUE;
483}
484
485static Boln readHeader (Tcl_Interp *interp, tkimg_MFile *handle, RAWHEADER *th)
486{
487    char buf[HEADLEN];
488    char tmpStr[HEADLEN];
489
490    if (!readHeaderLine (interp, handle, buf) ||
491        (1 != sscanf (buf, strMagic, th->id))) {
492	Tcl_AppendResult (interp, "Unable to parse header field Magic\n", NULL);
493	return FALSE;
494    }
495    if (strcmp (th->id, "RAW") != 0) {
496	Tcl_AppendResult (interp, "Invalid value for header field Magic:",
497                                  "Must be \"RAW\"\n", NULL);
498	return FALSE;
499    }
500
501    if (!readHeaderLine (interp, handle, buf) ||
502        (1 != sscanf (buf, strWidth, &th->width))) {
503	Tcl_AppendResult (interp, "Unable to parse header field Width\n", NULL);
504	return FALSE;
505    }
506    if (th->width < 1) {
507	Tcl_AppendResult (interp, "Invalid value for header field Width:",
508                                  "Must be greater than zero\n", NULL);
509	return FALSE;
510    }
511
512    if (!readHeaderLine (interp, handle, buf) ||
513        (1 != sscanf (buf, strHeight, &th->height))) {
514	Tcl_AppendResult (interp, "Unable to parse header field Height\n", NULL);
515	return FALSE;
516    }
517    if (th->height < 1) {
518	Tcl_AppendResult (interp, "Invalid value for header field Height:",
519                                  "Must be greater than zero\n", NULL);
520	return FALSE;
521    }
522
523    if (!readHeaderLine (interp, handle, buf) ||
524        (1 != sscanf (buf, strNumChan, &th->nChans))) {
525	Tcl_AppendResult (interp, "Unable to parse header field NumChan\n", NULL);
526	return FALSE;
527    }
528    if (th->nChans != 1 && th->nChans != 3) {
529	Tcl_AppendResult (interp, "Invalid value for header field NumChan:",
530                                  "Must be 1 or 3\n", NULL);
531	return FALSE;
532    }
533
534    if (!readHeaderLine (interp, handle, buf) ||
535        (1 != sscanf (buf, strByteOrder, tmpStr))) {
536	Tcl_AppendResult (interp, "Unable to parse header field ByteOrder\n", NULL);
537	return FALSE;
538    }
539
540    if (strcmp (tmpStr, strIntel) == 0) {
541	th->byteOrder = INTEL;
542    } else if (strcmp (tmpStr, strMotorola) == 0) {
543	th->byteOrder = MOTOROLA;
544    } else {
545	Tcl_AppendResult (interp, "Invalid value for header field ByteOrder:",
546                                  "Must be ", strIntel, " or ", strMotorola,
547				  "\n", NULL);
548	return FALSE;
549    }
550
551    if (!readHeaderLine (interp, handle, buf) ||
552        (1 != sscanf (buf, strScanOrder, tmpStr))) {
553	Tcl_AppendResult (interp, "Unable to parse header field ScanOrder\n", NULL);
554	return FALSE;
555    }
556    if (strcmp (tmpStr, strTopDown) == 0) {
557	th->scanOrder = TOP_DOWN;
558    } else if (strcmp (tmpStr, strBottomUp) == 0) {
559	th->scanOrder = BOTTOM_UP;
560    } else {
561	Tcl_AppendResult (interp, "Invalid value for header field ScanOrder:",
562                                  "Must be ", strTopDown, " or ", strBottomUp,
563                                  "\n", NULL);
564	return FALSE;
565    }
566
567    if (!readHeaderLine (interp, handle, buf) ||
568        (1 != sscanf (buf, strPixelType, tmpStr))) {
569	Tcl_AppendResult (interp, "Unable to parse header field PixelType\n", NULL);
570	return FALSE;
571    }
572    if (strcmp (tmpStr, strFloat) == 0) {
573	th->pixelType = TYPE_FLOAT;
574    } else if (strcmp (tmpStr, strUShort) == 0) {
575	th->pixelType = TYPE_USHORT;
576    } else if (strcmp (tmpStr, strUByte) == 0) {
577	th->pixelType = TYPE_UBYTE;
578    } else {
579	Tcl_AppendResult (interp, "Invalid value for header field PixelType:",
580                                  "Must be ", strFloat, ",", strUShort, " or ", strUByte,
581                                  "\n", NULL);
582	return FALSE;
583    }
584
585    return TRUE;
586}
587
588static Boln writeHeader (tkimg_MFile *handle, RAWHEADER *th)
589{
590    char buf[1024];
591
592    sprintf (buf, strMagic, "RAW");
593    tkimg_Write (handle, buf, strlen (buf));
594    sprintf (buf, strWidth, th->width);
595    tkimg_Write (handle, buf, strlen (buf));
596    sprintf (buf, strHeight, th->height);
597    tkimg_Write (handle, buf, strlen (buf));
598    sprintf (buf, strNumChan, th->nChans);
599    tkimg_Write (handle, buf, strlen (buf));
600    sprintf (buf, strByteOrder, isIntel()? strIntel: strMotorola);
601    tkimg_Write (handle, buf, strlen (buf));
602    sprintf (buf, strScanOrder, th->scanOrder == TOP_DOWN?
603                                strTopDown: strBottomUp);
604    tkimg_Write (handle, buf, strlen (buf));
605    sprintf (buf, strPixelType, (th->pixelType == TYPE_FLOAT?  strFloat:
606				(th->pixelType == TYPE_USHORT? strUShort:
607				(th->pixelType == TYPE_UBYTE?  strUByte:
608			                                       strUnknown))));
609    tkimg_Write (handle, buf, strlen (buf));
610    return TRUE;
611}
612
613static void initHeader (RAWHEADER *th)
614{
615    th->id[0]     = 'R';
616    th->id[1]     = 'A';
617    th->id[2]     = 'W';
618    th->nChans    = 1;
619    th->width     = 128;
620    th->height    = 128;
621    th->scanOrder = TOP_DOWN;
622    th->byteOrder = INTEL;
623    th->pixelType = TYPE_UBYTE;
624    return;
625}
626
627static Boln readFloatFile (tkimg_MFile *handle, Float *buf, Int width, Int height,
628                           Int nchan, Boln swapBytes, Boln verbose,
629                           Float minVals[], Float maxVals[])
630{
631    Int x, y, c;
632    Float *bufPtr = buf;
633    char  *line;
634
635#ifdef DEBUG_LOCAL
636	printf ("readFloatFile: Width=%d Height=%d nchan=%d swapBytes=%s\n",
637                 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout);
638#endif
639    for (c=0; c<nchan; c++) {
640	minVals[c] =  (Float)1.0E30;
641	maxVals[c] = (Float)-1.0E30;
642    }
643    line = ckalloc (sizeof (Float) * nchan * width);
644
645    for (y=0; y<height; y++) {
646	if (!readFloatRow (handle, bufPtr, nchan * width, line, swapBytes))
647	    return FALSE;
648	for (x=0; x<width; x++) {
649	    for (c=0; c<nchan; c++) {
650		if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
651		if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
652		bufPtr++;
653	    }
654	}
655    }
656    if (verbose) {
657	printf ("\tMinimum pixel values :");
658	for (c=0; c<nchan; c++) {
659	    printf (" %f", minVals[c]);
660	}
661	printf ("\n");
662	printf ("\tMaximum pixel values :");
663	for (c=0; c<nchan; c++) {
664	    printf (" %f", maxVals[c]);
665	}
666	printf ("\n");
667	fflush (stdout);
668    }
669    ckfree (line);
670    return TRUE;
671}
672
673static Boln readUShortFile (tkimg_MFile *handle, UShort *buf, Int width, Int height,
674                            Int nchan, Boln swapBytes, Boln verbose,
675                            Float minVals[], Float maxVals[])
676{
677    Int    x, y, c;
678    UShort *bufPtr = buf;
679    char   *line;
680
681#ifdef DEBUG_LOCAL
682	printf ("readUShortFile: Width=%d Height=%d nchan=%d swapBytes=%s\n",
683                 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout);
684#endif
685    for (c=0; c<nchan; c++) {
686	minVals[c] =  (Float)1.0E30;
687	maxVals[c] = (Float)-1.0E30;
688    }
689    line = ckalloc (sizeof (UShort) * nchan * width);
690
691    for (y=0; y<height; y++) {
692	if (!readUShortRow (handle, bufPtr, nchan * width, line, swapBytes))
693	    return FALSE;
694	for (x=0; x<width; x++) {
695	    for (c=0; c<nchan; c++) {
696		if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
697		if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
698		bufPtr++;
699	    }
700	}
701    }
702    if (verbose) {
703	printf ("\tMinimum pixel values :");
704	for (c=0; c<nchan; c++) {
705	    printf (" %d", (UShort)minVals[c]);
706	}
707	printf ("\n");
708	printf ("\tMaximum pixel values :");
709	for (c=0; c<nchan; c++) {
710	    printf (" %d", (UShort)maxVals[c]);
711	}
712	printf ("\n");
713	fflush (stdout);
714    }
715    ckfree (line);
716    return TRUE;
717}
718
719static Boln readUByteFile (tkimg_MFile *handle, UByte *buf, Int width, Int height,
720                           Int nchan, Boln swapBytes, Boln verbose,
721                           Float minVals[], Float maxVals[])
722{
723    Int   x, y, c;
724    UByte *bufPtr = buf;
725    char  *line;
726
727#ifdef DEBUG_LOCAL
728	printf ("readUByteFile: Width=%d Height=%d nchan=%d swapBytes=%s\n",
729                 width, height, nchan, swapBytes? "yes": "no"); fflush (stdout);
730#endif
731    for (c=0; c<nchan; c++) {
732	minVals[c] =  (Float)1.0E30;
733	maxVals[c] = (Float)-1.0E30;
734    }
735    line = ckalloc (sizeof (UByte) * nchan * width);
736
737    for (y=0; y<height; y++) {
738	if (!readUByteRow (handle, bufPtr, nchan * width, line, swapBytes))
739	    return FALSE;
740	for (x=0; x<width; x++) {
741	    for (c=0; c<nchan; c++) {
742		if (*bufPtr > maxVals[c]) maxVals[c] = *bufPtr;
743		if (*bufPtr < minVals[c]) minVals[c] = *bufPtr;
744		bufPtr++;
745	    }
746	}
747    }
748    if (verbose) {
749	printf ("\tMinimum pixel values :");
750	for (c=0; c<nchan; c++) {
751	    printf (" %d", (UByte)minVals[c]);
752	}
753	printf ("\n");
754	printf ("\tMaximum pixel values :");
755	for (c=0; c<nchan; c++) {
756	    printf (" %d", (UByte)maxVals[c]);
757	}
758	printf ("\n");
759	fflush (stdout);
760    }
761    ckfree (line);
762    return TRUE;
763}
764
765static Boln remapFloatValues (Float *buf, Int width, Int height, Int nchan,
766                              Float minVals[], Float maxVals[])
767{
768    Int x, y, c;
769    Float *bufPtr = buf;
770    Float m[MAXCHANS], t[MAXCHANS];
771
772    for (c=0; c<nchan; c++) {
773	m[c] = (1.0 - 0.0) / (maxVals[c] - minVals[c]);
774	t[c] = 0.0 - m[c] * minVals[c];
775    }
776    for (y=0; y<height; y++) {
777	for (x=0; x<width; x++) {
778	    for (c=0; c<nchan; c++) {
779		*bufPtr = *bufPtr * m[c] + t[c];
780		if (*bufPtr < 0.0) *bufPtr = 0.0;
781		if (*bufPtr > 1.0) *bufPtr = 1.0;
782	        bufPtr++;
783	    }
784	}
785    }
786    return TRUE;
787}
788
789static Boln remapUShortValues (UShort *buf, Int width, Int height, Int nchan,
790                               Float minVals[], Float maxVals[])
791{
792    Int x, y, c;
793    UShort *bufPtr = buf;
794    Float m[MAXCHANS], t[MAXCHANS];
795
796    for (c=0; c<nchan; c++) {
797	m[c] = (65535.0 - 0.0) / (maxVals[c] - minVals[c]);
798	t[c] = 0.0 - m[c] * minVals[c];
799    }
800    for (y=0; y<height; y++) {
801	for (x=0; x<width; x++) {
802	    for (c=0; c<nchan; c++) {
803		*bufPtr = *bufPtr * m[c] + t[c];
804	        bufPtr++;
805	    }
806	}
807    }
808    return TRUE;
809}
810
811/*
812 * Here is the start of the standard functions needed for every image format.
813 */
814
815/*
816 * Prototypes for local procedures defined in this file:
817 */
818
819static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
820	FMTOPT *opts);
821static int CommonMatch(Tcl_Interp *interp, tkimg_MFile *handle,
822	Tcl_Obj *format, int *widthPtr, int *heightPtr,
823	RAWHEADER *rawHeaderPtr);
824static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
825	const char *filename, Tcl_Obj *format,
826	Tk_PhotoHandle imageHandle, int destX, int destY,
827	int width, int height, int srcX, int srcY);
828static int CommonWrite(Tcl_Interp *interp,
829	const char *filename, Tcl_Obj *format,
830	tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
831
832static int ParseFormatOpts (interp, format, opts)
833    Tcl_Interp *interp;
834    Tcl_Obj *format;
835    FMTOPT *opts;
836{
837    static const char *const rawOptions[] = {
838         "-verbose", "-width", "-height", "-nchan", "-byteorder",
839         "-scanorder", "-pixeltype", "-min", "-max", "-gamma",
840         "-useheader", "-nomap", "-uuencode"
841    };
842    int objc, length, c, i, index;
843    Tcl_Obj **objv;
844    const char *widthStr, *heightStr, *nchanStr, *verboseStr, *useheaderStr,
845         *byteOrderStr, *scanorderStr, *pixelTypeStr,
846         *minStr, *maxStr, *gammaStr, *nomapStr, *uuencodeStr;
847
848    /* Initialize format options with default values. */
849    verboseStr   = "0";
850    byteOrderStr = isIntel ()? strIntel: strMotorola;
851    widthStr     = "128";
852    heightStr    = "128";
853    nchanStr     = "1";
854    scanorderStr = strTopDown;
855    pixelTypeStr = strUByte;
856    minStr       = "0.0";
857    maxStr       = "0.0";
858    gammaStr     = "1.0";
859    useheaderStr = "1";
860    nomapStr     = "0";
861    uuencodeStr  = "1";
862
863    if (tkimg_ListObjGetElements (interp, format, &objc, &objv) != TCL_OK)
864	return TCL_ERROR;
865    if (objc) {
866	for (i=1; i<objc; i++) {
867	    if (Tcl_GetIndexFromObj (interp, objv[i], (CONST84 char *CONST86 *)rawOptions,
868		    "format option", 0, &index) != TCL_OK) {
869		return TCL_ERROR;
870	    }
871	    if (++i >= objc) {
872		Tcl_AppendResult (interp, "No value for option \"",
873			Tcl_GetStringFromObj (objv[--i], (int *) NULL),
874			"\"", (char *) NULL);
875		return TCL_ERROR;
876	    }
877	    switch(index) {
878		case 0:
879		    verboseStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
880		    break;
881		case 1:
882		    widthStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
883		    break;
884		case 2:
885		    heightStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
886		    break;
887		case 3:
888		    nchanStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
889		    break;
890		case 4:
891		    byteOrderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
892		    break;
893		case 5:
894		    scanorderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
895		    break;
896		case 6:
897		    pixelTypeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
898		    break;
899		case 7:
900		    minStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
901		    break;
902		case 8:
903		    maxStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
904		    break;
905		case 9:
906		    gammaStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
907		    break;
908		case 10:
909		    useheaderStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
910		    break;
911		case 11:
912		    nomapStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
913		    break;
914		case 12:
915		    uuencodeStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
916		    break;
917	    }
918	}
919    }
920
921    /* OPA TODO: Check for valid integer and float strings. */
922    opts->width  = atoi (widthStr);
923    opts->height = atoi (heightStr);
924    opts->nchan  = atoi (nchanStr);
925
926    opts->minVal = atof (minStr);
927    opts->maxVal = atof (maxStr);
928    opts->gamma  = atof (gammaStr);
929
930    c = byteOrderStr[0]; length = strlen (byteOrderStr);
931    if (!strncmp (byteOrderStr, strIntel, length)) {
932	opts->byteOrder = INTEL;
933    } else if (!strncmp (byteOrderStr, strMotorola, length)) {
934	opts->byteOrder = MOTOROLA;
935    } else {
936	Tcl_AppendResult (interp, "Invalid byteorder mode \"", byteOrderStr,
937			  "\": Should be ", strIntel, " or ", strMotorola,
938			  (char *) NULL);
939	return TCL_ERROR;
940    }
941
942    c = verboseStr[0]; length = strlen (verboseStr);
943    if (!strncmp (verboseStr, "1", length) || \
944	!strncmp (verboseStr, "true", length) || \
945	!strncmp (verboseStr, "on", length)) {
946	opts->verbose = 1;
947    } else if (!strncmp (verboseStr, "0", length) || \
948	!strncmp (verboseStr, "false", length) || \
949	!strncmp (verboseStr, "off", length)) {
950	opts->verbose = 0;
951    } else {
952	Tcl_AppendResult (interp, "invalid verbose mode \"", verboseStr,
953			  "\": should be 1 or 0, on or off, true or false",
954			  (char *) NULL);
955	return TCL_ERROR;
956    }
957
958    c = useheaderStr[0]; length = strlen (useheaderStr);
959    if (!strncmp (useheaderStr, "1", length) || \
960	!strncmp (useheaderStr, "true", length) || \
961	!strncmp (useheaderStr, "on", length)) {
962	opts->useHeader = 1;
963    } else if (!strncmp (useheaderStr, "0", length) || \
964	!strncmp (useheaderStr, "false", length) || \
965	!strncmp (useheaderStr, "off", length)) {
966	opts->useHeader = 0;
967    } else {
968	Tcl_AppendResult (interp, "invalid useheader mode \"", useheaderStr,
969			  "\": should be 1 or 0, on or off, true or false",
970			  (char *) NULL);
971	return TCL_ERROR;
972    }
973
974    c = nomapStr[0]; length = strlen (nomapStr);
975    if (!strncmp (nomapStr, "1", length) || \
976	!strncmp (nomapStr, "true", length) || \
977	!strncmp (nomapStr, "on", length)) {
978	opts->nomap = 1;
979    } else if (!strncmp (nomapStr, "0", length) || \
980	!strncmp (nomapStr, "false", length) || \
981	!strncmp (nomapStr, "off", length)) {
982	opts->nomap = 0;
983    } else {
984	Tcl_AppendResult (interp, "invalid nomap mode \"", nomapStr,
985			  "\": should be 1 or 0, on or off, true or false",
986			  (char *) NULL);
987	return TCL_ERROR;
988    }
989
990    c = scanorderStr[0]; length = strlen (scanorderStr);
991    if (!strncmp (scanorderStr, strTopDown, length)) {
992	opts->scanOrder = TOP_DOWN;
993    } else if (!strncmp (scanorderStr, strBottomUp, length)) {
994	opts->scanOrder = BOTTOM_UP;
995    } else {
996	Tcl_AppendResult (interp, "invalid scanline order \"", scanorderStr,
997			  "\": should be TopDown or BottomUp",
998			  (char *) NULL);
999	return TCL_ERROR;
1000    }
1001
1002    c = pixelTypeStr[0]; length = strlen (pixelTypeStr);
1003    if (!strncmp (pixelTypeStr, strFloat, length)) {
1004	opts->pixelType = TYPE_FLOAT;
1005    } else if (!strncmp (pixelTypeStr, strUShort, length)) {
1006	opts->pixelType = TYPE_USHORT;
1007    } else if (!strncmp (pixelTypeStr, strUByte, length)) {
1008	opts->pixelType = TYPE_UBYTE;
1009    } else {
1010	Tcl_AppendResult (interp, "invalid pixel type \"", pixelTypeStr,
1011			  "\": should be float, short or byte",
1012			  (char *) NULL);
1013	return TCL_ERROR;
1014    }
1015
1016    c = uuencodeStr[0]; length = strlen (uuencodeStr);
1017    if (!strncmp (uuencodeStr, "1", length) || \
1018	!strncmp (uuencodeStr, "true", length) || \
1019	!strncmp (uuencodeStr, "on", length)) {
1020	opts->uuencode = 1;
1021    } else if (!strncmp (uuencodeStr, "0", length) || \
1022	!strncmp (uuencodeStr, "false", length) || \
1023	!strncmp (uuencodeStr, "off", length)) {
1024	opts->uuencode = 0;
1025    } else {
1026	Tcl_AppendResult (interp, "invalid uuencode mode \"", uuencodeStr,
1027			  "\": should be 1 or 0, on or off, true or false",
1028			  (char *) NULL);
1029	return TCL_ERROR;
1030    }
1031    return TCL_OK;
1032}
1033
1034static int ChnMatch(
1035    Tcl_Channel chan,
1036    const char *filename,
1037    Tcl_Obj *format,
1038    int *widthPtr,
1039    int *heightPtr,
1040    Tcl_Interp *interp
1041) {
1042    tkimg_MFile handle;
1043
1044    handle.data = (char *) chan;
1045    handle.state = IMG_CHAN;
1046
1047    return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
1048}
1049
1050static int ObjMatch(
1051    Tcl_Obj *data,
1052    Tcl_Obj *format,
1053    int *widthPtr,
1054    int *heightPtr,
1055    Tcl_Interp *interp
1056) {
1057    tkimg_MFile handle;
1058    FMTOPT opts;
1059
1060    if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
1061        return TCL_ERROR;
1062    }
1063    if (!opts.uuencode) {
1064        handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length);
1065        handle.state = IMG_STRING;
1066    } else {
1067        tkimg_ReadInit(data, 'M', &handle);
1068    }
1069    return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
1070}
1071
1072static int CommonMatch (interp, handle, format, widthPtr, heightPtr, rawHeaderPtr)
1073    Tcl_Interp *interp;
1074    tkimg_MFile *handle;
1075    Tcl_Obj *format;
1076    int *widthPtr;
1077    int *heightPtr;
1078    RAWHEADER *rawHeaderPtr;
1079{
1080    RAWHEADER th;
1081    FMTOPT opts;
1082
1083    initHeader (&th);
1084
1085    if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
1086	return TCL_ERROR;
1087    }
1088    if (opts.useHeader) {
1089	if (!readHeader (interp, handle, &th))
1090	    return 0;
1091    } else {
1092	th.width  = opts.width;
1093	th.height = opts.height;
1094	th.nChans = opts.nchan;
1095	th.pixelType = opts.pixelType;
1096	th.scanOrder = opts.scanOrder;
1097	th.byteOrder = opts.byteOrder;
1098    }
1099    *widthPtr  = th.width;
1100    *heightPtr = th.height;
1101    if (rawHeaderPtr) {
1102	*rawHeaderPtr = th;
1103    }
1104    return 1;
1105}
1106
1107static int ChnRead (interp, chan, filename, format, imageHandle,
1108                    destX, destY, width, height, srcX, srcY)
1109    Tcl_Interp *interp;         /* Interpreter to use for reporting errors. */
1110    Tcl_Channel chan;           /* The image channel, open for reading. */
1111    const char *filename;       /* The name of the image file. */
1112    Tcl_Obj *format;            /* User-specified format object, or NULL. */
1113    Tk_PhotoHandle imageHandle; /* The photo image to write into. */
1114    int destX, destY;           /* Coordinates of top-left pixel in
1115				 * photo image to be written to. */
1116    int width, height;          /* Dimensions of block of photo image to
1117				 * be written to. */
1118    int srcX, srcY;             /* Coordinates of top-left pixel to be used
1119			         * in image being read. */
1120{
1121    tkimg_MFile handle;
1122
1123    handle.data = (char *) chan;
1124    handle.state = IMG_CHAN;
1125
1126    return CommonRead (interp, &handle, filename, format, imageHandle,
1127                       destX, destY, width, height, srcX, srcY);
1128}
1129
1130static int ObjRead (interp, data, format, imageHandle,
1131	            destX, destY, width, height, srcX, srcY)
1132    Tcl_Interp *interp;
1133    Tcl_Obj *data;
1134    Tcl_Obj *format;
1135    Tk_PhotoHandle imageHandle;
1136    int destX, destY;
1137    int width, height;
1138    int srcX, srcY;
1139{
1140    tkimg_MFile handle;
1141    FMTOPT opts;
1142
1143    if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
1144        return TCL_ERROR;
1145    }
1146    if (!opts.uuencode) {
1147        handle.data = (char *) tkimg_GetByteArrayFromObj(data, &handle.length);
1148        handle.state = IMG_STRING;
1149    } else {
1150        tkimg_ReadInit(data, 'M', &handle);
1151    }
1152
1153    return CommonRead (interp, &handle, "InlineData", format, imageHandle,
1154                       destX, destY, width, height, srcX, srcY);
1155}
1156
1157static int CommonRead (interp, handle, filename, format, imageHandle,
1158                       destX, destY, width, height, srcX, srcY)
1159    Tcl_Interp *interp;         /* Interpreter to use for reporting errors. */
1160    tkimg_MFile *handle;        /* The image file, open for reading. */
1161    const char *filename;       /* The name of the image file. */
1162    Tcl_Obj *format;            /* User-specified format object, or NULL. */
1163    Tk_PhotoHandle imageHandle; /* The photo image to write into. */
1164    int destX, destY;           /* Coordinates of top-left pixel in
1165				 * photo image to be written to. */
1166    int width, height;          /* Dimensions of block of photo image to
1167			         * be written to. */
1168    int srcX, srcY;             /* Coordinates of top-left pixel to be used
1169			         * in image being read. */
1170{
1171    Tk_PhotoImageBlock block;
1172    Int x, y, c;
1173    Int fileWidth, fileHeight;
1174    Float minVals[MAXCHANS], maxVals[MAXCHANS];
1175    int stopY, outY, outWidth, outHeight;
1176    RAWFILE tf;
1177    FMTOPT opts;
1178    Boln swapBytes;
1179    Int  byteOrder;
1180    Int  scanOrder;
1181    Int  pixelType;
1182    Int  matte = 0;
1183    UByte  *pixbufPtr;
1184    Float  *floatBufPtr;
1185    UShort *ushortBufPtr;
1186    UByte  *ubyteBufPtr;
1187    Float  gtable[GTABSIZE];
1188    int result = TCL_OK;
1189
1190    memset (&tf, 0, sizeof (RAWFILE));
1191    initHeader (&tf.th);
1192
1193    CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th);
1194
1195    if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
1196        return TCL_ERROR;
1197    }
1198
1199    gtableFloat (opts.gamma, gtable);
1200
1201    if (opts.verbose)
1202	printImgInfo (&tf.th, &opts, filename, "Reading image:");
1203
1204    if ((srcX + width) > fileWidth) {
1205	outWidth = fileWidth - srcX;
1206    } else {
1207	outWidth = width;
1208    }
1209    if ((srcY + height) > fileHeight) {
1210	outHeight = fileHeight - srcY;
1211    } else {
1212	outHeight = height;
1213    }
1214    if ((outWidth <= 0) || (outHeight <= 0)
1215	|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
1216	return TCL_OK;
1217    }
1218
1219    byteOrder = opts.useHeader? tf.th.byteOrder: opts.byteOrder;
1220    scanOrder = opts.useHeader? tf.th.scanOrder: opts.scanOrder;
1221    pixelType = opts.useHeader? tf.th.pixelType: opts.pixelType;
1222    swapBytes = (( isIntel () && (byteOrder != INTEL)) ||
1223                 (!isIntel () && (byteOrder == INTEL)));
1224
1225    switch (pixelType) {
1226        case TYPE_FLOAT: {
1227	    tf.floatBuf = (Float *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (Float));
1228	    readFloatFile (handle, tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
1229			   swapBytes, opts.verbose, minVals, maxVals);
1230	    break;
1231        }
1232        case TYPE_USHORT: {
1233	    tf.ushortBuf = (UShort *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UShort));
1234	    readUShortFile (handle, tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
1235			    swapBytes, opts.verbose, minVals, maxVals);
1236	    break;
1237        }
1238        case TYPE_UBYTE: {
1239	    tf.ubyteBuf = (UByte *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UByte));
1240	    readUByteFile (handle, tf.ubyteBuf, fileWidth, fileHeight, tf.th.nChans,
1241			    swapBytes, opts.verbose, minVals, maxVals);
1242	    break;
1243        }
1244    }
1245    if (opts.nomap) {
1246	for (c=0; c<tf.th.nChans; c++) {
1247	    minVals[c] = 0.0;
1248	    maxVals[c] = 255.0;
1249	}
1250    } else if (opts.minVal != 0.0 || opts.maxVal != 0.0) {
1251	for (c=0; c<tf.th.nChans; c++) {
1252	    minVals[c] = opts.minVal;
1253	    maxVals[c] = opts.maxVal;
1254	}
1255    }
1256    switch (pixelType) {
1257        case TYPE_FLOAT: {
1258	    remapFloatValues (tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
1259			      minVals, maxVals);
1260	    break;
1261        }
1262        case TYPE_USHORT: {
1263	    remapUShortValues (tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
1264			       minVals, maxVals);
1265	    break;
1266        }
1267    }
1268
1269    if (tkimg_PhotoExpand (interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
1270	rawClose (&tf);
1271    return TCL_ERROR;
1272    }
1273
1274    tf.pixbuf = (UByte *) ckalloc (fileWidth * tf.th.nChans);
1275
1276    block.pixelSize = tf.th.nChans;
1277    block.pitch = fileWidth * tf.th.nChans;
1278    block.width = outWidth;
1279    block.height = 1;
1280    block.offset[0] = 0;
1281    block.offset[1] = (tf.th.nChans > 1? 1: 0);
1282    block.offset[2] = (tf.th.nChans > 1? 2: 0);
1283    block.offset[3] = (tf.th.nChans == 4 && matte? 3: 0);
1284    block.pixelPtr = tf.pixbuf + srcX * tf.th.nChans;
1285
1286    stopY = srcY + outHeight;
1287    outY = destY;
1288
1289    for (y=0; y<stopY; y++) {
1290	pixbufPtr = tf.pixbuf;
1291	switch (pixelType) {
1292	    case TYPE_FLOAT: {
1293		if (scanOrder == BOTTOM_UP) {
1294		    floatBufPtr = tf.floatBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1295		} else {
1296		    floatBufPtr = tf.floatBuf + y * fileWidth * tf.th.nChans;
1297		}
1298		FloatGammaUByte (fileWidth * tf.th.nChans, floatBufPtr,
1299				 opts.gamma != 1.0? gtable: NULL, pixbufPtr);
1300		floatBufPtr += fileWidth * tf.th.nChans;
1301		break;
1302	    }
1303	    case TYPE_USHORT: {
1304		if (scanOrder == BOTTOM_UP) {
1305		    ushortBufPtr = tf.ushortBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1306		} else {
1307		    ushortBufPtr = tf.ushortBuf + y * fileWidth * tf.th.nChans;
1308		}
1309		UShortGammaUByte (fileWidth * tf.th.nChans, ushortBufPtr,
1310				  opts.gamma != 1.0? gtable: NULL, pixbufPtr);
1311		ushortBufPtr += fileWidth * tf.th.nChans;
1312		break;
1313	    }
1314	    case TYPE_UBYTE: {
1315		if (scanOrder == BOTTOM_UP) {
1316		    ubyteBufPtr = tf.ubyteBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1317		} else {
1318		    ubyteBufPtr = tf.ubyteBuf + y * fileWidth * tf.th.nChans;
1319		}
1320		for (x=0; x<fileWidth * tf.th.nChans; x++) {
1321		    pixbufPtr[x] = ubyteBufPtr[x];
1322		}
1323		ubyteBufPtr += fileWidth * tf.th.nChans;
1324		break;
1325	    }
1326	}
1327	if (y >= srcY) {
1328	    if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
1329                                width, 1,
1330                                block.offset[3]?
1331                                TK_PHOTO_COMPOSITE_SET:
1332                                TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
1333		result = TCL_ERROR;
1334		break;
1335	    }
1336	    outY++;
1337	}
1338    }
1339    rawClose (&tf);
1340    return result;
1341}
1342
1343static int ChnWrite (interp, filename, format, blockPtr)
1344    Tcl_Interp *interp;
1345    const char *filename;
1346    Tcl_Obj *format;
1347    Tk_PhotoImageBlock *blockPtr;
1348{
1349    Tcl_Channel chan;
1350    tkimg_MFile handle;
1351    int result;
1352
1353    chan = tkimg_OpenFileChannel (interp, filename, 0644);
1354    if (!chan) {
1355	return TCL_ERROR;
1356    }
1357
1358    handle.data = (char *) chan;
1359    handle.state = IMG_CHAN;
1360
1361    result = CommonWrite (interp, filename, format, &handle, blockPtr);
1362    if (Tcl_Close(interp, chan) == TCL_ERROR) {
1363	return TCL_ERROR;
1364    }
1365    return result;
1366}
1367
1368static int StringWrite(
1369    Tcl_Interp *interp,
1370    Tcl_Obj *format,
1371    Tk_PhotoImageBlock *blockPtr
1372) {
1373    tkimg_MFile handle;
1374    int result;
1375    Tcl_DString data;
1376
1377    Tcl_DStringInit(&data);
1378    tkimg_WriteInit (&data, &handle);
1379    result = CommonWrite (interp, "InlineData", format, &handle, blockPtr);
1380    tkimg_Putc(IMG_DONE, &handle);
1381
1382    if (result == TCL_OK) {
1383	Tcl_DStringResult(interp, &data);
1384    } else {
1385	Tcl_DStringFree(&data);
1386    }
1387    return result;
1388}
1389
1390static int CommonWrite (interp, filename, format, handle, blockPtr)
1391    Tcl_Interp *interp;
1392    const char *filename;
1393    Tcl_Obj *format;
1394    tkimg_MFile *handle;
1395    Tk_PhotoImageBlock *blockPtr;
1396{
1397    Int     x, y;
1398    Int     redOffset, greenOffset, blueOffset, alphaOffset;
1399    UByte   *pixelPtr, *rowPixPtr;
1400    RAWFILE tf;
1401    FMTOPT opts;
1402    UByte *ubyteBufPtr;
1403    Int bytesPerLine;
1404
1405    memset (&tf, 0, sizeof (RAWFILE));
1406    if (ParseFormatOpts (interp, format, &opts) != TCL_OK) {
1407        return TCL_ERROR;
1408    }
1409
1410    redOffset   = 0;
1411    greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1412    blueOffset  = blockPtr->offset[2] - blockPtr->offset[0];
1413    alphaOffset = blockPtr->offset[0];
1414
1415    if (alphaOffset < blockPtr->offset[2]) {
1416        alphaOffset = blockPtr->offset[2];
1417    }
1418    if (++alphaOffset < blockPtr->pixelSize) {
1419        alphaOffset -= blockPtr->offset[0];
1420    } else {
1421        alphaOffset = 0;
1422    }
1423
1424    initHeader (&tf.th);
1425    tf.th.width = blockPtr->width;
1426    tf.th.height = blockPtr->height;
1427    tf.th.nChans = opts.nchan;
1428    tf.th.scanOrder = opts.scanOrder;
1429    tf.th.pixelType = TYPE_UBYTE;
1430
1431    writeHeader (handle, &tf.th);
1432    bytesPerLine = blockPtr->width * tf.th.nChans * sizeof (UByte);
1433    tf.ubyteBuf = (UByte *)ckalloc (bytesPerLine);
1434
1435    rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0];
1436    for (y = 0; y < blockPtr->height; y++) {
1437	ubyteBufPtr = tf.ubyteBuf;
1438	pixelPtr = rowPixPtr;
1439	if (tf.th.nChans == 1) {
1440	    for (x=0; x<blockPtr->width; x++) {
1441		*ubyteBufPtr = pixelPtr[redOffset];
1442		ubyteBufPtr++;
1443		pixelPtr += blockPtr->pixelSize;
1444	    }
1445	} else {
1446	    for (x=0; x<blockPtr->width; x++) {
1447		*(ubyteBufPtr++) = pixelPtr[redOffset];
1448		*(ubyteBufPtr++) = pixelPtr[greenOffset];
1449		*(ubyteBufPtr++) = pixelPtr[blueOffset];
1450		if (tf.th.nChans == 4) {
1451		    /* Have a matte channel and write it. */
1452		    *(ubyteBufPtr++) = pixelPtr[alphaOffset];
1453		}
1454		pixelPtr += blockPtr->pixelSize;
1455	    }
1456	}
1457	if (tkimg_Write (handle, (char *)tf.ubyteBuf, bytesPerLine) != bytesPerLine) {
1458	    rawClose (&tf);
1459	    return TCL_ERROR;
1460	}
1461	rowPixPtr += blockPtr->pitch;
1462    }
1463    if (opts.verbose)
1464        printImgInfo (&tf.th, &opts, filename, "Saving image:");
1465    rawClose (&tf);
1466    return TCL_OK;
1467}
1468
1469