1/* STARTHEADER
2 *
3 * File :       sgi.c
4 *
5 * Author :     Paul Obermeier (paul@poSoft.de)
6 *
7 * Date :       Wed Nov 22 21:45:17 CET 2000
8 *
9 * Copyright :  (C) 2000-2002 Paul Obermeier
10 *
11 * Description :
12 *
13 * A photo image handler for Silicon Graphics' native file format.
14 *
15 * The following image types are supported:
16 *
17 * 24-bit pixels: True-color (RGB, each channel 8 bit).
18 * 32-bit pixels: True-color with alpha channel (RGBA, each channel 8 bit).
19 * 48-bit pixels: True-color (RGB, each channel 16 bit).
20 * 64-bit pixels: True-color with alpha channel (RGBA, each channel 16 bit).
21 *
22 * List of currently supported features:
23 *
24 * Type   |     Read      |     Write     |
25 *        | -file | -data | -file | -data |
26 * ----------------------------------------
27 * 24-bit | Yes   | Yes*  | Yes   | Yes*  |
28 * 32-bit | Yes   | Yes*  | Yes   | Yes*  |
29 * 48-bit | Yes   | Yes*  | No    | No    |
30 * 64-bit | Yes   | Yes*  | No    | No    |
31 *
32 * *: Implemented by reading/writing from/to a temporary file. This will
33 *    be slow for larger images.
34 *
35 * All images types may be either uncompressed or run-length encoded.
36 *
37 *
38 * The following format options are available:
39 *
40 * Read  SGI image: "sgi -matte <bool> -verbose <bool>"
41 * Write SGI image: "sgi -matte <bool> -verbose <bool> -compression <type>"
42 *
43 * -matte <bool>:       If set to false, a matte (alpha) channel is ignored
44 *                      during reading or writing. Default is true.
45 * -verbose <bool>:     If set to true, additional information about the file
46 *                      format is printed to stdout. Default is false.
47 * -compression <type>: Set the compression mode to either "none" or "rle".
48 *                      Default is "rle".
49 *
50 * Notes:
51 *
52 * - Parts of this code are taken from Paul Haeberli's original
53 *   image library code, written in 1984.
54 *
55 * - Due to the heavy use of file seeks in Haeberli's code and the behaviour
56 *   of Tcl_Seek on Windows when writing to files (sounds like smashing your
57 *   HD), there is some workaround to use fseek and fwrite instead.
58 *   See "#define TCLSEEK_WORKAROUND".
59 *
60 * ENDHEADER
61 *
62 * $Id: sgi.c 251 2010-04-28 13:28:28Z nijtmans $
63 *
64 */
65
66/* #define DEBUG_LOCAL */
67
68/*
69 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
70 */
71
72#include "init.c"
73
74
75#ifdef WIN32
76#   define TCLSEEK_WORKAROUND
77#endif
78
79#ifdef TCLSEEK_WORKAROUND
80    static int ioMode = 0; /* Needed for Windows patch */
81
82    static int MyWrite (Tcl_Channel chan, char *buf, int size)
83    {
84	if (1 == fwrite(buf, size, 1, (FILE *)chan)) {
85	    return size;
86	} else {
87	    return -1;
88	}
89    }
90
91    static int MyClose (Tcl_Interp *interp, Tcl_Channel chan)
92    {
93	if (0 == fclose((FILE *)chan)) {
94	    return TCL_OK;
95	} else {
96	    return TCL_ERROR;
97	}
98    }
99
100    static int MySeek (Tcl_Channel chan, int offset, int seekMode)
101    {
102	if (ioMode == 0) { /* Read mode */
103	    return Tcl_Seek (chan, offset, seekMode);
104	} else {
105	    return fseek((FILE *)chan, offset, seekMode);
106	}
107    }
108
109#   define MYCHANNEL Tcl_Channel
110#   undef Tcl_Seek
111#   define Tcl_Seek MySeek
112#   undef Tcl_Write
113#   define Tcl_Write MyWrite
114#   define MYCLOSE MyClose
115#else
116#   define MYCHANNEL Tcl_Channel
117#   define MYCLOSE Tcl_Close
118#endif
119
120/* Some defines and typedefs for compatibility reasons. */
121#define TRUE  1
122#define FALSE 0
123typedef unsigned char Boln;	/* Boolean value: TRUE or FALSE */
124typedef unsigned char UByte;	/* Unsigned  8 bit integer */
125typedef char  Byte;		/* Signed    8 bit integer */
126typedef short Short;		/* Signed   16 bit integer */
127typedef unsigned short UShort;	/* Unsigned 16 bit integer */
128typedef int Int;		/* Signed   32 bit integer */
129typedef unsigned int UInt;	/* Unsigned 32 bit integer */
130
131
132/* Start of original code from SGI image library, slightly modified. */
133
134#define IMAGIC        0x01DA
135
136/* colormap of images */
137#define CM_NORMAL               0       /* file contains rows of values which
138                                         * are either RGB values (zsize == 3)
139                                         * or greyramp values (zsize == 1) */
140#define CM_DITHERED             1
141#define CM_SCREEN               2       /* file contains data which is a screen
142                                         * image; getrow returns buffer which
143                                         * can be displayed directly with
144                                         * writepixels */
145#define CM_COLORMAP             3       /* a colormap file */
146
147#define TYPEMASK                0xff00
148#define BPPMASK                 0x00ff
149#define ITYPE_UNCOMPRESSED      0x0000
150#define ITYPE_RLE               0x0100
151#define ISRLE(type)             (((type) & 0xff00) == ITYPE_RLE)
152#define ISUNCOMPRESSED(type)    (((type) & 0xff00) == ITYPE_UNCOMPRESSED)
153#define BPP(type)               ((type) & BPPMASK)
154#define RLE(bpp)                (ITYPE_RLE | (bpp))
155#define UNCOMPRESSED(bpp)       (ITYPE_UNCOMPRESSED | (bpp))
156#define IBUFSIZE(pixels)        ((pixels+(pixels>>6))<<2)
157#define RLE_NOP                 0x00
158
159#define ierror(p)               (((p)->flags&_IOERR)!=0)
160#define ifileno(p)              ((p)->file)
161#define getpix(p)               (--(p)->cnt>=0 ? *(p)->ptr++ : ifilbuf(p))
162#define putpix(p,x)             (--(p)->cnt>=0 \
163                                    ? ((int)(*(p)->ptr++=(unsigned)(x))) \
164                                    : iflsbuf(p,(unsigned)(x)))
165
166/* The number of bytes of the IMAGE struct, which must be written to disk.
167 * All other information is needed only internally. It is filled with zeros
168 * on disk. */
169#define RELEVANT_HEADER_BYTES 108
170
171typedef struct {
172    UShort    imagic;         /* stuff saved on disk . . */
173    UShort    type;
174    UShort    dim;
175    UShort    xsize;
176    UShort    ysize;
177    UShort    zsize;
178    UInt      min;
179    UInt      max;
180    UInt      wastebytes;
181    char      name[80];
182    UInt      colormap;
183
184    MYCHANNEL file;	/* Stuff not stored in the file. */
185    UShort    flags;
186    Short     dorev;
187    Short     x;
188    Short     y;
189    Short     z;
190    Short     cnt;
191    UShort    *ptr;
192    UShort    *base;
193    UShort    *tmpbuf;
194    UInt      offset;
195    UInt      rleend;         /* for rle images */
196    UInt      *rowstart;      /* for rle images */
197    Int       *rowsize;       /* for rle images */
198    char      dummy[512-146]; /* Fill bytes, so that this structure is greater
199                                 than 512 bytes */
200} IMAGE;
201
202#if !defined (_IOWRT)
203#   define _IOWRT  1
204#endif
205#if !defined (_IOREAD)
206#   define _IOREAD 2
207#endif
208#if !defined (_IORW)
209#   define _IORW   4
210#endif
211#if !defined (_IOERR)
212#   define _IOERR  8
213#endif
214#if !defined (_IOEOF)
215#   define _IOEOF 16
216#endif
217
218static int img_badrow(IMAGE *image, unsigned int y, unsigned int z);
219static int img_write(IMAGE *image, char *buffer,int count);
220static int img_writeheader(IMAGE *image);
221static int iflush(IMAGE *image);
222static unsigned short *ibufalloc(IMAGE *image);
223static unsigned int img_optseek(IMAGE *image, unsigned int offset);
224static int imgopen(int, MYCHANNEL, IMAGE *, const char *,unsigned int, unsigned int,
225		unsigned int, unsigned int, unsigned int);
226static int getrow(IMAGE *image, unsigned short *buffer,
227		unsigned int y, unsigned int z);
228static int putrow(IMAGE *image, unsigned short *buffer,
229		unsigned int y, unsigned int z);
230
231/*	error handler for the image library.  If the iseterror() routine
232	has been called, sprintf's the args into a string and calls the
233	error function.  Otherwise calls fprintf with the args and then
234	exit.  This allows 'old' programs to assume that no errors
235	ever need be worried about, while programs that know how and
236	want to can handle the errors themselves.  Olson, 11/88
237*/
238static void i_errhdlr(fmt, a1, a2, a3, a4)  /* most args currently used is 2 */
239char *fmt;
240{
241    /* fprintf(stderr, fmt); */
242    return;
243}
244
245/*
246 *	isetname and isetcolormap -
247 *
248 *				Paul Haeberli - 1984
249 *
250 */
251
252static void isetname(IMAGE *image, const char *name)
253{
254    strncpy(image->name,name,80);
255}
256
257/* This function is commented out because it is not used anywhere
258static void isetcolormap(IMAGE *image, int colormap)
259{
260    image->colormap = colormap;
261}
262*/
263
264static void cvtshorts( buffer, n)
265register unsigned short buffer[];
266register int n;
267{
268    register short i;
269    register int nshorts = n>>1;
270    register unsigned short swrd;
271
272    for(i=0; i<nshorts; i++) {
273	swrd = *buffer;
274	*buffer++ = (swrd>>8) | (swrd<<8);
275    }
276}
277
278static void cvtlongs( buffer, n)
279register int buffer[];
280register int n;
281{
282    register short i;
283    register int nlongs = n>>2;
284    register int lwrd;
285    Byte *bytePtr;
286
287    bytePtr = (Byte *) buffer;
288    for(i=0; i<nlongs; i++) {
289	lwrd = buffer[i];
290	*bytePtr = (Byte) (lwrd >> 24); bytePtr++;
291	*bytePtr = (Byte) (lwrd >> 16); bytePtr++;
292	*bytePtr = (Byte) (lwrd >> 8);  bytePtr++;
293	*bytePtr = (Byte) (lwrd);       bytePtr++;
294    }
295}
296
297static void cvtimage( buffer )
298int buffer[];
299{
300    cvtshorts((unsigned short *)buffer,12);
301    cvtlongs(buffer+3,12);
302}
303
304/*
305 *	iopen -
306 *
307 *				Paul Haeberli - 1984
308 *
309 */
310
311static unsigned short *ibufalloc(IMAGE *image)
312{
313    return (unsigned short *)malloc(IBUFSIZE(image->xsize));
314}
315
316static int imgOpenRead (MYCHANNEL file, IMAGE *image, const char *mode)
317{
318#ifdef TCLSEEK_WORKAROUND
319	ioMode = 0;
320#endif
321    return imgopen (0, file, image, mode, 0, 0, 0, 0, 0);
322}
323
324static int imgOpenWrite (MYCHANNEL file, IMAGE *image, const char *mode,
325                     unsigned int type, unsigned int dim,
326                     unsigned int xsize, unsigned int ysize, unsigned int zsize)
327{
328#ifdef TCLSEEK_WORKAROUND
329	ioMode = 1;
330#endif
331    return imgopen (0, file, image, mode, type, dim, xsize, ysize, zsize);
332}
333
334static int imgopen(int f, MYCHANNEL file, IMAGE *image, const char *mode,
335		unsigned int type, unsigned int dim,
336		unsigned int xsize, unsigned int ysize, unsigned int zsize)
337{
338    register int rw;
339    int tablesize;
340    register int i, max;
341
342    rw = mode[1] == '+';
343    if(rw) {
344	i_errhdlr("iopen: read/write mode not supported\n");
345	return 0;
346    }
347    if (*mode=='w') {
348	image->type = type;
349	image->xsize = xsize;
350	image->ysize = 1;
351	image->zsize = 1;
352	if (dim>1)
353	    image->ysize = ysize;
354	if (dim>2)
355	    image->zsize = zsize;
356	if(image->zsize == 1) {
357	    image->dim = 2;
358	    if(image->ysize == 1)
359		image->dim = 1;
360	} else {
361	    image->dim = 3;
362	}
363	image->min = 10000000;
364	image->max = 0;
365	isetname(image,"no name");
366	image->wastebytes = 0;
367	if (512 != Tcl_Write (file, (char *)image, 512)) {
368	    i_errhdlr("iopen: error on write of image header\n");
369	    return 0;
370	}
371    } else {
372	if (512 != Tcl_Read (file, (char *)image, 512)) {
373	    i_errhdlr("iopen: error on read of image header\n");
374	    return 0;
375	}
376	if( ((image->imagic>>8) | ((image->imagic&0xff)<<8)) == IMAGIC ) {
377	    image->dorev = 1;
378	    cvtimage((int *)image);
379	} else
380	    image->dorev = 0;
381	if (image->imagic != IMAGIC) {
382	    i_errhdlr("iopen: bad magic in image file %x\n",image->imagic);
383	    return 0;
384	}
385    }
386    if (rw)
387	image->flags = _IORW;
388    else if (*mode != 'r')
389	image->flags = _IOWRT;
390    else
391	image->flags = _IOREAD;
392    if(ISRLE(image->type)) {
393	tablesize = image->ysize*image->zsize*sizeof(int);
394	image->rowstart = (unsigned int *)malloc(tablesize);
395	image->rowsize = (int *)malloc(tablesize);
396	if( image->rowstart == 0 || image->rowsize == 0 ) {
397	    i_errhdlr("iopen: error on table alloc\n");
398	    return 0;
399	}
400	image->rleend = 512L+2*tablesize;
401	if (*mode=='w') {
402	    max = image->ysize*image->zsize;
403	    for(i=0; i<max; i++) {
404		image->rowstart[i] = 0;
405		image->rowsize[i] = -1;
406	    }
407	} else {
408	    tablesize = image->ysize*image->zsize*sizeof(int);
409	    Tcl_Seek (file, 512L, 0);
410	    if (tablesize != Tcl_Read (file, (char *)image->rowstart, tablesize)) {
411		i_errhdlr("iopen: error on read of rowstart\n");
412		return 0;
413	    }
414	    if(image->dorev)
415		cvtlongs(image->rowstart,tablesize);
416	    if (Tcl_Read (file, (char *)image->rowsize, tablesize) != tablesize) {
417		i_errhdlr("iopen: error on read of rowsize\n");
418		return 0;
419	    }
420	    if(image->dorev)
421		cvtlongs(image->rowsize,tablesize);
422	}
423    }
424    image->cnt = 0;
425    image->ptr = 0;
426    image->base = 0;
427    if( (image->tmpbuf = ibufalloc(image)) == 0 ) {
428	i_errhdlr("iopen: error on tmpbuf alloc %d\n",image->xsize);
429	return 0;
430    }
431    image->x = image->y = image->z = 0;
432    image->file = file;
433    image->offset = 512L;			/* set up for img_optseek */
434    Tcl_Seek (image->file, 512L, 0);
435    return 1;
436}
437
438
439/*
440 *	iclose and iflush -
441 *
442 *				Paul Haeberli - 1984
443 *
444 */
445
446static int iclose(IMAGE *image)
447{
448    int tablesize;
449
450    iflush(image);
451    img_optseek(image, 0);
452    if (image->flags&_IOWRT) {
453	if(image->dorev)
454	    cvtimage((int *)image);
455	if ( !img_writeheader(image)) {
456	    i_errhdlr("iclose: error on write of image header\n");
457	    return EOF;
458	}
459	if(image->dorev)
460	    cvtimage((int *)image);
461	if(ISRLE(image->type)) {
462	    img_optseek(image, 512L);
463	    tablesize = image->ysize*image->zsize*sizeof(int);
464	    if(image->dorev)
465		cvtlongs(image->rowstart,tablesize);
466	    if (img_write(image,(char *)(image->rowstart),tablesize) != tablesize) {
467		i_errhdlr("iclose: error on write of rowstart\n");
468		return EOF;
469	    }
470	    if(image->dorev)
471		cvtlongs(image->rowsize,tablesize);
472	    if (img_write(image,(char *)(image->rowsize),tablesize) != tablesize) {
473		i_errhdlr("iclose: error on write of rowsize\n");
474		return EOF;
475	    }
476	}
477    }
478    if(image->base) {
479	free(image->base);
480	image->base = 0;
481    }
482    if(image->tmpbuf) {
483	free(image->tmpbuf);
484	image->tmpbuf = 0;
485    }
486    if(ISRLE(image->type)) {
487	free(image->rowstart);
488	image->rowstart = 0;
489	free(image->rowsize);
490	image->rowsize = 0;
491    }
492    return 0;
493}
494
495static int iflush(IMAGE *image)
496{
497    unsigned short *base;
498
499    if ( (image->flags&_IOWRT)
500     && (base=image->base)!=NULL && (image->ptr-base)>0) {
501	    if (putrow(image, base, image->y,image->z)!=image->xsize) {
502		    image->flags |= _IOERR;
503		    return(EOF);
504	    }
505    }
506    return(0);
507}
508
509/*
510 *	ifilbuf -
511 *
512 *				Paul Haeberli - 1984
513 *
514 */
515
516/* This function is commented out because it is not used anywhere
517static int ifilbuf(IMAGE *image)
518{
519    if ((image->flags&_IOREAD) == 0)
520	return(EOF);
521    if (image->base==NULL) {
522	if ((image->base = ibufalloc(image)) == NULL) {
523	    i_errhdlr("can't alloc image buffer\n");
524	    return EOF;
525	}
526    }
527    image->cnt = getrow(image,image->base,image->y,image->z);
528    image->ptr = image->base;
529    if (--image->cnt < 0) {
530	if (image->cnt == -1) {
531	    image->flags |= _IOEOF;
532	    if (image->flags & _IORW)
533		image->flags &= ~_IOREAD;
534	} else
535	    image->flags |= _IOERR;
536	image->cnt = 0;
537	return -1;
538    }
539    if(++image->y >= image->ysize) {
540	image->y = 0;
541	if(++image->z >= image->zsize) {
542	    image->z = image->zsize-1;
543	    image->flags |= _IOEOF;
544	    return -1;
545	}
546    }
547    return *image->ptr++ & 0xffff;
548}
549*/
550
551/*
552 *	iflsbuf -
553 *
554 *				Paul Haeberli - 1984
555 *
556 */
557
558/* This function is commented out because it is not used anywhere
559static unsigned int iflsbuf(IMAGE *image, unsigned int c)
560{
561    register unsigned short *base;
562    register int n, rn;
563
564    if ((image->flags&_IOWRT)==0)
565	return(EOF);
566    if ((base=image->base)==NULL) {
567	if ((image->base=base=ibufalloc(image)) == NULL) {
568	    i_errhdlr("flsbuf: error on buf alloc\n");
569	    return EOF;
570	}
571	rn = n = 0;
572    } else if ((rn = n = image->ptr - base) > 0)  {
573	n = putrow(image,base,image->y,image->z);
574	if(++image->y >= image->ysize) {
575	    image->y = 0;
576	    if(++image->z >= image->zsize) {
577		image->z = image->zsize-1;
578		image->flags |= _IOEOF;
579		return -1;
580	    }
581	}
582    }
583    image->cnt = image->xsize-1;
584    *base++ = c;
585    image->ptr = base;
586    if (rn != n) {
587	image->flags |= _IOERR;
588	return(EOF);
589    }
590    return(c);
591}
592*/
593
594
595/*
596 *	img_seek, img_write, img_read, img_optseek -
597 *
598 *				Paul Haeberli - 1984
599 *
600 */
601
602static unsigned int img_seek(IMAGE *image, unsigned int y, unsigned int z)
603{
604    if(img_badrow(image,y,z)) {
605	i_errhdlr("img_seek: row number out of range\n");
606	return EOF;
607    }
608    image->x = 0;
609    image->y = y;
610    image->z = z;
611    if(ISUNCOMPRESSED(image->type)) {
612	switch(image->dim) {
613	    case 1:
614		return img_optseek(image, 512L);
615	    case 2:
616		return img_optseek(image,512L+(y*image->xsize)*BPP(image->type));
617	    case 3:
618		return img_optseek(image,
619		    512L+(y*image->xsize+z*image->xsize*image->ysize)*
620							BPP(image->type));
621	    default:
622		i_errhdlr("img_seek: weird dim\n");
623		break;
624	}
625    } else if(ISRLE(image->type)) {
626	switch(image->dim) {
627	    case 1:
628		return img_optseek(image, image->rowstart[0]);
629	    case 2:
630		return img_optseek(image, image->rowstart[y]);
631	    case 3:
632		return img_optseek(image, image->rowstart[y+z*image->ysize]);
633	    default:
634		i_errhdlr("img_seek: weird dim\n");
635		break;
636	}
637    } else
638	i_errhdlr("img_seek: weird image type\n");
639    return((unsigned int)-1);
640}
641
642static int img_badrow(IMAGE *image, unsigned int y, unsigned int z)
643{
644    if(y>=image->ysize || z>=image->zsize)
645	return 1;
646    else
647        return 0;
648}
649
650static int img_write(IMAGE *image, char *buffer,int count)
651{
652    int retval;
653
654    retval = Tcl_Write (image->file, buffer, count);
655    if(retval == count)
656	image->offset += count;
657    else
658	image->offset = -1;
659    return retval;
660}
661
662static int img_writeheader(IMAGE *image)
663{
664    int retval;
665
666    retval = Tcl_Write (image->file, (char *)image, RELEVANT_HEADER_BYTES);
667    if(retval == RELEVANT_HEADER_BYTES)
668	image->offset += sizeof (IMAGE);
669    else
670	image->offset = -1;
671    return retval;
672}
673
674static int img_read(IMAGE *image, char *buffer, int count)
675{
676    int retval;
677
678    retval = Tcl_Read (image->file, buffer, count);
679    if (retval == count)
680	image->offset += count;
681    else
682	image->offset = -1;
683    return retval;
684}
685
686static unsigned int img_optseek(IMAGE *image, unsigned int offset)
687{
688    if(image->offset != offset) {
689       image->offset = offset;
690       return ((unsigned int) Tcl_Seek (image->file,offset,0));
691   }
692   return offset;
693}
694
695/*
696 *	getpix and putpix -
697 *
698 *				Paul Haeberli - 1984
699 *
700 */
701
702#undef getpix
703#undef putpix
704
705/* These functions are commented out because they are not used anywhere
706static int getpix(IMAGE *image)
707{
708    if(--(image)->cnt>=0)
709    	return (int)(*(image)->ptr++);
710    else
711	return ifilbuf(image);
712}
713
714static unsigned int putpix(IMAGE *image, unsigned int pix)
715{
716    if(--(image)->cnt>=0)
717        return (unsigned int)(*(image)->ptr++ = pix);
718    else
719	return iflsbuf(image,pix);
720}
721*/
722
723/*
724 *	img_getrowsize, img_setrowsize, img_rle_compact, img_rle_expand -
725 *
726 *				Paul Haeberli - 1984
727 *
728 */
729
730static int img_getrowsize(IMAGE *image)
731{
732    switch(image->dim) {
733	case 1:
734	    return image->rowsize[0];
735	case 2:
736	    return image->rowsize[image->y];
737	case 3:
738	    return image->rowsize[image->y+image->z*image->ysize];
739    }
740    return -1;
741}
742
743static void img_setrowsize(IMAGE *image, int cnt, int y, int z)
744{
745    int *sizeptr;
746
747    if(img_badrow(image,y,z))
748	return;
749    switch(image->dim) {
750	case 1:
751	    sizeptr = &image->rowsize[0];
752	    image->rowstart[0] = image->rleend;
753	    break;
754	case 2:
755	    sizeptr = &image->rowsize[y];
756	    image->rowstart[y] = image->rleend;
757	    break;
758	case 3:
759	    sizeptr = &image->rowsize[y+z*image->ysize];
760	    image->rowstart[y+z*image->ysize] = image->rleend;
761	    break;
762        default:
763	    i_errhdlr ("img_setrowsize: bad dim: %d\n", image->dim);
764	    return;
765    }
766    if(*sizeptr != -1)
767	image->wastebytes += *sizeptr;
768    *sizeptr = cnt;
769    image->rleend += cnt;
770}
771
772#define docompact 							\
773	while(iptr<ibufend) {						\
774	    sptr = iptr;						\
775	    iptr += 2;							\
776	    while((iptr<ibufend)&&((iptr[-2]!=iptr[-1])||(iptr[-1]!=iptr[0])))\
777		iptr++;							\
778	    iptr -= 2;							\
779	    count = iptr-sptr;						\
780	    while(count) {						\
781		todo = count>126 ? 126:count; 				\
782		count -= todo;						\
783		*optr++ = 0x80|todo;					\
784		while(todo--)						\
785		    *optr++ = *sptr++;					\
786	    }								\
787	    sptr = iptr;						\
788	    cc = *iptr++;						\
789	    while( (iptr<ibufend) && (*iptr == cc) )			\
790		iptr++;							\
791	    count = iptr-sptr;						\
792	    while(count) {						\
793		todo = count>126 ? 126:count; 				\
794		count -= todo;						\
795		*optr++ = todo;						\
796		*optr++ = cc;						\
797	    }								\
798	}								\
799	*optr++ = 0;
800
801static int img_rle_compact(unsigned short *expbuf, int ibpp,
802			unsigned short *rlebuf, int obpp, int cnt)
803{
804    if(ibpp == 1 && obpp == 1) {
805	register unsigned char *iptr = (unsigned char *)expbuf;
806	register unsigned char *ibufend = iptr+cnt;
807	register unsigned char *sptr;
808	register unsigned char *optr = (unsigned char *)rlebuf;
809	register short todo, cc;
810	register int count;
811
812	docompact;
813	return optr - (unsigned char *)rlebuf;
814    } else if(ibpp == 1 && obpp == 2) {
815	register unsigned char *iptr = (unsigned char *)expbuf;
816	register unsigned char *ibufend = iptr+cnt;
817	register unsigned char *sptr;
818	register unsigned short *optr = rlebuf;
819	register short todo, cc;
820	register int count;
821
822	docompact;
823	return optr - rlebuf;
824    } else if(ibpp == 2 && obpp == 1) {
825	register unsigned short *iptr = expbuf;
826	register unsigned short *ibufend = iptr+cnt;
827	register unsigned short *sptr;
828	register unsigned char *optr = (unsigned char *)rlebuf;
829	register short todo, cc;
830	register int count;
831
832	docompact;
833	return optr - (unsigned char *)rlebuf;
834    } else if(ibpp == 2 && obpp == 2) {
835	register unsigned short *iptr = expbuf;
836	register unsigned short *ibufend = iptr+cnt;
837	register unsigned short *sptr;
838	register unsigned short *optr = rlebuf;
839	register short todo, cc;
840	register int count;
841
842	docompact;
843	return optr - rlebuf;
844    } else  {
845	i_errhdlr("rle_compact: bad bpp: %d %d\n",ibpp,obpp);
846	return 0;
847    }
848}
849
850#define doexpand				\
851	while(1) {				\
852	    pixel = *iptr++;			\
853	    if ( !(count = (pixel & 0x7f)) )	\
854		return;				\
855	    if(pixel & 0x80) {			\
856	       while(count--)			\
857		    *optr++ = *iptr++;		\
858	    } else {				\
859	       pixel = *iptr++;			\
860	       while(count--)			\
861		    *optr++ = pixel;		\
862	    }					\
863	}
864
865static void img_rle_expand(unsigned short *rlebuf, int ibpp,
866			unsigned short *expbuf, int obpp)
867{
868    if(ibpp == 1 && obpp == 1) {
869	register unsigned char *iptr = (unsigned char *)rlebuf;
870	register unsigned char *optr = (unsigned char *)expbuf;
871	register unsigned short pixel,count;
872
873	doexpand;
874    } else if(ibpp == 1 && obpp == 2) {
875	register unsigned char *iptr = (unsigned char *)rlebuf;
876	register unsigned short *optr = expbuf;
877	register unsigned short pixel,count;
878
879	doexpand;
880    } else if(ibpp == 2 && obpp == 1) {
881	register unsigned short *iptr = rlebuf;
882	register unsigned char  *optr = (unsigned char *)expbuf;
883	register unsigned short pixel,count;
884
885	doexpand;
886    } else if(ibpp == 2 && obpp == 2) {
887	register unsigned short *iptr = rlebuf;
888	register unsigned short *optr = expbuf;
889	register unsigned short pixel,count;
890
891	doexpand;
892    } else
893	i_errhdlr("rle_expand: bad bpp: %d %d\n",ibpp,obpp);
894}
895
896/*
897 *	putrow, getrow -
898 *
899 *				Paul Haeberli - 1984
900 *
901 */
902
903static int putrow(IMAGE *image, unsigned short *buffer,
904		unsigned int y, unsigned int z)
905{
906    register unsigned short 	*sptr;
907    register unsigned char      *cptr;
908    register unsigned int x;
909    register unsigned int min, max;
910    register int cnt;
911
912    if( !(image->flags & (_IORW|_IOWRT)) )
913	return -1;
914    if(image->dim<3)
915	z = 0;
916    if(image->dim<2)
917	y = 0;
918    if(ISUNCOMPRESSED(image->type)) {
919	switch(BPP(image->type)) {
920	    case 1:
921		min = image->min;
922		max = image->max;
923		cptr = (unsigned char *)image->tmpbuf;
924		sptr = buffer;
925		for(x=image->xsize; x--;) {
926		    *cptr = *sptr++;
927		    if (*cptr > max) max = *cptr;
928		    if (*cptr < min) min = *cptr;
929		    cptr++;
930		}
931		image->min = min;
932		image->max = max;
933		img_seek(image,y,z);
934		cnt = image->xsize;
935		if (img_write(image,(char *)(image->tmpbuf),cnt) != cnt)
936		    return -1;
937		else
938		    return cnt;
939		/* NOTREACHED */
940
941	    case 2:
942		min = image->min;
943		max = image->max;
944		sptr = buffer;
945		for(x=image->xsize; x--;) {
946		    if (*sptr > max) max = *sptr;
947		    if (*sptr < min) min = *sptr;
948		    sptr++;
949		}
950		image->min = min;
951		image->max = max;
952		img_seek(image,y,z);
953		cnt = image->xsize<<1;
954		if(image->dorev)
955		    cvtshorts(buffer,cnt);
956		if (img_write(image,(char *)(buffer),cnt) != cnt) {
957		    if(image->dorev)
958			cvtshorts(buffer,cnt);
959		    return -1;
960		} else {
961		    if(image->dorev)
962			cvtshorts(buffer,cnt);
963		    return image->xsize;
964		}
965		/* NOTREACHED */
966
967	    default:
968		i_errhdlr("putrow: weird bpp\n");
969	}
970    } else if(ISRLE(image->type)) {
971	switch(BPP(image->type)) {
972	    case 1:
973		min = image->min;
974		max = image->max;
975		sptr = buffer;
976		for(x=image->xsize; x--;) {
977		    if (*sptr > max) max = *sptr;
978		    if (*sptr < min) min = *sptr;
979		    sptr++;
980		}
981		image->min = min;
982		image->max = max;
983		cnt = img_rle_compact(buffer,2,image->tmpbuf,1,image->xsize);
984		img_setrowsize(image,cnt,y,z);
985		img_seek(image,y,z);
986		if (img_write(image,(char *)(image->tmpbuf),cnt) != cnt)
987		    return -1;
988		else
989		    return image->xsize;
990		/* NOTREACHED */
991
992	    case 2:
993		min = image->min;
994		max = image->max;
995		sptr = buffer;
996		for(x=image->xsize; x--;) {
997		    if (*sptr > max) max = *sptr;
998		    if (*sptr < min) min = *sptr;
999		    sptr++;
1000		}
1001		image->min = min;
1002		image->max = max;
1003		cnt = img_rle_compact(buffer,2,image->tmpbuf,2,image->xsize);
1004		cnt <<= 1;
1005		img_setrowsize(image,cnt,y,z);
1006		img_seek(image,y,z);
1007		if(image->dorev)
1008		    cvtshorts(image->tmpbuf,cnt);
1009		if (img_write(image,(char *)(image->tmpbuf),cnt) != cnt) {
1010		    if(image->dorev)
1011			cvtshorts(image->tmpbuf,cnt);
1012		    return -1;
1013		} else {
1014		    if(image->dorev)
1015			cvtshorts(image->tmpbuf,cnt);
1016		    return image->xsize;
1017		}
1018		/* NOTREACHED */
1019
1020	    default:
1021		i_errhdlr("putrow: weird bpp\n");
1022	}
1023    } else
1024	i_errhdlr("putrow: weird image type\n");
1025    return(-1);
1026}
1027
1028static int getrow(IMAGE *image, unsigned short *buffer,
1029		unsigned int y, unsigned int z)
1030{
1031    register short i;
1032    register unsigned char *cptr;
1033    register unsigned short *sptr;
1034    register short cnt;
1035
1036    if( !(image->flags & (_IORW|_IOREAD)) )
1037	return -1;
1038    if(image->dim<3)
1039	z = 0;
1040    if(image->dim<2)
1041	y = 0;
1042    img_seek(image, y, z);
1043    if(ISUNCOMPRESSED(image->type)) {
1044	switch(BPP(image->type)) {
1045	    case 1:
1046		if (img_read(image,(char *)image->tmpbuf,image->xsize)
1047							    != image->xsize)
1048		    return -1;
1049		else {
1050		    cptr = (unsigned char *)image->tmpbuf;
1051		    sptr = buffer;
1052		    for(i=image->xsize; i--;)
1053			*sptr++ = *cptr++;
1054		}
1055		return image->xsize;
1056		/* NOTREACHED */
1057
1058	    case 2:
1059		cnt = image->xsize<<1;
1060		if (img_read(image,(char *)(buffer),cnt) != cnt)
1061		    return -1;
1062		else {
1063		    if(image->dorev)
1064			cvtshorts(buffer,cnt);
1065		    return image->xsize;
1066		}
1067		/* NOTREACHED */
1068
1069	    default:
1070		i_errhdlr("getrow: weird bpp\n");
1071		break;
1072	}
1073    } else if(ISRLE(image->type)) {
1074	switch(BPP(image->type)) {
1075	    case 1:
1076		if( (cnt = img_getrowsize(image)) == -1 )
1077		    return -1;
1078		if( img_read(image,(char *)(image->tmpbuf),cnt) != cnt )
1079		    return -1;
1080		else {
1081		    img_rle_expand(image->tmpbuf,1,buffer,2);
1082		    return image->xsize;
1083		}
1084		/* NOTREACHED */
1085
1086	    case 2:
1087		if( (cnt = img_getrowsize(image)) == -1 )
1088		    return -1;
1089		if( cnt != img_read(image,(char *)(image->tmpbuf),cnt) )
1090		    return -1;
1091		else {
1092		    if(image->dorev)
1093			cvtshorts(image->tmpbuf,cnt);
1094		    img_rle_expand(image->tmpbuf,2,buffer,2);
1095		    return image->xsize;
1096		}
1097		/* NOTREACHED */
1098
1099	    default:
1100		i_errhdlr("getrow: weird bpp\n");
1101		break;
1102	}
1103    } else
1104	i_errhdlr("getrow: weird image type\n");
1105    return -1;
1106}
1107
1108/* End of original SGI image code */
1109
1110/* Structure to hold information about the SGI file being processed. */
1111typedef struct {
1112    IMAGE th;
1113    UByte *red,		/* Pointers to step through scanlines */
1114	  *green,
1115	  *blue,
1116	  *matte;
1117    UByte *redScan,	/* Buffer for one scanline: Red   channel */
1118	  *greenScan,	/* Buffer for one scanline: Green channel */
1119	  *blueScan,	/* Buffer for one scanline: Blue  channel */
1120	  *matteScan;	/* Buffer for one scanline: Matte channel */
1121    UByte *scanline;
1122    unsigned short *pixbuf;
1123} SGIFILE;
1124
1125/* This function determines at runtime, whether we have to switch bytes.
1126   The SGI image format expects data to be in big-endian format. */
1127
1128static int isIntel (void)
1129{
1130    unsigned long val = 513;
1131    /* On Intel (little-endian) systems this value is equal to "\01\02\00\00".
1132       On big-endian systems this value equals "\00\00\02\01" */
1133    return memcmp(&val, "\01\02", 2) == 0;
1134}
1135
1136static void sgiClose (SGIFILE *tf)
1137{
1138    if (tf->redScan)   ckfree ((char *)tf->redScan);
1139    if (tf->greenScan) ckfree ((char *)tf->greenScan);
1140    if (tf->blueScan)  ckfree ((char *)tf->blueScan);
1141    if (tf->matteScan) ckfree ((char *)tf->matteScan);
1142    if (tf->pixbuf)    ckfree ((char *)tf->pixbuf);
1143    return;
1144}
1145
1146#define OUT Tcl_WriteChars (outChan, str, -1)
1147static void printImgInfo (IMAGE *th, const char *filename, const char *msg)
1148{
1149    Tcl_Channel outChan;
1150    char str[256];
1151
1152    outChan = Tcl_GetStdChannel (TCL_STDOUT);
1153    if (!outChan) {
1154        return;
1155    }
1156    sprintf(str, "%s %s\n", msg, filename);                                      OUT;
1157    sprintf(str, "\tSize in pixel      : %d x %d\n", th->xsize, th->ysize);      OUT;
1158    sprintf(str, "\tNo. of channels    : %d\n", (th->zsize));                    OUT;
1159    sprintf(str, "\tBytes per pixel    : %d\n", BPP(th->type));                  OUT;
1160    sprintf(str, "\tCompression        : %s\n", ISRLE(th->type)? "RLE": "None"); OUT;
1161    Tcl_Flush(outChan);
1162}
1163#undef OUT
1164
1165static Boln readHeader (tkimg_MFile *handle, IMAGE *th)
1166{
1167    if (512 != tkimg_Read(handle, (char *)th, 512)) {
1168	return FALSE;
1169    }
1170										    if( ((th->imagic>>8) | ((th->imagic&0xff)<<8)) == IMAGIC ) {
1171	th->dorev = 1;
1172	cvtimage((int *)th);
1173    } else {
1174	th->dorev = 0;
1175    }
1176    if (th->imagic != IMAGIC) {
1177	return FALSE;
1178    }
1179
1180    return TRUE;
1181}
1182
1183static Boln writeHeader(tkimg_MFile *handle, IMAGE *th, UInt type, UInt dim,
1184                         UInt xsize, UInt ysize, UInt zsize)
1185{
1186    if (!imgOpenWrite ((MYCHANNEL)handle->data, th, "w",
1187                       type, dim, xsize, ysize, zsize))
1188	return FALSE;
1189    return TRUE;
1190}
1191
1192static Boln readChannel (SGIFILE *tf, UByte *dest, Int sgichn, Int nchan,
1193			 Int y, Int n)
1194{
1195    unsigned short *src = tf->pixbuf;
1196    unsigned short *stop = src + n;
1197
1198    if (-1 == getrow (&tf->th, tf->pixbuf, y, sgichn))
1199	return FALSE;
1200
1201    dest += sgichn;
1202    switch (BPP(tf->th.type)) {
1203	case 1:	{	/* 8-bit pixel values */
1204	    while (src < stop) {
1205		*dest = *(src++);
1206		dest += nchan;
1207	    }
1208	    break;
1209	}
1210	case 2: {	/* 16-bit values will be linearly mapped to 8-bit. */
1211	    while (src < stop) {
1212		*dest = *(src++) >> 8;
1213		dest += nchan;
1214	    }
1215	    break;
1216	}
1217    }
1218    return TRUE;
1219}
1220
1221static Boln sgiReadScan (Tcl_Interp *interp, tkimg_MFile *handle,
1222                         SGIFILE *tf, Int y)
1223{
1224    Int nchan;
1225
1226    nchan = tf->th.zsize;
1227
1228    /* Read 1. channel: This is either the red or brightness channel. */
1229    if (!readChannel (tf, tf->scanline, 0, nchan, y, tf->th.xsize)) {
1230        return FALSE;
1231    }
1232
1233    if ( nchan >= 3) {
1234	/* This is either a RGB or RGBA image. Read green and blue channels. */
1235        if (!readChannel (tf, tf->scanline, 1, nchan, y, tf->th.xsize) ||
1236	    !readChannel (tf, tf->scanline, 2, nchan, y, tf->th.xsize))
1237	    return FALSE;
1238    }
1239    if (nchan > 3 || nchan == 2) {
1240        /* If nchan is 2, we have a brightness-alpha image, if nchan is 4, we
1241           have RGBA. */
1242        if (!readChannel (tf, tf->scanline, nchan == 2? 1: 3, nchan,
1243                          y, tf->th.xsize))
1244            return FALSE;
1245    }
1246    return TRUE;
1247}
1248
1249static Boln writeChannel (SGIFILE *tf, UByte *src, Int sgichn, Int y, Int n)
1250{
1251    unsigned short *dest = tf->pixbuf;
1252    UByte *stop = src + n;
1253
1254    while (src < stop)
1255	*(dest++) = *(src++);
1256
1257    if (-1 == putrow (&tf->th, tf->pixbuf, y, sgichn))
1258	return FALSE;
1259
1260    return TRUE;
1261}
1262
1263static Boln sgiWriteScan(Tcl_Interp *interp, tkimg_MFile *handle,
1264                          SGIFILE *tf, Int y)
1265{
1266    if (!writeChannel (tf, tf->redScan,   0, y, tf->th.xsize) ||
1267        !writeChannel (tf, tf->greenScan, 1, y, tf->th.xsize) ||
1268        !writeChannel (tf, tf->blueScan,  2, y, tf->th.xsize))
1269        return FALSE;
1270
1271    if (tf->th.zsize > 3)
1272        if (!writeChannel (tf, tf->matteScan, 3, y, tf->th.xsize))
1273            return FALSE;
1274    return TRUE;
1275}
1276
1277/*
1278 * Here is the start of the standard functions needed for every image format.
1279 */
1280
1281/*
1282 * Prototypes for local procedures defined in this file:
1283 */
1284
1285static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
1286	int *comp, int *verb, int *matte);
1287static int CommonMatch(tkimg_MFile *handle, int *widthPtr,
1288	int *heightPtr, IMAGE *sgiHeaderPtr);
1289static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
1290	const char *filename, Tcl_Obj *format,
1291	Tk_PhotoHandle imageHandle, int destX, int destY,
1292	int width, int height, int srcX, int srcY);
1293static int CommonWrite(Tcl_Interp *interp,
1294	const char *filename, Tcl_Obj *format,
1295	tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
1296
1297static int ParseFormatOpts (interp, format, comp, verb, matte)
1298    Tcl_Interp *interp;
1299    Tcl_Obj *format;
1300    int *comp;
1301    int *verb;
1302    int *matte;
1303{
1304    static const char *const sgiOptions[] = {"-compression", "-verbose", "-matte"};
1305    int objc, length, c, i, index;
1306    Tcl_Obj **objv;
1307    const char *compression, *verbose, *transp;
1308
1309    *comp  = 1;
1310    *verb  = 0;
1311    *matte = 1;
1312
1313    if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK)
1314	return TCL_ERROR;
1315    if (objc) {
1316	compression = "rle";
1317	verbose     = "0";
1318	transp      = "1";
1319	for (i=1; i<objc; i++) {
1320	    if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)sgiOptions,
1321		    "format option", 0, &index) != TCL_OK) {
1322		return TCL_ERROR;
1323	    }
1324	    if (++i >= objc) {
1325		Tcl_AppendResult(interp, "No value for option \"",
1326			Tcl_GetStringFromObj (objv[--i], (int *) NULL),
1327			"\"", (char *) NULL);
1328		return TCL_ERROR;
1329	    }
1330	    switch(index) {
1331		case 0:
1332		    compression = Tcl_GetStringFromObj(objv[i], (int *) NULL);
1333		    break;
1334		case 1:
1335		    verbose = Tcl_GetStringFromObj(objv[i], (int *) NULL);
1336		    break;
1337		case 2:
1338		    transp = Tcl_GetStringFromObj(objv[i], (int *) NULL);
1339		    break;
1340	    }
1341	}
1342
1343	c = compression[0]; length = strlen (compression);
1344	if ((c == 'n') && (!strncmp (compression, "none", length))) {
1345	    *comp = ITYPE_UNCOMPRESSED;
1346	} else if ((c == 'r') && (!strncmp (compression, "rle",length))) {
1347	    *comp = ITYPE_RLE;
1348	} else {
1349	    Tcl_AppendResult(interp, "invalid compression mode \"",
1350		    compression, "\": should be rle or none", (char *) NULL);
1351	    return TCL_ERROR;
1352	}
1353
1354	c = verbose[0]; length = strlen (verbose);
1355	if (!strncmp (verbose, "1", length) || \
1356	    !strncmp (verbose, "true", length) || \
1357	    !strncmp (verbose, "on", length)) {
1358	    *verb = 1;
1359	} else if (!strncmp (verbose, "0", length) || \
1360	    !strncmp (verbose, "false", length) || \
1361	    !strncmp (verbose, "off", length)) {
1362	    *verb = 0;
1363	} else {
1364	    Tcl_AppendResult(interp, "invalid verbose mode \"", verbose,
1365                              "\": should be 1 or 0, on or off, true or false",
1366			      (char *) NULL);
1367	    return TCL_ERROR;
1368	}
1369
1370	c = transp[0]; length = strlen (transp);
1371	if (!strncmp (transp, "1", length) || \
1372	    !strncmp (transp, "true", length) || \
1373	    !strncmp (transp, "on", length)) {
1374	    *matte = 1;
1375	} else if (!strncmp (transp, "0", length) || \
1376	    !strncmp (transp, "false", length) || \
1377	    !strncmp (transp, "off", length)) {
1378	    *matte = 0;
1379	} else {
1380	    Tcl_AppendResult(interp, "invalid alpha (matte) mode \"", verbose,
1381                              "\": should be 1 or 0, on or off, true or false",
1382			      (char *) NULL);
1383	    return TCL_ERROR;
1384	}
1385    }
1386    return TCL_OK;
1387}
1388
1389static int ChnMatch(
1390    Tcl_Channel chan,
1391    const char *filename,
1392    Tcl_Obj *format,
1393    int *widthPtr,
1394    int *heightPtr,
1395    Tcl_Interp *interp
1396) {
1397    tkimg_MFile handle;
1398
1399    handle.data = (char *) chan;
1400    handle.state = IMG_CHAN;
1401
1402    return CommonMatch(&handle, widthPtr, heightPtr, NULL);
1403}
1404
1405static int ObjMatch(
1406    Tcl_Obj *data,
1407    Tcl_Obj *format,
1408    int *widthPtr,
1409    int *heightPtr,
1410    Tcl_Interp *interp
1411) {
1412    tkimg_MFile handle;
1413
1414    if (!tkimg_ReadInit(data, '\001', &handle)) {
1415        return 0;
1416    }
1417    return CommonMatch(&handle, widthPtr, heightPtr, NULL);
1418}
1419
1420static int CommonMatch(handle, widthPtr, heightPtr, sgiHeaderPtr)
1421    tkimg_MFile *handle;
1422    int *widthPtr;
1423    int *heightPtr;
1424    IMAGE *sgiHeaderPtr;
1425{
1426    IMAGE th;
1427
1428    if (!sgiHeaderPtr) {
1429	if (!readHeader (handle, &th))
1430	    return 0;
1431    } else {
1432	if (!imgOpenRead ((MYCHANNEL)handle->data, &th, "r"))
1433	    return 0;
1434    }
1435
1436    if (th.xsize <= 0 || th.ysize <= 0)
1437	return 0;
1438
1439    *widthPtr  = th.xsize;
1440    *heightPtr = th.ysize;
1441    if (sgiHeaderPtr)
1442	*sgiHeaderPtr = th;
1443    return 1;
1444}
1445
1446static int ChnRead(interp, chan, filename, format, imageHandle,
1447	               destX, destY, width, height, srcX, srcY)
1448    Tcl_Interp *interp;
1449    Tcl_Channel chan;
1450    const char *filename;
1451    Tcl_Obj *format;
1452    Tk_PhotoHandle imageHandle;
1453    int destX, destY;
1454    int width, height;
1455    int srcX, srcY;
1456{
1457    tkimg_MFile handle;
1458
1459    handle.data = (char *) chan;
1460    handle.state = IMG_CHAN;
1461
1462    return CommonRead (interp, &handle, filename, format,
1463                       imageHandle, destX, destY,
1464		       width, height, srcX, srcY);
1465}
1466
1467#define BUFLEN 2048
1468
1469static int ObjRead (interp, data, format, imageHandle,
1470	            destX, destY, width, height, srcX, srcY)
1471    Tcl_Interp *interp;
1472    Tcl_Obj *data;
1473    Tcl_Obj *format;
1474    Tk_PhotoHandle imageHandle;
1475    int destX, destY;
1476    int width, height;
1477    int srcX, srcY;
1478{
1479    tkimg_MFile handle;
1480    char *tempFileName, tempFileNameBuffer[256];
1481    char buffer[BUFLEN];
1482    MYCHANNEL outchan;
1483    Tcl_Channel inchan;
1484    int count, retVal;
1485
1486    tkimg_ReadInit (data, '\001', &handle);
1487
1488    tempFileName = tmpnam(tempFileNameBuffer);
1489#ifdef TCLSEEK_WORKAROUND
1490    outchan = (Tcl_Channel)fopen (tempFileName, "wb");
1491#else
1492    outchan = tkimg_OpenFileChannel (interp, tempFileName, 0644);
1493#endif
1494    if (!outchan) {
1495	return TCL_ERROR;
1496    }
1497
1498    count = tkimg_Read(&handle, buffer, BUFLEN);
1499    while (count == BUFLEN) {
1500	Tcl_Write (outchan, buffer, count);
1501	count = tkimg_Read(&handle, buffer, BUFLEN);
1502    }
1503    if (count>0) {
1504	Tcl_Write (outchan, buffer, count);
1505    }
1506    if (MYCLOSE (interp, outchan) == TCL_ERROR) {
1507	return TCL_ERROR;
1508    }
1509
1510    inchan = tkimg_OpenFileChannel (interp, tempFileName, 0);
1511    if (!inchan) {
1512	return TCL_ERROR;
1513    }
1514
1515    handle.data = (char *) inchan;
1516    handle.state = IMG_CHAN;
1517
1518    retVal = CommonRead (interp, &handle, tempFileName, format, imageHandle,
1519                            destX, destY, width, height, srcX, srcY);
1520    if (Tcl_Close (interp, inchan) == TCL_ERROR) {
1521	return TCL_ERROR;
1522    }
1523    remove (tempFileName);
1524    return retVal;
1525}
1526
1527static int CommonRead (interp, handle, filename, format, imageHandle,
1528                       destX, destY, width, height, srcX, srcY)
1529    Tcl_Interp *interp;         /* Interpreter to use for reporting errors. */
1530    tkimg_MFile *handle;        /* The image file, open for reading. */
1531    const char *filename;       /* The name of the image file. */
1532    Tcl_Obj *format;            /* User-specified format object, or NULL. */
1533    Tk_PhotoHandle imageHandle; /* The photo image to write into. */
1534    int destX, destY;           /* Coordinates of top-left pixel in
1535                                 * photo image to be written to. */
1536    int width, height;          /* Dimensions of block of photo image to
1537                                 * be written to. */
1538    int srcX, srcY;             /* Coordinates of top-left pixel to be used
1539                                 * in image being read. */
1540{
1541	Tk_PhotoImageBlock block;
1542    Int y, nchan;
1543    int fileWidth, fileHeight;
1544    int stopY, outY, outWidth, outHeight;
1545    SGIFILE tf;
1546    int compr, verbose, matte;
1547    int result = TCL_OK;
1548
1549    memset(&tf, 0, sizeof (SGIFILE));
1550    if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) {
1551        return TCL_ERROR;
1552    }
1553
1554    CommonMatch(handle, &fileWidth, &fileHeight, &tf.th);
1555    if (verbose)
1556	printImgInfo (&tf.th, filename, "Reading image:");
1557
1558    if ((srcX + width) > fileWidth) {
1559	outWidth = fileWidth - srcX;
1560    } else {
1561	outWidth = width;
1562    }
1563    if ((srcY + height) > fileHeight) {
1564	outHeight = fileHeight - srcY;
1565    } else {
1566	outHeight = height;
1567    }
1568    if ((outWidth <= 0) || (outHeight <= 0)
1569	|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
1570	return TCL_OK;
1571    }
1572
1573    if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
1574	return TCL_ERROR;
1575    }
1576
1577    nchan = tf.th.zsize;
1578
1579    tf.pixbuf   = (UShort *) ckalloc (fileWidth * nchan * sizeof (UShort));
1580    tf.scanline = (UByte  *) ckalloc (fileWidth * nchan);
1581
1582    block.pixelSize = nchan;
1583    block.pitch = fileWidth * nchan;
1584    block.width = outWidth;
1585    block.height = 1;
1586    switch (nchan) {
1587	case 1: /* Brightness */
1588	case 2: /* Brightness + Matte */
1589	    block.offset[0] = 0;
1590	    block.offset[1] = 0;
1591	    block.offset[2] = 0;
1592	    block.offset[3] = matte? 1: 0;
1593	    break;
1594	case 3: /* RGB */
1595	case 4: /* RGB + Matte */
1596	    block.offset[0] = 0;
1597	    block.offset[1] = 1;
1598	    block.offset[2] = 2;
1599	    block.offset[3] = matte? 3: 0;
1600	    break;
1601	default:
1602	    printf("Invalid number of channels: %d\n", (int) nchan);
1603	    return TCL_ERROR;
1604	    break;
1605    }
1606    block.pixelPtr = tf.scanline + srcX * nchan;
1607
1608    stopY = srcY + outHeight;
1609    outY = destY;
1610
1611    for (y=0; y<stopY; y++) {
1612	sgiReadScan (interp, handle, &tf, fileHeight-1-y);
1613	if (y >= srcY) {
1614	    if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, outWidth, 1, matte? TK_PHOTO_COMPOSITE_OVERLAY: TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
1615		result = TCL_ERROR;
1616		break;
1617	    }
1618	    outY++;
1619	}
1620    }
1621    sgiClose (&tf);
1622    return result;
1623}
1624
1625static int ChnWrite (interp, filename, format, blockPtr)
1626    Tcl_Interp *interp;
1627    const char *filename;
1628    Tcl_Obj *format;
1629    Tk_PhotoImageBlock *blockPtr;
1630{
1631    MYCHANNEL chan;
1632    tkimg_MFile handle;
1633    int result;
1634
1635#ifdef TCLSEEK_WORKAROUND
1636    chan = (Tcl_Channel)fopen(filename, "wb");
1637#else
1638    chan = tkimg_OpenFileChannel (interp, filename, 0644);
1639#endif
1640    if (!chan) {
1641	return TCL_ERROR;
1642    }
1643
1644    handle.data = (char *) chan;
1645    handle.state = IMG_CHAN;
1646
1647    result = CommonWrite (interp, filename, format, &handle, blockPtr);
1648    if (MYCLOSE(interp, chan) == TCL_ERROR) {
1649	return TCL_ERROR;
1650    }
1651    return result;
1652}
1653
1654static int StringWrite(
1655    Tcl_Interp *interp,
1656    Tcl_Obj *format,
1657    Tk_PhotoImageBlock *blockPtr
1658) {
1659    tkimg_MFile handle;
1660    int result;
1661    Tcl_DString data;
1662    Tcl_Channel inchan;
1663    MYCHANNEL outchan;
1664    char *tempFileName, tempFileNameBuffer[256];
1665    char buffer[BUFLEN];
1666    int count;
1667
1668    Tcl_DStringInit(&data);
1669    tempFileName = tmpnam(tempFileNameBuffer);
1670#ifdef TCLSEEK_WORKAROUND
1671    outchan = (Tcl_Channel)fopen(tempFileName, "wb");
1672#else
1673    outchan = tkimg_OpenFileChannel (interp, tempFileName, 0644);
1674#endif
1675    if (!outchan) {
1676	return TCL_ERROR;
1677    }
1678
1679    handle.data = (char *) outchan;
1680    handle.state = IMG_CHAN;
1681
1682    result = CommonWrite(interp, tempFileName, format, &handle, blockPtr);
1683    if (MYCLOSE(interp, outchan) == TCL_ERROR) {
1684	return TCL_ERROR;
1685    }
1686
1687    tkimg_WriteInit(&data, &handle);
1688
1689    inchan = tkimg_OpenFileChannel(interp, tempFileName, 0);
1690    if (!inchan) {
1691	return TCL_ERROR;
1692    }
1693
1694    count = Tcl_Read(inchan, buffer, BUFLEN);
1695    while (count == BUFLEN) {
1696	tkimg_Write(&handle, buffer, count);
1697	count = Tcl_Read(inchan, buffer, BUFLEN);
1698    }
1699    if (count>0) {
1700	tkimg_Write(&handle, buffer, count);
1701    }
1702    if (Tcl_Close(interp, inchan) == TCL_ERROR) {
1703	return TCL_ERROR;
1704    }
1705    remove (tempFileName);
1706    tkimg_Putc(IMG_DONE, &handle);
1707
1708    if (result == TCL_OK) {
1709	Tcl_DStringResult(interp, &data);
1710    } else {
1711	Tcl_DStringFree(&data);
1712    }
1713    return result;
1714}
1715
1716static int CommonWrite (interp, filename, format, handle, blockPtr)
1717    Tcl_Interp *interp;
1718    const char *filename;
1719    Tcl_Obj *format;
1720    tkimg_MFile *handle;
1721    Tk_PhotoImageBlock *blockPtr;
1722{
1723    Int     x, y, bpp, nchan;
1724    Int     redOffset, greenOffset, blueOffset, alphaOffset;
1725    UByte   *pixelPtr, *rowPixPtr;
1726    SGIFILE tf;
1727    int compr, verbose, matte; /* Format options */
1728
1729    memset (&tf, 0, sizeof (SGIFILE));
1730    if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) {
1731        return TCL_ERROR;
1732    }
1733
1734    bpp = 1;
1735
1736    redOffset   = 0;
1737    greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1738    blueOffset  = blockPtr->offset[2] - blockPtr->offset[0];
1739    alphaOffset = blockPtr->offset[0];
1740
1741    if (alphaOffset < blockPtr->offset[2]) {
1742	alphaOffset = blockPtr->offset[2];
1743    }
1744    if (++alphaOffset < blockPtr->pixelSize) {
1745	alphaOffset -= blockPtr->offset[0];
1746    } else {
1747	alphaOffset = 0;
1748    }
1749    nchan = ((matte && alphaOffset)? 4: 3);
1750
1751    tf.redScan   = (UByte *)  ckalloc (blockPtr->width);
1752    tf.greenScan = (UByte *)  ckalloc (blockPtr->width);
1753    tf.blueScan  = (UByte *)  ckalloc (blockPtr->width);
1754    tf.matteScan = (UByte *)  ckalloc (blockPtr->width);
1755    tf.pixbuf    = (UShort *) ckalloc (blockPtr->width * sizeof (UShort));
1756    tf.th.imagic = IMAGIC;
1757
1758    if (!writeHeader(handle, &tf.th,
1759                      compr? RLE(bpp): UNCOMPRESSED(bpp),
1760                      nchan, blockPtr->width, blockPtr->height, nchan)) {
1761	return TCL_ERROR;
1762    }
1763    tf.th.dorev  = isIntel();
1764
1765    rowPixPtr = blockPtr->pixelPtr + blockPtr->offset[0];
1766    for (y = blockPtr->height -1; y >=0; y--) {
1767	tf.red = tf.redScan;
1768	tf.green = tf.greenScan;
1769	tf.blue = tf.blueScan;
1770	tf.matte = tf.matteScan;
1771	pixelPtr = rowPixPtr;
1772	for (x = 0; x < blockPtr->width; x++) {
1773	    *(tf.red++)   = pixelPtr[redOffset];
1774	    *(tf.green++) = pixelPtr[greenOffset];
1775	    *(tf.blue++)  = pixelPtr[blueOffset];
1776	    if (nchan == 4) {
1777                /* Have a matte channel and write it. */
1778		*(tf.matte++) = pixelPtr[alphaOffset];
1779	    }
1780	    pixelPtr += blockPtr->pixelSize;
1781	}
1782	if (!sgiWriteScan(interp, handle, &tf, y)) {
1783	    sgiClose (&tf);
1784	    return TCL_ERROR;
1785	}
1786	rowPixPtr += blockPtr->pitch;
1787    }
1788    if (verbose)
1789        printImgInfo (&tf.th, filename, "Saving image:");
1790
1791    iclose (&tf.th);
1792    sgiClose (&tf);
1793    return TCL_OK;
1794}
1795