1 /* STARTHEADER
2 *
3 * File : raw.c
4 *
5 * Author : Paul Obermeier (paul@poSoft.de)
6 *
7 * Date : 2001 / 02 / 21
8 *
9 * Copyright : (C) 2001-2019 Paul Obermeier
10 *
11 * Description :
12 *
13 * A photo image handler for raw matrix 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> -map <enum>
48 * -gamma <float> -min <float> -max <float>
49 * -cutoff <float> -saturation <float> -printagc <bool>"
50 * Write RAW image: "raw -useheader true -verbose <bool> -nchan <int>
51 * -scanorder <string>"
52 *
53 * For raw images without header:
54 * Read RAW image: "raw -useheader false -verbose <bool> -map <enum>
55 * -gamma <float> -min <float> -max <float>
56 * -cutoff <float> -saturation <float> -printagc <bool>
57 * -nchan <int> -scanorder <string> -byteorder <string>
58 * -width <int> -height <int>
59 * -pixeltype <string> -uuencode <bool>"
60 * Write RAW image: "raw -useheader false -verbose <bool> -nchan <int>
61 * -scanorder <string>"
62 *
63 * -verbose <bool>: If set to true, additional information about the file
64 * format is printed to stdout. Default is false.
65 * -useheader <bool>: If set to true, use file header information for reading
66 * and writing. Default is true.
67 *
68 * -map <enum>: Specify the mode when mapping the 32 or 16-bit values
69 * of the image to 8-bit gray scale values for displaying.
70 * Valid mapping mode strings are: none, minmax, agc.
71 * Default mode is minmax.
72 *
73 * Mode "none":
74 * If mapping mode is set to "none", no mapping of input
75 * values is done. Use this mode, if the image already
76 * contains RGB values in the range of 0 ..255.
77 * When using mode "none", no information about the
78 * minimum and maximum pixel values is gathered during
79 * reading and therefore no verbose output is printed.
80 * On the other hand reading the image is faster.
81 *
82 * Mode "minmax":
83 * "minmax" maps the minimum and maximum values of the image data to
84 * 256 gray scale values.
85 *
86 * Mode "agc":
87 * "agc" applies an automatic gain control algorithmn to the
88 * image data.
89 * Currently implemented for 1-channel 32-bit float images only.
90 * -gamma <float>: Specify a gamma correction to be applied when mapping
91 * the input data to 8-bit image values.
92 * Default is 1.0.
93 * Valid for mapping modes: minmax, agc
94 * -max <float>: Specify the maximum pixel value to be used for mapping
95 * the input data to 8-bit image values.
96 * Default is the maximum value found in the image data.
97 * -min <float>: Specify the minimum pixel value to be used for mapping
98 * the input data to 8-bit image values.
99 * Default is the minimum value found in the image data.
100 * -saturation <float>: If option is given, an Automatic Gain Control algorithmn is
101 * applied to the input values. The supplied value specifies the
102 * saturation value, i.e. all pixel values greater than the
103 * saturation are mapped to white.
104 * Valid for mapping mode: agc
105 * -cutoff <float>: If option is given, an Automatic Gain Control algorithmn is
106 * applied to the input values. The supplied value specifies the
107 * cut-off value in percent.
108 * Valid for mapping mode: agc
109 * -printagc <bool>: If set to true, additional information about the Automatic
110 * Gain Control is printed to stdout. Default is false.
111 * Valid for mapping mode: agc
112 *
113 * -nchan <int>: Specify the number of channels of the input image.
114 * Only valid, if reading image data without header.
115 * Default is 1.
116 * -width <int>: Specify the width of the input image. Only valid, if
117 * reading image data without header. Default is 128.
118 * -height <int>: Specify the height of the input image. Only valid, if
119 * reading image data without header. Default is 128.
120 * -byteorder <string>: Specify the byteorder of the input image. Only valid, if
121 * reading image data without header.
122 * Possible values: "Intel" or "Motorola".
123 * Default is assuming the same byteorder as that of the
124 * host computer.
125 * -scanorder <string>: Specify the scanline order of the input image. Only
126 * valid, if reading image data without header.
127 * Possible values: "TopDown" or "BottomUp".
128 * Default is "TopDown".
129 * -pixeltype <string>: Specify the type of the pixel values.
130 * Only valid, if reading image data without header.
131 * Possible values: "float", "short" or "byte".
132 * Default is "byte".
133 * -uuencode <bool>: If set to false, do not assume, that the image data stored in a
134 * variable is uuencoded. Default is true, i.e. the image data is
135 * assumed to be uuencoded.
136 *
137 * Notes:
138 * Currently RAW files are only written in "byte" pixel format.
139 *
140 * ENDHEADER
141 *
142 */
143
144 #include <stdlib.h>
145 #include <math.h>
146
147 /*
148 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
149 */
150
151 #include "init.c"
152
153 /* Maximum length of a header line. */
154 #define HEADLEN 100
155
156 /* Header fields. */
157 #define strMagic "Magic=%s\n"
158 #define strWidth "Width=%d\n"
159 #define strHeight "Height=%d\n"
160 #define strNumChan "NumChan=%d\n"
161 #define strByteOrder "ByteOrder=%s\n"
162 #define strScanOrder "ScanOrder=%s\n"
163 #define strPixelType "PixelType=%s\n"
164
165 /* Header fields possible values. */
166 #define strIntel "Intel"
167 #define strMotorola "Motorola"
168 #define strTopDown "TopDown"
169 #define strBottomUp "BottomUp"
170 #define strFloat "float"
171 #define strUShort "short"
172 #define strUByte "byte"
173
174 #define strUnknown "Unknown"
175
176 #define BOTTOM_UP 0
177 #define TOP_DOWN 1
178 #define INTEL 0
179 #define MOTOROLA 1
180 #define TYPE_FLOAT 0
181 #define TYPE_USHORT 1
182 #define TYPE_UBYTE 2
183
184 /* Some general defines and typedefs. */
185 #define TRUE 1
186 #define FALSE 0
187
188 typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */
189 typedef unsigned char UByte; /* Unsigned 8 bit integer */
190 typedef char Byte; /* Signed 8 bit integer */
191 typedef unsigned short UShort; /* Unsigned 16 bit integer */
192 typedef short Short; /* Signed 16 bit integer */
193 typedef int UInt; /* Unsigned 32 bit integer */
194 typedef int Int; /* Signed 32 bit integer */
195 typedef float Float; /* IEEE 32 bit floating point */
196 typedef double Double; /* IEEE 64 bit floating point */
197
198 /* RAW file header structure */
199 typedef struct {
200 char id[3];
201 Int nChans;
202 Int width;
203 Int height;
204 Int scanOrder;
205 Int byteOrder;
206 Int pixelType;
207 } RAWHEADER;
208
209 /* Format options structure for use with ParseFormatOpts */
210 typedef struct {
211 Int width;
212 Int height;
213 Int nchan;
214 Int scanOrder;
215 Int byteOrder;
216 Int pixelType;
217 Int mapMode;
218 Float gamma; /* IMG_MAP_MINMAX and IMG_MAP_AGC */
219 Float minVal; /* IMG_MAP_MINMAX */
220 Float maxVal; /* IMG_MAP_MINMAX */
221 Float saturation; /* IMG_MAP_AGC */
222 Float cutOff; /* IMG_MAP_AGC */
223 Boln verbose;
224 Boln printAgc;
225 Boln uuencode;
226 Boln useHeader;
227 } FMTOPT;
228
229 /* Structure to hold information about the image file being processed. */
230 typedef struct {
231 RAWHEADER th;
232 UByte *pixbuf;
233 Float *floatBuf;
234 UShort *ushortBuf;
235 UByte *ubyteBuf;
236 } RAWFILE;
237
rawClose(RAWFILE * tf,Boln fastMode)238 static void rawClose (RAWFILE *tf, Boln fastMode)
239 {
240 if (!fastMode) {
241 if (tf->pixbuf) ckfree ((char *)tf->pixbuf);
242 }
243 if (tf->floatBuf) ckfree ((char *)tf->floatBuf);
244 if (tf->ushortBuf) ckfree ((char *)tf->ushortBuf);
245 if (tf->ubyteBuf) ckfree ((char *)tf->ubyteBuf);
246 return;
247 }
248
249 #define OUT Tcl_WriteChars (outChan, str, -1)
printImgInfo(RAWHEADER * th,FMTOPT * opts,const char * filename,const char * msg)250 static void printImgInfo (RAWHEADER *th, FMTOPT *opts,
251 const char *filename, const char *msg)
252 {
253 Tcl_Channel outChan;
254 char str[256];
255
256 outChan = Tcl_GetStdChannel (TCL_STDOUT);
257 if (!outChan) {
258 return;
259 }
260 sprintf (str, "%s %s\n", msg, filename); OUT;
261 sprintf (str, "\tSize in pixel : %d x %d\n", th->width, th->height); OUT;
262 sprintf (str, "\tNo. of channels : %d\n", th->nChans); OUT;
263 sprintf (str, "\tPixel type : %s\n", (th->pixelType == TYPE_FLOAT? strFloat:
264 (th->pixelType == TYPE_USHORT? strUShort:
265 (th->pixelType == TYPE_UBYTE? strUByte:
266 strUnknown)))); OUT;
267 sprintf (str, "\tVertical encoding: %s\n", th->scanOrder == TOP_DOWN?
268 strTopDown: strBottomUp); OUT;
269 sprintf (str, "\tHost byte order : %s\n", tkimg_IsIntel ()? strIntel: strMotorola); OUT;
270 sprintf (str, "\tFile byte order : %s\n", th->byteOrder == INTEL?
271 strIntel: strMotorola); OUT;
272 sprintf (str, "\tMapping mode : %s\n", (opts->mapMode == IMG_MAP_NONE? IMG_MAP_NONE_STR:
273 (opts->mapMode == IMG_MAP_MINMAX? IMG_MAP_MINMAX_STR:
274 (opts->mapMode == IMG_MAP_AGC? IMG_MAP_AGC_STR:
275 strUnknown)))); OUT;
276 if (opts->mapMode != IMG_MAP_NONE) {
277 sprintf (str, "\tGamma correction : %f\n", opts->gamma); OUT;
278 if (opts->mapMode == IMG_MAP_MINMAX) {
279 sprintf (str, "\tMinimum map value: %f\n", opts->minVal); OUT;
280 sprintf (str, "\tMaximum map value: %f\n", opts->maxVal); OUT;
281 }
282 if (opts->mapMode == IMG_MAP_AGC) {
283 sprintf (str, "\tSaturation : %f\n", opts->saturation); OUT;
284 sprintf (str, "\tCutOff : %f%%\n", opts->cutOff); OUT;
285 }
286 }
287 Tcl_Flush (outChan);
288 }
289 #undef OUT
290
readHeaderLine(Tcl_Interp * interp,tkimg_MFile * handle,char * buf)291 static Boln readHeaderLine (Tcl_Interp *interp, tkimg_MFile *handle, char *buf)
292 {
293 char c, *bufPtr, *bufEndPtr;
294 Boln failure;
295
296 bufPtr = buf;
297 bufEndPtr = buf + HEADLEN;
298 failure = TRUE;
299
300 while (tkimg_Read2(handle, &c, 1) == 1 && bufPtr < bufEndPtr) {
301 if (c == '\n') {
302 *bufPtr = '\0';
303 failure = FALSE;
304 break;
305 }
306 *bufPtr = c;
307 bufPtr++;
308 }
309 if (failure) {
310 Tcl_AppendResult (interp, "RAW handler: Error reading header line (",
311 buf, ")\n", NULL);
312 return FALSE;
313 }
314 return TRUE;
315 }
316
readHeader(Tcl_Interp * interp,tkimg_MFile * handle,RAWHEADER * th)317 static Boln readHeader (Tcl_Interp *interp, tkimg_MFile *handle, RAWHEADER *th)
318 {
319 char buf[HEADLEN];
320 char tmpStr[HEADLEN];
321
322 if (!readHeaderLine (interp, handle, buf) ||
323 (1 != sscanf (buf, strMagic, th->id))) {
324 Tcl_AppendResult (interp, "Unable to parse header field Magic\n", NULL);
325 return FALSE;
326 }
327 if (strcmp (th->id, "RAW") != 0) {
328 Tcl_AppendResult (interp, "Invalid value for header field Magic:",
329 "Must be \"RAW\"\n", NULL);
330 return FALSE;
331 }
332
333 if (!readHeaderLine (interp, handle, buf) ||
334 (1 != sscanf (buf, strWidth, &th->width))) {
335 Tcl_AppendResult (interp, "Unable to parse header field Width\n", NULL);
336 return FALSE;
337 }
338 if (th->width < 1) {
339 Tcl_AppendResult (interp, "Invalid value for header field Width:",
340 "Must be greater than zero\n", NULL);
341 return FALSE;
342 }
343
344 if (!readHeaderLine (interp, handle, buf) ||
345 (1 != sscanf (buf, strHeight, &th->height))) {
346 Tcl_AppendResult (interp, "Unable to parse header field Height\n", NULL);
347 return FALSE;
348 }
349 if (th->height < 1) {
350 Tcl_AppendResult (interp, "Invalid value for header field Height:",
351 "Must be greater than zero\n", NULL);
352 return FALSE;
353 }
354
355 if (!readHeaderLine (interp, handle, buf) ||
356 (1 != sscanf (buf, strNumChan, &th->nChans))) {
357 Tcl_AppendResult (interp, "Unable to parse header field NumChan\n", NULL);
358 return FALSE;
359 }
360 if (th->nChans != 1 && th->nChans != 3) {
361 Tcl_AppendResult (interp, "Invalid value for header field NumChan:",
362 "Must be 1 or 3\n", NULL);
363 return FALSE;
364 }
365
366 if (!readHeaderLine (interp, handle, buf) ||
367 (1 != sscanf (buf, strByteOrder, tmpStr))) {
368 Tcl_AppendResult (interp, "Unable to parse header field ByteOrder\n", NULL);
369 return FALSE;
370 }
371
372 if (strcmp (tmpStr, strIntel) == 0) {
373 th->byteOrder = INTEL;
374 } else if (strcmp (tmpStr, strMotorola) == 0) {
375 th->byteOrder = MOTOROLA;
376 } else {
377 Tcl_AppendResult (interp, "Invalid value for header field ByteOrder:",
378 "Must be ", strIntel, " or ", strMotorola,
379 "\n", NULL);
380 return FALSE;
381 }
382
383 if (!readHeaderLine (interp, handle, buf) ||
384 (1 != sscanf (buf, strScanOrder, tmpStr))) {
385 Tcl_AppendResult (interp, "Unable to parse header field ScanOrder\n", NULL);
386 return FALSE;
387 }
388 if (strcmp (tmpStr, strTopDown) == 0) {
389 th->scanOrder = TOP_DOWN;
390 } else if (strcmp (tmpStr, strBottomUp) == 0) {
391 th->scanOrder = BOTTOM_UP;
392 } else {
393 Tcl_AppendResult (interp, "Invalid value for header field ScanOrder:",
394 "Must be ", strTopDown, " or ", strBottomUp,
395 "\n", NULL);
396 return FALSE;
397 }
398
399 if (!readHeaderLine (interp, handle, buf) ||
400 (1 != sscanf (buf, strPixelType, tmpStr))) {
401 Tcl_AppendResult (interp, "Unable to parse header field PixelType\n", NULL);
402 return FALSE;
403 }
404 if (strcmp (tmpStr, strFloat) == 0) {
405 th->pixelType = TYPE_FLOAT;
406 } else if (strcmp (tmpStr, strUShort) == 0) {
407 th->pixelType = TYPE_USHORT;
408 } else if (strcmp (tmpStr, strUByte) == 0) {
409 th->pixelType = TYPE_UBYTE;
410 } else {
411 Tcl_AppendResult (interp, "Invalid value for header field PixelType:",
412 "Must be ", strFloat, ",", strUShort, " or ", strUByte,
413 "\n", NULL);
414 return FALSE;
415 }
416
417 return TRUE;
418 }
419
writeHeader(tkimg_MFile * handle,RAWHEADER * th)420 static Boln writeHeader (tkimg_MFile *handle, RAWHEADER *th)
421 {
422 char buf[1024];
423
424 sprintf (buf, strMagic, "RAW");
425 tkimg_Write2(handle, buf, strlen (buf));
426 sprintf (buf, strWidth, th->width);
427 tkimg_Write2(handle, buf, strlen (buf));
428 sprintf (buf, strHeight, th->height);
429 tkimg_Write2(handle, buf, strlen (buf));
430 sprintf (buf, strNumChan, th->nChans);
431 tkimg_Write2(handle, buf, strlen (buf));
432 sprintf (buf, strByteOrder, tkimg_IsIntel()? strIntel: strMotorola);
433 tkimg_Write2(handle, buf, strlen (buf));
434 sprintf (buf, strScanOrder, th->scanOrder == TOP_DOWN?
435 strTopDown: strBottomUp);
436 tkimg_Write2(handle, buf, strlen (buf));
437 sprintf (buf, strPixelType, (th->pixelType == TYPE_FLOAT? strFloat:
438 (th->pixelType == TYPE_USHORT? strUShort:
439 (th->pixelType == TYPE_UBYTE? strUByte:
440 strUnknown))));
441 tkimg_Write2(handle, buf, strlen (buf));
442 return TRUE;
443 }
444
initHeader(RAWHEADER * th)445 static void initHeader (RAWHEADER *th)
446 {
447 th->id[0] = 'R';
448 th->id[1] = 'A';
449 th->id[2] = 'W';
450 th->nChans = 1;
451 th->width = 128;
452 th->height = 128;
453 th->scanOrder = TOP_DOWN;
454 th->byteOrder = INTEL;
455 th->pixelType = TYPE_UBYTE;
456 return;
457 }
458
459 /*
460 * Here is the start of the standard functions needed for every image format.
461 */
462
463 /*
464 * Prototypes for local procedures defined in this file:
465 */
466
467 static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
468 FMTOPT *opts);
469 static int CommonMatch(Tcl_Interp *interp, tkimg_MFile *handle,
470 Tcl_Obj *format, int *widthPtr, int *heightPtr,
471 RAWHEADER *rawHeaderPtr);
472 static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
473 const char *filename, Tcl_Obj *format,
474 Tk_PhotoHandle imageHandle, int destX, int destY,
475 int width, int height, int srcX, int srcY);
476 static int CommonWrite(Tcl_Interp *interp,
477 const char *filename, Tcl_Obj *format,
478 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
479
ParseFormatOpts(Tcl_Interp * interp,Tcl_Obj * format,FMTOPT * opts)480 static int ParseFormatOpts(
481 Tcl_Interp *interp,
482 Tcl_Obj *format,
483 FMTOPT *opts
484 ) {
485 static const char *const rawOptions[] = {
486 "-verbose", "-width", "-height", "-nchan", "-byteorder",
487 "-scanorder", "-pixeltype", "-min", "-max", "-gamma",
488 "-useheader", "-map", "-uuencode", "-saturation", "-cutoff",
489 "-nomap", "-printagc", NULL
490 };
491 int objc, i, index;
492 char *optionStr;
493 Tcl_Obj **objv;
494 int boolVal;
495 int intVal;
496 double doubleVal;
497
498 /* Initialize options with default values. */
499 opts->verbose = 0;
500 opts->width = 128;
501 opts->height = 128;
502 opts->nchan = 1;
503 opts->byteOrder = tkimg_IsIntel()? INTEL: MOTOROLA;
504 opts->scanOrder = TOP_DOWN;
505 opts->pixelType = TYPE_UBYTE;
506 opts->minVal = -1.0;
507 opts->maxVal = -1.0;
508 opts->gamma = 1.0;
509 opts->useHeader = 1;
510 opts->mapMode = IMG_MAP_MINMAX;
511 opts->uuencode = 1;
512 opts->saturation = -1.0;
513 opts->cutOff = 3.0;
514 opts->printAgc = 0;
515
516 if (tkimg_ListObjGetElements (interp, format, &objc, &objv) != TCL_OK)
517 return TCL_ERROR;
518 if (objc) {
519 for (i=1; i<objc; i++) {
520 if (Tcl_GetIndexFromObj (interp, objv[i], (const char *CONST86 *)rawOptions,
521 "format option", 0, &index) != TCL_OK) {
522 return TCL_ERROR;
523 }
524 i++;
525 if (i >= objc) {
526 Tcl_AppendResult (interp, "No value for option \"",
527 Tcl_GetStringFromObj (objv[--i], (int *) NULL),
528 "\"", (char *) NULL);
529 return TCL_ERROR;
530 }
531 optionStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
532 switch(index) {
533 case 0:
534 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
535 Tcl_AppendResult (interp, "Invalid verbose mode \"", optionStr,
536 "\": should be 1 or 0, on or off, true or false",
537 (char *) NULL);
538 return TCL_ERROR;
539 }
540 opts->verbose = boolVal;
541 break;
542 case 1:
543 if (Tcl_GetInt(interp, optionStr, &intVal) == TCL_ERROR || intVal <= 0) {
544 Tcl_AppendResult (interp, "Invalid image width \"", optionStr,
545 "\": Must be greater than zero.", (char *) NULL);
546 return TCL_ERROR;
547 }
548 opts->width = intVal;
549 break;
550 case 2:
551 if (Tcl_GetInt(interp, optionStr, &intVal) == TCL_ERROR || intVal <= 0) {
552 Tcl_AppendResult (interp, "Invalid image height \"", optionStr,
553 "\": Must be greater than zero.", (char *) NULL);
554 return TCL_ERROR;
555 }
556 opts->height = intVal;
557 break;
558 case 3:
559 if (Tcl_GetInt(interp, optionStr, &intVal) == TCL_ERROR ||
560 intVal <= 0 || intVal > 4) {
561 Tcl_AppendResult (interp, "Invalid number of channels \"", optionStr,
562 "\": Must be either 1, 2, 3 or 4.", (char *) NULL);
563 return TCL_ERROR;
564 }
565 opts->nchan = intVal;
566 break;
567 case 4:
568 if (!strncmp (optionStr, strIntel, strlen (strIntel))) {
569 opts->byteOrder = INTEL;
570 } else if (!strncmp (optionStr, strMotorola, strlen (strMotorola))) {
571 opts->byteOrder = MOTOROLA;
572 } else {
573 Tcl_AppendResult (interp, "Invalid byteorder mode \"", optionStr,
574 "\": Must be ", strIntel, " or ", strMotorola, ".",
575 (char *) NULL);
576 return TCL_ERROR;
577 }
578 break;
579 case 5:
580 if (!strncmp (optionStr, strTopDown, strlen (strTopDown))) {
581 opts->scanOrder = TOP_DOWN;
582 } else if (!strncmp (optionStr, strBottomUp, strlen (strBottomUp))) {
583 opts->scanOrder = BOTTOM_UP;
584 } else {
585 Tcl_AppendResult (interp, "Invalid scanline order \"", optionStr,
586 "\": should be TopDown or BottomUp",
587 (char *) NULL);
588 return TCL_ERROR;
589 }
590 break;
591 case 6:
592 if (!strncmp (optionStr, strFloat, strlen (strFloat))) {
593 opts->pixelType = TYPE_FLOAT;
594 } else if (!strncmp (optionStr, strUShort, strlen (strUShort))) {
595 opts->pixelType = TYPE_USHORT;
596 } else if (!strncmp (optionStr, strUByte, strlen (strUByte))) {
597 opts->pixelType = TYPE_UBYTE;
598 } else {
599 Tcl_AppendResult (interp, "Invalid pixel type \"", optionStr,
600 "\": should be float, short or byte",
601 (char *) NULL);
602 return TCL_ERROR;
603 }
604 break;
605 case 7:
606 if (Tcl_GetDouble(interp, optionStr, &doubleVal) == TCL_ERROR) {
607 Tcl_AppendResult (interp, "Invalid minimum map value \"", optionStr,
608 "\": Must be greater than or equal to zero.", (char *) NULL);
609 return TCL_ERROR;
610 }
611 if (doubleVal >= 0.0) {
612 opts->minVal = doubleVal;
613 }
614 break;
615 case 8:
616 if (Tcl_GetDouble(interp, optionStr, &doubleVal) == TCL_ERROR) {
617 Tcl_AppendResult (interp, "Invalid maximum map value \"", optionStr,
618 "\": Must be greater than or equal to zero.", (char *) NULL);
619 return TCL_ERROR;
620 }
621 if (doubleVal >= 0.0) {
622 opts->maxVal = doubleVal;
623 }
624 break;
625 case 9:
626 if (Tcl_GetDouble(interp, optionStr, &doubleVal) == TCL_ERROR) {
627 Tcl_AppendResult (interp, "Invalid gamma value \"", optionStr,
628 "\": Must be greater than or equal to zero.", (char *) NULL);
629 return TCL_ERROR;
630 }
631 if (doubleVal >= 0.0) {
632 opts->gamma = doubleVal;
633 }
634 break;
635 case 10:
636 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
637 Tcl_AppendResult (interp, "Invalid useheader mode \"", optionStr,
638 "\": should be 1 or 0, on or off, true or false",
639 (char *) NULL);
640 return TCL_ERROR;
641 }
642 opts->useHeader = boolVal;
643 break;
644 case 11:
645 if (!strncmp (optionStr, IMG_MAP_NONE_STR, strlen (IMG_MAP_NONE_STR))) {
646 opts->mapMode = IMG_MAP_NONE;
647 } else if (!strncmp (optionStr, IMG_MAP_MINMAX_STR, strlen (IMG_MAP_MINMAX_STR))) {
648 opts->mapMode = IMG_MAP_MINMAX;
649 } else if (!strncmp (optionStr, IMG_MAP_AGC_STR, strlen (IMG_MAP_AGC_STR))) {
650 opts->mapMode = IMG_MAP_AGC;
651 } else {
652 Tcl_AppendResult (interp, "Invalid mapping mode \"", optionStr,
653 "\": should be none, minmax or agc",
654 (char *) NULL);
655 return TCL_ERROR;
656 }
657 break;
658 case 12:
659 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
660 Tcl_AppendResult (interp, "Invalid uuencode mode \"", optionStr,
661 "\": should be 1 or 0, on or off, true or false",
662 (char *) NULL);
663 return TCL_ERROR;
664 }
665 opts->uuencode = boolVal;
666 break;
667 case 13:
668 if (Tcl_GetDouble(interp, optionStr, &doubleVal) == TCL_ERROR) {
669 Tcl_AppendResult (interp, "Invalid saturation value \"", optionStr,
670 "\": Must be greater than or equal to zero.", (char *) NULL);
671 return TCL_ERROR;
672 }
673 if (doubleVal >= 0.0) {
674 opts->saturation = doubleVal;
675 }
676 break;
677 case 14:
678 if (Tcl_GetDouble(interp, optionStr, &doubleVal) == TCL_ERROR) {
679 Tcl_AppendResult (interp, "Invalid cutoff value \"", optionStr,
680 "\": Must be greater than or equal to zero.", (char *) NULL);
681 return TCL_ERROR;
682 }
683 if (doubleVal >= 0.0) {
684 opts->cutOff = doubleVal;
685 }
686 break;
687 case 15:
688 /* Option "-nomap" for backward compatibility. */
689 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
690 Tcl_AppendResult (interp, "Invalid nomap mode \"", optionStr,
691 "\": should be 1 or 0, on or off, true or false",
692 (char *) NULL);
693 return TCL_ERROR;
694 }
695 if (boolVal) {
696 opts->mapMode = IMG_MAP_NONE;
697 }
698 break;
699 case 16:
700 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
701 Tcl_AppendResult (interp, "Invalid printagc mode \"", optionStr,
702 "\": should be 1 or 0, on or off, true or false",
703 (char *) NULL);
704 return TCL_ERROR;
705 }
706 opts->printAgc = boolVal;
707 break;
708 }
709 }
710 }
711
712 /* Convert minimum and maximum range values. */
713 if (opts->minVal >= 0.0 && opts->maxVal >= 0.0 && opts->minVal >= opts->maxVal) {
714 Tcl_AppendResult (interp, "Invalid range values: Maximum must be grater than minimum.", (char *) NULL);
715 return TCL_ERROR;
716 }
717 return TCL_OK;
718 }
719
ChnMatch(Tcl_Channel chan,const char * filename,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)720 static int ChnMatch(
721 Tcl_Channel chan,
722 const char *filename,
723 Tcl_Obj *format,
724 int *widthPtr,
725 int *heightPtr,
726 Tcl_Interp *interp
727 ) {
728 tkimg_MFile handle;
729
730 handle.data = (char *) chan;
731 handle.state = IMG_CHAN;
732
733 return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
734 }
735
ObjMatch(Tcl_Obj * data,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)736 static int ObjMatch(
737 Tcl_Obj *data,
738 Tcl_Obj *format,
739 int *widthPtr,
740 int *heightPtr,
741 Tcl_Interp *interp
742 ) {
743 tkimg_MFile handle;
744 FMTOPT opts;
745
746 if (ParseFormatOpts (interp, format, &opts) == TCL_ERROR) {
747 return FALSE;
748 }
749 if (!opts.uuencode) {
750 size_t length;
751 handle.data = (char *) tkimg_GetByteArrayFromObj2(data, &length);
752 handle.length = length;
753 handle.state = IMG_STRING;
754 } else {
755 tkimg_ReadInit(data, 'M', &handle);
756 }
757 return CommonMatch (interp, &handle, format, widthPtr, heightPtr, NULL);
758 }
759
CommonMatch(Tcl_Interp * interp,tkimg_MFile * handle,Tcl_Obj * format,int * widthPtr,int * heightPtr,RAWHEADER * rawHeaderPtr)760 static int CommonMatch(
761 Tcl_Interp *interp,
762 tkimg_MFile *handle,
763 Tcl_Obj *format,
764 int *widthPtr,
765 int *heightPtr,
766 RAWHEADER *rawHeaderPtr
767 ) {
768 RAWHEADER th;
769 FMTOPT opts;
770
771 initHeader (&th);
772
773 if (ParseFormatOpts (interp, format, &opts) == TCL_ERROR) {
774 return FALSE;
775 }
776 if (opts.useHeader) {
777 if (!readHeader (interp, handle, &th)) {
778 return FALSE;
779 }
780 } else {
781 th.width = opts.width;
782 th.height = opts.height;
783 th.nChans = opts.nchan;
784 th.pixelType = opts.pixelType;
785 th.scanOrder = opts.scanOrder;
786 th.byteOrder = opts.byteOrder;
787 }
788 *widthPtr = th.width;
789 *heightPtr = th.height;
790 if (rawHeaderPtr) {
791 *rawHeaderPtr = th;
792 }
793 return TRUE;
794 }
795
ChnRead(Tcl_Interp * interp,Tcl_Channel chan,const char * filename,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)796 static int ChnRead(
797 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
798 Tcl_Channel chan, /* The image channel, open for reading. */
799 const char *filename, /* The name of the image file. */
800 Tcl_Obj *format, /* User-specified format object, or NULL. */
801 Tk_PhotoHandle imageHandle, /* The photo image to write into. */
802 int destX, int destY, /* Coordinates of top-left pixel in
803 * photo image to be written to. */
804 int width, int height, /* Dimensions of block of photo image to
805 * be written to. */
806 int srcX, int srcY /* Coordinates of top-left pixel to be used
807 * in image being read. */
808 ) {
809 tkimg_MFile handle;
810
811 handle.data = (char *) chan;
812 handle.state = IMG_CHAN;
813
814 return CommonRead (interp, &handle, filename, format, imageHandle,
815 destX, destY, width, height, srcX, srcY);
816 }
817
ObjRead(Tcl_Interp * interp,Tcl_Obj * data,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)818 static int ObjRead(
819 Tcl_Interp *interp,
820 Tcl_Obj *data,
821 Tcl_Obj *format,
822 Tk_PhotoHandle imageHandle,
823 int destX, int destY,
824 int width, int height,
825 int srcX, int srcY
826 ) {
827 tkimg_MFile handle;
828 FMTOPT opts;
829
830 if (ParseFormatOpts (interp, format, &opts) == TCL_ERROR) {
831 return TCL_ERROR;
832 }
833 if (!opts.uuencode) {
834 size_t length;
835 handle.data = (char *) tkimg_GetByteArrayFromObj2(data, &length);
836 handle.length = length;
837 handle.state = IMG_STRING;
838 } else {
839 tkimg_ReadInit(data, 'M', &handle);
840 }
841
842 return CommonRead (interp, &handle, "InlineData", format, imageHandle,
843 destX, destY, width, height, srcX, srcY);
844 }
845
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)846 static int CommonRead(
847 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
848 tkimg_MFile *handle, /* The image file, open for reading. */
849 const char *filename, /* The name of the image file. */
850 Tcl_Obj *format, /* User-specified format object, or NULL. */
851 Tk_PhotoHandle imageHandle, /* The photo image to write into. */
852 int destX, int destY, /* Coordinates of top-left pixel in
853 * photo image to be written to. */
854 int width, int height, /* Dimensions of block of photo image to
855 * be written to. */
856 int srcX, int srcY /* Coordinates of top-left pixel to be used
857 * in image being read. */
858 ) {
859 Tk_PhotoImageBlock block;
860 Int x, y, c;
861 Int fileWidth = 0, fileHeight = 0;
862 Float minVals[IMG_MAX_CHANNELS], maxVals[IMG_MAX_CHANNELS];
863 int stopY, outY, outWidth, outHeight;
864 RAWFILE tf;
865 FMTOPT opts;
866 Boln swapBytes;
867 Boln fastMode;
868 Int byteOrder;
869 Int scanOrder;
870 Int pixelType;
871 Int matte = 0;
872 UByte *pixbufPtr;
873 Float *floatBufPtr;
874 UShort *ushortBufPtr;
875 UByte *ubyteBufPtr;
876 Float gtable[IMG_GAMMA_TABLE_SIZE];
877 int result = TCL_OK;
878
879 memset (&tf, 0, sizeof (RAWFILE));
880 initHeader (&tf.th);
881
882 if (!CommonMatch (interp, handle, format, &fileWidth, &fileHeight, &tf.th)) {
883 return TCL_ERROR;
884 }
885
886 if (ParseFormatOpts (interp, format, &opts) == TCL_ERROR) {
887 return TCL_ERROR;
888 }
889
890 if (opts.verbose) {
891 printImgInfo (&tf.th, &opts, filename, "Reading image:");
892 }
893
894 if ((srcX + width) > fileWidth) {
895 outWidth = fileWidth - srcX;
896 } else {
897 outWidth = width;
898 }
899 if ((srcY + height) > fileHeight) {
900 outHeight = fileHeight - srcY;
901 } else {
902 outHeight = height;
903 }
904 if ((outWidth <= 0) || (outHeight <= 0)
905 || (srcX >= fileWidth) || (srcY >= fileHeight)) {
906 return TCL_OK;
907 }
908
909 byteOrder = opts.useHeader? tf.th.byteOrder: opts.byteOrder;
910 scanOrder = opts.useHeader? tf.th.scanOrder: opts.scanOrder;
911 pixelType = opts.useHeader? tf.th.pixelType: opts.pixelType;
912 swapBytes = (( tkimg_IsIntel () && (byteOrder != INTEL)) ||
913 (!tkimg_IsIntel () && (byteOrder == INTEL)));
914 fastMode = (opts.mapMode == IMG_MAP_NONE &&
915 pixelType == TYPE_UBYTE && scanOrder == TOP_DOWN &&
916 fileWidth == width && fileHeight == height);
917
918 if (!fastMode) {
919 tkimg_CreateGammaTable (opts.gamma, gtable);
920 }
921
922 switch (pixelType) {
923 case TYPE_FLOAT: {
924 tf.floatBuf = (Float *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (Float));
925 tkimg_ReadFloatFile (handle, tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
926 swapBytes, opts.verbose, opts.mapMode != IMG_MAP_NONE,
927 minVals, maxVals, opts.saturation);
928 break;
929 }
930 case TYPE_USHORT: {
931 tf.ushortBuf = (UShort *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UShort));
932 tkimg_ReadUShortFile (handle, tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
933 swapBytes, opts.verbose, opts.mapMode != IMG_MAP_NONE, minVals, maxVals);
934 break;
935 }
936 case TYPE_UBYTE: {
937 tf.ubyteBuf = (UByte *)ckalloc (fileWidth*fileHeight*tf.th.nChans*sizeof (UByte));
938 tkimg_ReadUByteFile (handle, tf.ubyteBuf, fileWidth, fileHeight, tf.th.nChans,
939 opts.verbose, opts.mapMode != IMG_MAP_NONE, minVals, maxVals);
940 break;
941 }
942 }
943 switch (opts.mapMode) {
944 case IMG_MAP_NONE: {
945 for (c=0; c<tf.th.nChans; c++) {
946 minVals[c] = 0.0;
947 maxVals[c] = 255.0;
948 }
949 break;
950 }
951 case IMG_MAP_MINMAX: {
952 if (opts.minVal >= 0.0) {
953 for (c=0; c<tf.th.nChans; c++) {
954 minVals[c] = opts.minVal;
955 }
956 }
957 if (opts.maxVal >= 0.0) {
958 for (c=0; c<tf.th.nChans; c++) {
959 maxVals[c] = opts.maxVal;
960 }
961 }
962 break;
963 }
964 case IMG_MAP_AGC: {
965 /* Nothing to do. Saturation is considered on tkimg_ReadFloatFile. */
966 break;
967 }
968 }
969
970 switch (pixelType) {
971 case TYPE_FLOAT: {
972 tkimg_RemapFloatValues (
973 tf.floatBuf, fileWidth, fileHeight, tf.th.nChans,
974 minVals, maxVals, opts.mapMode == IMG_MAP_AGC? opts.cutOff: -1.0,
975 opts.printAgc
976 );
977 break;
978 }
979 case TYPE_USHORT: {
980 tkimg_RemapUShortValues (
981 tf.ushortBuf, fileWidth, fileHeight, tf.th.nChans,
982 minVals, maxVals
983 );
984 break;
985 }
986 }
987
988 if (tkimg_PhotoExpand (interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
989 rawClose (&tf, fastMode);
990 return TCL_ERROR;
991 }
992
993 if (fastMode) {
994 tf.pixbuf = tf.ubyteBuf;
995 } else {
996 tf.pixbuf = (UByte *) ckalloc (fileWidth * tf.th.nChans);
997 }
998
999 block.pixelSize = tf.th.nChans;
1000 block.pitch = fileWidth * tf.th.nChans;
1001 block.width = outWidth;
1002 block.height = fastMode? outHeight: 1;
1003 block.offset[0] = 0;
1004 block.offset[1] = (tf.th.nChans > 1? 1: 0);
1005 block.offset[2] = (tf.th.nChans > 1? 2: 0);
1006 block.offset[3] = (tf.th.nChans == 4 && matte? 3: 0);
1007 block.pixelPtr = tf.pixbuf + srcX * tf.th.nChans;
1008
1009 stopY = srcY + outHeight;
1010 outY = destY;
1011
1012 if (fastMode) {
1013 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
1014 width, height,
1015 block.offset[3]?
1016 TK_PHOTO_COMPOSITE_SET:
1017 TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
1018 result = TCL_ERROR;
1019 }
1020 } else {
1021 for (y=0; y<stopY; y++) {
1022 pixbufPtr = tf.pixbuf;
1023 switch (pixelType) {
1024 case TYPE_FLOAT: {
1025 if (scanOrder == BOTTOM_UP) {
1026 floatBufPtr = tf.floatBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1027 } else {
1028 floatBufPtr = tf.floatBuf + y * fileWidth * tf.th.nChans;
1029 }
1030 tkimg_FloatToUByte (fileWidth * tf.th.nChans, floatBufPtr,
1031 opts.gamma != 1.0? gtable: NULL, pixbufPtr);
1032 floatBufPtr += fileWidth * tf.th.nChans;
1033 break;
1034 }
1035 case TYPE_USHORT: {
1036 if (scanOrder == BOTTOM_UP) {
1037 ushortBufPtr = tf.ushortBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1038 } else {
1039 ushortBufPtr = tf.ushortBuf + y * fileWidth * tf.th.nChans;
1040 }
1041 tkimg_UShortToUByte (fileWidth * tf.th.nChans, ushortBufPtr,
1042 opts.gamma != 1.0? gtable: NULL, pixbufPtr);
1043 ushortBufPtr += fileWidth * tf.th.nChans;
1044 break;
1045 }
1046 case TYPE_UBYTE: {
1047 if (scanOrder == BOTTOM_UP) {
1048 ubyteBufPtr = tf.ubyteBuf + (fileHeight -1 - y) * fileWidth * tf.th.nChans;
1049 } else {
1050 ubyteBufPtr = tf.ubyteBuf + y * fileWidth * tf.th.nChans;
1051 }
1052 for (x=0; x<fileWidth * tf.th.nChans; x++) {
1053 pixbufPtr[x] = ubyteBufPtr[x];
1054 }
1055 ubyteBufPtr += fileWidth * tf.th.nChans;
1056 break;
1057 }
1058 }
1059 if (y >= srcY) {
1060 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY,
1061 width, 1,
1062 block.offset[3]?
1063 TK_PHOTO_COMPOSITE_SET:
1064 TK_PHOTO_COMPOSITE_OVERLAY) == TCL_ERROR) {
1065 result = TCL_ERROR;
1066 break;
1067 }
1068 outY++;
1069 }
1070 }
1071 }
1072 rawClose (&tf, fastMode);
1073 return result;
1074 }
1075
ChnWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)1076 static int ChnWrite(
1077 Tcl_Interp *interp,
1078 const char *filename,
1079 Tcl_Obj *format,
1080 Tk_PhotoImageBlock *blockPtr
1081 ) {
1082 Tcl_Channel chan;
1083 tkimg_MFile handle;
1084 int result;
1085
1086 chan = tkimg_OpenFileChannel (interp, filename, 0644);
1087 if (!chan) {
1088 return TCL_ERROR;
1089 }
1090
1091 handle.data = (char *) chan;
1092 handle.state = IMG_CHAN;
1093
1094 result = CommonWrite (interp, filename, format, &handle, blockPtr);
1095 if (Tcl_Close(interp, chan) == TCL_ERROR) {
1096 return TCL_ERROR;
1097 }
1098 return result;
1099 }
1100
StringWrite(Tcl_Interp * interp,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)1101 static int StringWrite(
1102 Tcl_Interp *interp,
1103 Tcl_Obj *format,
1104 Tk_PhotoImageBlock *blockPtr
1105 ) {
1106 tkimg_MFile handle;
1107 int result;
1108 Tcl_DString data;
1109
1110 Tcl_DStringInit(&data);
1111 tkimg_WriteInit (&data, &handle);
1112 result = CommonWrite (interp, "InlineData", format, &handle, blockPtr);
1113 tkimg_Putc(IMG_DONE, &handle);
1114
1115 if (result == TCL_OK) {
1116 Tcl_DStringResult(interp, &data);
1117 } else {
1118 Tcl_DStringFree(&data);
1119 }
1120 return result;
1121 }
1122
CommonWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,tkimg_MFile * handle,Tk_PhotoImageBlock * blockPtr)1123 static int CommonWrite(
1124 Tcl_Interp *interp,
1125 const char *filename,
1126 Tcl_Obj *format,
1127 tkimg_MFile *handle,
1128 Tk_PhotoImageBlock *blockPtr
1129 ) {
1130 Int x, y;
1131 Int redOffset, greenOffset, blueOffset, alphaOffset;
1132 UByte *pixelPtr, *rowPixPtr;
1133 RAWFILE tf;
1134 FMTOPT opts;
1135 UByte *ubyteBufPtr;
1136 Int bytesPerLine;
1137
1138 memset (&tf, 0, sizeof (RAWFILE));
1139 if (ParseFormatOpts (interp, format, &opts) == TCL_ERROR) {
1140 return TCL_ERROR;
1141 }
1142
1143 redOffset = 0;
1144 greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1145 blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
1146 alphaOffset = blockPtr->offset[0];
1147
1148 if (alphaOffset < blockPtr->offset[2]) {
1149 alphaOffset = blockPtr->offset[2];
1150 }
1151 if (++alphaOffset < blockPtr->pixelSize) {
1152 alphaOffset -= blockPtr->offset[0];
1153 } else {
1154 alphaOffset = 0;
1155 }
1156
1157 initHeader (&tf.th);
1158 tf.th.width = blockPtr->width;
1159 tf.th.height = blockPtr->height;
1160 tf.th.nChans = opts.nchan;
1161 tf.th.scanOrder = opts.scanOrder;
1162 tf.th.pixelType = TYPE_UBYTE;
1163
1164 writeHeader (handle, &tf.th);
1165 bytesPerLine = blockPtr->width * tf.th.nChans * sizeof (UByte);
1166 tf.ubyteBuf = (UByte *)ckalloc (bytesPerLine);
1167
1168 rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0];
1169 for (y = 0; y < blockPtr->height; y++) {
1170 ubyteBufPtr = tf.ubyteBuf;
1171 pixelPtr = rowPixPtr;
1172 if (tf.th.nChans == 1) {
1173 for (x=0; x<blockPtr->width; x++) {
1174 *ubyteBufPtr = pixelPtr[redOffset];
1175 ubyteBufPtr++;
1176 pixelPtr += blockPtr->pixelSize;
1177 }
1178 } else {
1179 for (x=0; x<blockPtr->width; x++) {
1180 *(ubyteBufPtr++) = pixelPtr[redOffset];
1181 *(ubyteBufPtr++) = pixelPtr[greenOffset];
1182 *(ubyteBufPtr++) = pixelPtr[blueOffset];
1183 if (tf.th.nChans == 4) {
1184 /* Have a matte channel and write it. */
1185 *(ubyteBufPtr++) = pixelPtr[alphaOffset];
1186 }
1187 pixelPtr += blockPtr->pixelSize;
1188 }
1189 }
1190 if (tkimg_Write2(handle, (char *)tf.ubyteBuf, bytesPerLine) != bytesPerLine) {
1191 rawClose (&tf, FALSE);
1192 return TCL_ERROR;
1193 }
1194 rowPixPtr += blockPtr->pitch;
1195 }
1196 if (opts.verbose)
1197 printImgInfo (&tf.th, &opts, filename, "Saving image:");
1198 rawClose (&tf, FALSE);
1199 return TCL_OK;
1200 }
1201
1202