1/* STARTHEADER
2 *
3 * File :       sun.c
4 *
5 * Author :     Paul Obermeier (paul@poSoft.de)
6 *
7 * Date :       Mon Jan 22 21:32:48 CET 2001
8 *
9 * Copyright :  (C) 2001-2002 Paul Obermeier
10 *
11 * Description :
12 *
13 * A photo image handler for SUN's Raster file format.
14 *
15 * The following image types are supported:
16 *
17 *  1-bit pixels: Black and White.
18 *  8-bit pixels: Grayscale or indexed.
19 * 24-bit pixels: True-color (RGB, each channel 8 bit).
20 * 32-bit pixels: True-color with alpha channel (RGBA, each channel 8 bit).
21 *
22 * List of currently supported features:
23 *
24 * Type   |     Read      |     Write     |
25 *        | -file | -data | -file | -data |
26 * ----------------------------------------
27 *  1-bit | Yes   | Yes   | No    | No    |
28 *  8-bit | Yes   | Yes   | No    | No    |
29 * 24-bit | Yes   | Yes   | Yes   | Yes   |
30 * 32-bit | Yes   | Yes   | Yes   | Yes   |
31 *
32 * All images types may be either uncompressed or run-length encoded.
33 *
34 *
35 * The following format options are available:
36 *
37 * Read  SUN image: "sun -matte <bool> -verbose <bool>"
38 * Write SUN image: "sun -matte <bool> -verbose <bool> -compression <type>"
39 *
40 * -matte <bool>:       If set to false, a matte (alpha) channel is ignored
41 *                      during reading or writing. Default is true.
42 * -verbose <bool>:     If set to true, additional information about the file
43 *                      format is printed to stdout. Default is false.
44 * -compression <type>: Set the compression mode to either "none" or "rle".
45 * 			Default is "rle".
46 *
47 * Notes:
48 *
49 * - The "UNIX" encoding of SUN's "imagetool" is not supported.
50 *
51 * - Part of this code was taken from the "sunras" GIMP plugin:
52 *
53 *  >> The GIMP -- an image manipulation program
54 *  >> Copyright (C) 1995 Spencer Kimball and Peter Mattis
55 *  >> SUN raster reading and writing code Copyright (C) 1996 Peter Kirchgessner
56 *  >> (email: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg)
57 *
58 * ENDHEADER
59 *
60 * $Id: sun.c 233 2010-04-01 09:28:00Z nijtmans $
61 *
62 */
63
64/*
65 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
66 */
67
68#include "init.c"
69
70
71/* #define DEBUG_LOCAL */
72
73/* Some defines and typedefs. */
74#define TRUE  1
75#define FALSE 0
76typedef unsigned char  Boln;	/* Boolean value: TRUE or FALSE */
77typedef char           Byte;	/* Signed    8 bit integer */
78typedef unsigned char  UByte;	/* Unsigned  8 bit integer */
79typedef unsigned short UShort;	/* Unsigned 16 bit integer */
80typedef unsigned int   UInt;	/* Unsigned 32 bit integer */
81
82/* SunRaster magic number */
83#define RAS_MAGIC 0x59a66a95
84
85/* Supported SunRaster types */
86#define RAS_TYPE_STD 1    /* Standard uncompressed format */
87#define RAS_TYPE_RLE 2    /* Runlength compression format */
88
89/* The SunRaster header structure */
90typedef struct {
91  UInt ras_magic;    /* Magic Number */
92  UInt ras_width;    /* Width */
93  UInt ras_height;   /* Height */
94  UInt ras_depth;    /* Number of bits per pixel (1,8,24,32) */
95  UInt ras_length;   /* Length of image data (but may also be 0) */
96  UInt ras_type;     /* Encoding */
97  UInt ras_maptype;  /* Type of colormap */
98  UInt ras_maplength;/* Number of bytes for colormap */
99} SUNHEADER;
100
101/* Buffer for run-length encoding and decoding. */
102typedef struct {
103    int val;   /* The value that is to be repeated */
104    int n;     /* How many times it is repeated */
105} RLEBUF;
106
107/* Forward declarations of static functions. */
108/* This function is commented out because it is not used anywhere
109static void byte2bit (UByte *byteline, int width, UByte *bitline, int invert);
110*/
111
112static void rle_startread (tkimg_MFile *ifp);
113static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp);
114static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp);
115static int rle_fgetc (tkimg_MFile *ifp);
116static int sun_getc (tkimg_MFile *ifp);
117#define rle_getc(fp) ((rlebuf.n > 0) ? (rlebuf.n)--,rlebuf.val : rle_fgetc (fp))
118
119static void rle_startwrite (tkimg_MFile *ofp);
120/* This function is commented out because it is not used anywhere
121static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp);
122*/
123static int rle_fputc (int val, tkimg_MFile *ofp);
124static int rle_putrun (int n, int val, tkimg_MFile *ofp);
125static void rle_endwrite (tkimg_MFile *ofp);
126
127static Boln read_sun_header  (tkimg_MFile *ifp, SUNHEADER *sunhdr);
128static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr);
129static Boln read_sun_cols  (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap);
130/* This function is commented out because it is not used anywhere
131static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap);
132*/
133
134static RLEBUF
135	rlebuf;
136
137#ifdef DEBUG_LOCAL
138static Boln readUByte (tkimg_MFile *handle, UByte *b)
139{
140    char buf[1];
141    if (1 != tkimg_Read(handle, buf, 1))
142        return FALSE;
143    *b = (UByte) buf[0];
144    return TRUE;
145}
146#else
147    /* Use this macro for better performance, esp. when reading RLE files. */
148#   define readUByte(h,b) (1 == tkimg_Read((h),(char *)(b),1))
149#endif
150
151static Boln writeUByte (tkimg_MFile *handle, UByte b)
152{
153    UByte buf[1];
154    buf[0] = b;
155    if (1 != tkimg_Write(handle, (const char *)buf, 1))
156        return FALSE;
157    return TRUE;
158}
159
160static Boln readUInt (tkimg_MFile *ifp, UInt *i)
161{
162    UByte buf[4];
163    UInt  c;
164
165    if (4 != tkimg_Read(ifp, (char *)buf, 4)) {
166	return FALSE;
167    }
168
169    c  = (((UInt)(buf[0])) << 24);
170    c |= (((UInt)(buf[1])) << 16);
171    c |= (((UInt)(buf[2])) << 8);
172    c |=  ((UInt)(buf[3]));
173    *i = c;
174    return TRUE;
175}
176
177static Boln writeUInt (tkimg_MFile *ofp, UInt c)
178{
179    UByte buf[4];
180
181    buf[0] = (c >> 24) & 0xff;
182    buf[1] = (c >> 16) & 0xff;
183    buf[2] = (c >>  8) & 0xff;
184    buf[3] = (c      ) & 0xff;
185    if (4 != tkimg_Write(ofp, (const char *)buf, 4)) {
186	return FALSE;
187    }
188    return TRUE;
189}
190
191/* Convert n bytes of 0/1 to a line of bits */
192/* This function is commented out because it is not used anywhere
193static void byte2bit (UByte *byteline, int width,
194                      UByte *bitline, int invert)
195{
196    UByte bitval;
197    UByte rest[8];
198
199    while (width >= 8) {
200	bitval = 0;
201        if (*(byteline++)) bitval |= 0x80;
202        if (*(byteline++)) bitval |= 0x40;
203        if (*(byteline++)) bitval |= 0x20;
204        if (*(byteline++)) bitval |= 0x10;
205        if (*(byteline++)) bitval |= 0x08;
206        if (*(byteline++)) bitval |= 0x04;
207        if (*(byteline++)) bitval |= 0x02;
208        if (*(byteline++)) bitval |= 0x01;
209        *(bitline++) = invert ? ~bitval : bitval;
210        width -= 8;
211    }
212    if (width > 0) {
213       memset (rest, 0, 8);
214       memcpy (rest, byteline, width);
215       bitval = 0;
216       byteline = rest;
217       if (*(byteline++)) bitval |= 0x80;
218       if (*(byteline++)) bitval |= 0x40;
219       if (*(byteline++)) bitval |= 0x20;
220       if (*(byteline++)) bitval |= 0x10;
221       if (*(byteline++)) bitval |= 0x08;
222       if (*(byteline++)) bitval |= 0x04;
223       if (*(byteline++)) bitval |= 0x02;
224       *bitline = invert ? ~bitval : bitval;
225    }
226}
227*/
228
229/* Start reading Runlength Encoded Data */
230static void rle_startread (tkimg_MFile *ifp)
231{
232    (void) ifp;
233    rlebuf.val = rlebuf.n = 0;
234}
235
236/* Read pixels from RLE-stream */
237static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp)
238{
239    int elem_read, cnt, val, err = 0;
240
241    for (elem_read = 0; elem_read < nelem; elem_read++)
242    {
243        for (cnt = 0; cnt < sz; cnt++)
244	{
245	    val = rle_getc (ifp);
246	    if (val < 0) {
247                err = 1;
248                break;
249	    }
250	    *(ptr++) = (char)val;
251        }
252        if (err)
253	    break;
254    }
255    return elem_read;
256}
257
258/* Read uncompressed pixels from input stream "ifp" */
259static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp)
260{
261    if (nelem*sz != tkimg_Read(ifp, ptr, nelem*sz)) {
262	return -1;
263    }
264    return nelem;
265}
266
267/* Get one pixel from RLE-stream */
268static int rle_fgetc (tkimg_MFile *ifp)
269{
270    UByte flag, runcnt, runval;
271
272    if (rlebuf.n > 0)    /* Something in the buffer ? */
273    {
274       (rlebuf.n)--;
275       return rlebuf.val;
276    }
277
278    /* Nothing in the buffer. We have to read something */
279    if (!readUByte (ifp, &flag))
280	return -1;
281    if (flag != 0x80) return flag;    /* Single byte run ? */
282
283    if (!readUByte (ifp, &runcnt))
284	return -1;
285    if (runcnt == 0) return (0x80);     /* Single 0x80 ? */
286
287    /* The run */
288    if (!readUByte (ifp, &runval))
289	return -1;
290    rlebuf.n = runcnt;
291    rlebuf.val = runval;
292    return runval;
293}
294
295/* Read one byte from input stream "ifp" */
296static int sun_getc (tkimg_MFile *ifp)
297{
298    UByte val;
299    if (!readUByte (ifp, &val))
300	return -1;
301    return val;
302}
303
304/* Start writing Runlength Encoded Data */
305static void rle_startwrite (tkimg_MFile *ofp)
306{
307    (void) ofp;
308    rlebuf.val = 0;
309    rlebuf.n   = 0;
310}
311
312/* Write uncompressed elements to RLE-stream */
313/* This function is commented out because it is not used anywhere
314static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp)
315{
316    int elem_write, cnt, val, err = 0;
317    UByte *pixels = (UByte *)ptr;
318
319    for (elem_write = 0; elem_write < nelem; elem_write++) {
320	for (cnt = 0; cnt < sz; cnt++) {
321	    val = rle_fputc (*(pixels++), ofp);
322	    if (val < 0) {
323		err = 1;
324		break;
325	    }
326        }
327        if (err)
328	    break;
329    }
330    return elem_write;
331}
332*/
333
334/* Write uncompressed character to RLE-stream */
335static int rle_fputc (int val, tkimg_MFile *ofp)
336{
337    int retval;
338
339    if (rlebuf.n == 0) {   /* Nothing in the buffer ? Save the value */
340	rlebuf.n   = 1;
341	rlebuf.val = val;
342	return val;
343    }
344
345    /* Something in the buffer */
346    if (rlebuf.val == val) {   /* Same value in the buffer ? */
347	rlebuf.n++;
348	if (rlebuf.n == 257) { /* Can not be encoded in a single run ? */
349	    retval = rle_putrun (256, rlebuf.val, ofp);
350	    if (retval < 0)
351                return retval;
352	    rlebuf.n -= 256;
353	}
354	return val;
355    }
356
357    /* Something different in the buffer ? Write out the run */
358    retval = rle_putrun (rlebuf.n, rlebuf.val, ofp);
359    if (retval < 0)
360        return retval;
361
362    /* Save the new value */
363    rlebuf.n = 1;
364    return (rlebuf.val = val);
365}
366
367/* Write out a run with 0 < n < 257 */
368static int rle_putrun (int n, int val, tkimg_MFile *ofp)
369{
370    int retval = 1,
371        flag   = 0x80;
372
373    /* Useful to write a 3 byte run ? */
374    if ((n > 2) || ((n == 2) && (val == flag))) {
375	if (!writeUByte (ofp, flag) ||
376	    !writeUByte (ofp, n-1) ||
377            !writeUByte (ofp, val)) {
378	    retval = -1;
379	}
380    }
381    else if (n == 2) {
382        /* Write two single runs (could not be value 0x80) */
383	if (!writeUByte (ofp, val) || !writeUByte (ofp, val)) {
384	    retval = -1;
385	}
386    } else { 		/* Write a single run */
387	if (val == flag) {
388	    if (!writeUByte (ofp, flag) || !writeUByte (ofp, 0)) {
389		retval = -1;
390	    }
391	} else {
392	    if (!writeUByte (ofp, val)) {
393		retval = -1;
394	    }
395	}
396    }
397
398    return ((retval < 0) ? retval : val);
399}
400
401/* End writing Runlength Encoded Data */
402static void rle_endwrite (tkimg_MFile *ofp)
403{
404    if (rlebuf.n > 0) {
405	rle_putrun (rlebuf.n, rlebuf.val, ofp);
406	rlebuf.val = rlebuf.n = 0; 	/* Clear RLE-buffer */
407    }
408}
409
410#define OUT Tcl_WriteChars (outChan, str, -1)
411static void printImgInfo (SUNHEADER *sh, const char *filename, const char *msg)
412{
413    Tcl_Channel outChan;
414    char str[256];
415    UInt type = sh->ras_type;
416
417    outChan = Tcl_GetStdChannel (TCL_STDOUT);
418    if (!outChan) {
419        return;
420    }
421
422    sprintf(str, "%s %s\n", msg, filename);                                       OUT;
423    sprintf(str, "\tSize in pixel   : %d x %d\n", sh->ras_width, sh->ras_height); OUT;
424    sprintf(str, "\tDepth of pixels : %d\n", sh->ras_depth);                      OUT;
425    sprintf(str, "\tCompression     : %s\n", (type == RAS_TYPE_STD? "None":
426				              (type == RAS_TYPE_RLE? "RLE":
427					                             "Unknown"))); OUT;
428    sprintf(str, "\tColormap type   : %d\n", sh->ras_maptype);                    OUT;
429    Tcl_Flush(outChan);
430}
431#undef OUT
432
433static Boln read_sun_header (tkimg_MFile *ifp, SUNHEADER *sunhdr)
434{
435    int  i;
436    UInt *cp;
437
438    cp = (UInt *)sunhdr;
439
440    /* Read in all 32-bit values of the header and check for byte order */
441    for (i=0; i<sizeof(SUNHEADER) / sizeof(sunhdr->ras_magic); i++) {
442	if (!readUInt (ifp, cp))
443	    return FALSE;
444	cp++;
445    }
446    if (sunhdr->ras_magic != RAS_MAGIC)
447	return FALSE;
448    return TRUE;
449}
450
451/* Write out a SUN-fileheader */
452static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr)
453{
454    int i, hdr_entries;
455    UInt *cp;
456
457    cp = (UInt *)sunhdr;
458
459    hdr_entries = sizeof (SUNHEADER)/sizeof(sunhdr->ras_magic);
460    /* Write out all 32-bit values of the header and check for byte order */
461    for (i=0; i<hdr_entries; i++) {
462	if (!writeUInt (ofp, *(cp++))) {
463	    return FALSE;
464	}
465    }
466    return TRUE;
467}
468
469/* Read the sun colourmap */
470static Boln read_sun_cols (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap)
471{
472    int ncols;
473
474    /* Read in SUN-raster Colormap */
475    ncols = sunhdr->ras_maplength / 3;
476    if (ncols <= 0)
477	return FALSE;
478
479    if (3*ncols != tkimg_Read(ifp, (char *)colormap, 3*ncols)) {
480	return FALSE;
481    }
482    return TRUE;
483}
484
485/* Write a sun colourmap */
486/* This function is commented out because it is not used anywhere
487static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap)
488{
489    int ncols;
490
491    ncols = sunhdr->ras_maplength / 3;
492    if (3*ncols != tkimg_Write(ofp, (const char *)colormap, 3*ncols)) {
493	return FALSE;
494    }
495    return TRUE;
496}
497*/
498
499/* Load SUN Raster file with depth 1 */
500static Boln load_sun_d1 (Tcl_Interp *interp, tkimg_MFile *ifp,
501                         Tk_PhotoHandle imageHandle, int destX, int destY,
502                         int width, int height, int srcX, int srcY,
503			 int fileWidth, int fileHeight, int type)
504{
505    UByte *dest, bit2byte[256*8];
506    UByte *pixbuf;
507    Tk_PhotoImageBlock block;
508    int pix8;
509    int linepad;
510    int x, y;
511    int stopY, outY;
512    int i, j;
513    int err = 0, rle;
514    char errMsg[200];
515    Boln result = TRUE;
516
517    pixbuf = (UByte *) ckalloc (fileWidth);
518    if (!pixbuf) {
519	sprintf(errMsg, "Can't allocate memory of size %d", fileWidth);
520	Tcl_AppendResult(interp, errMsg, (char *)NULL);
521	return TCL_ERROR;
522    }
523
524    block.pixelSize = 1;
525    block.pitch     = fileWidth;
526    block.width     = width;
527    block.height    = 1;
528    block.offset[0] = 0;
529    block.offset[1] = 0;
530    block.offset[2] = 0;
531    block.offset[3] = 0;
532    block.pixelPtr  = pixbuf + srcX;
533
534    rle = (type == RAS_TYPE_RLE);
535    linepad = ((fileWidth+7)/8) % 2; 	/* Check for 16bit align */
536
537    if (rle)
538	rle_startread (ifp);
539
540    /* Get an array for mapping 8 bits in a byte to 8 bytes */
541    dest = bit2byte;
542    for (j=0; j<256; j++) {
543	for (i=7; i>=0; i--) {
544	    *(dest++) = ((j & (1 << i)) == 0) * 255;
545	}
546    }
547
548    stopY = srcY + height;
549    outY = destY;
550
551    for (y=0; y<stopY; y++) {
552	dest = pixbuf;
553	x = fileWidth;
554	while (x >= 8) {
555	    pix8 = rle ? rle_getc (ifp) : sun_getc (ifp);
556	    if (pix8 < 0) { err = 1; pix8 = 0; }
557
558	    memcpy (dest, bit2byte + pix8*8, 8);
559	    dest += 8;
560	    x -= 8;
561	}
562
563	if (x>0) {
564	    pix8 = rle ? rle_getc (ifp) : sun_getc (ifp);
565	    if (pix8 < 0) { err = 1; pix8 = 0; }
566
567	    memcpy (dest, bit2byte + pix8*8, x);
568	    dest += x;
569	}
570
571	if (linepad)
572	    err |= ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0);
573
574	if (err) {
575	    sprintf(errMsg, "Unexpected EOF while reading scanline %d", y);
576	    Tcl_AppendResult(interp, errMsg, (char *) NULL);
577	    return FALSE;
578        }
579	if (y >= srcY) {
580	    if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
581		result = FALSE;
582		break;
583	    }
584	    outY++;
585	}
586    }
587    return result;
588}
589
590/* Load SUN Raster file with depth 8 */
591static Boln load_sun_d8 (Tcl_Interp *interp, tkimg_MFile *ifp,
592                         Tk_PhotoHandle imageHandle, int destX, int destY,
593                         int width, int height, int srcX, int srcY,
594                         int fileWidth, int fileHeight,
595			 int type, UByte *suncolmap, int maplength)
596{
597    UByte *dest, *indData = NULL, *src;
598    UByte *pixbuf = NULL;
599    Tk_PhotoImageBlock block;
600    int linepad;
601    int x, y;
602    int stopY, outY;
603    int ncols;
604    int greyscale, nchan;
605    int err, rle;
606    char errMsg[200];
607    Boln result = TRUE;
608
609    rle     = (type == RAS_TYPE_RLE);
610    linepad = fileWidth % 2;
611    ncols   = maplength / 3;
612
613    /* Check, if it's a greyscale or color indexed image. */
614    greyscale = 1;
615    nchan     = 1;
616    if ((ncols > 0) && (suncolmap != NULL)) {
617	greyscale = 0;
618	nchan     = 3;
619    }
620
621    if (!greyscale) {
622	pixbuf = (UByte *) ckalloc (fileWidth * nchan);
623	if (!pixbuf) {
624	    sprintf(errMsg, "Can't allocate memory of size %d",
625			      fileWidth * nchan);
626	    Tcl_AppendResult(interp, errMsg, (char *)NULL);
627	    if (suncolmap)
628		ckfree ((char *)suncolmap);
629	    return TCL_ERROR;
630	}
631    }
632
633    /* This buffer contains either the color indices or the greyscale value. */
634    indData = (UByte *)ckalloc (fileWidth * sizeof (UByte));
635    if (!indData) {
636	sprintf(errMsg, "Can't allocate memory of size %d",
637			  fileWidth * sizeof (UByte));
638	Tcl_AppendResult(interp, errMsg, (char *)NULL);
639	return TCL_ERROR;
640    }
641
642    block.pixelSize = nchan;
643    block.pitch     = fileWidth * nchan;
644    block.width     = width;
645    block.height    = 1;
646    block.offset[0] = 0;
647    block.offset[1] = greyscale? 0: 1;
648    block.offset[2] = greyscale? 0: 2;
649    block.offset[3] = 0;
650    block.pixelPtr  = (greyscale?
651	              (indData + srcX * nchan):
652		      (pixbuf + srcX * nchan));
653
654    if (rle)
655	rle_startread (ifp);  /* Initialize RLE-buffer */
656
657    stopY = srcY + height;
658    outY = destY;
659
660    for (y=0; y<stopY; y++) {
661	src  = indData;
662	memset ((char *)src, 0, fileWidth);
663	err = ((rle ? rle_fread ((char *)src, 1, fileWidth, ifp) :
664		      sun_fread ((char *)src, 1, fileWidth, ifp)) != fileWidth);
665	if (err && (y != height -1)) {
666	    sprintf(errMsg, "Unexpected EOF while reading scanline %d", y);
667	    Tcl_AppendResult(interp, errMsg, (char *) NULL);
668	    ckfree ((char *)indData);
669	    return FALSE;
670	}
671	if (linepad) {
672	    err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0);
673	    if (err) {
674		sprintf(errMsg, "Unexpected EOF while reading scanline %d", y);
675		Tcl_AppendResult(interp, errMsg, (char *) NULL);
676		ckfree ((char *)indData);
677		return FALSE;
678	    }
679	}
680
681	if (!greyscale) {
682	    src  = indData;
683	    dest = pixbuf;
684	    for (x=0; x<width; x++) {
685		*dest++ = suncolmap[*src];
686		*dest++ = suncolmap[*src + ncols];
687		*dest++ = suncolmap[*src + 2*ncols];
688		src++;
689	    }
690	}
691
692	if (y >= srcY) {
693	    if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
694		result = FALSE;
695		break;
696	    }
697	    outY++;
698	}
699    }
700    ckfree ((char *)indData);
701    return result;
702}
703
704/* Load SUN Raster file with true color image: depth = 24 or 32 */
705
706static Boln load_rgb (Tcl_Interp *interp, tkimg_MFile *ifp,
707                      Tk_PhotoHandle imageHandle, int destX, int destY,
708                      int width, int height, int srcX, int srcY,
709		      int fileWidth, int fileHeight,
710		      int nchan, int type, int showMatte)
711{
712    UByte *dest, tmp;
713    UByte *pixbuf;
714    Tk_PhotoImageBlock block;
715    int linepad;
716    int x, y;
717    int stopY, outY;
718    int err, rle;
719    char errMsg[200];
720    Boln result = TRUE;
721
722    pixbuf = (UByte *) ckalloc (fileWidth * nchan);
723    if (!pixbuf) {
724	sprintf(errMsg, "Can't allocate memory of size %d",
725                          fileWidth * nchan);
726	Tcl_AppendResult(interp, errMsg, (char *)NULL);
727	return TCL_ERROR;
728    }
729
730    block.pixelSize = nchan;
731    block.pitch = fileWidth * nchan;
732    block.width = width;
733    block.height = 1;
734    block.offset[0] = 0;
735    block.offset[1] = 1;
736    block.offset[2] = 2;
737    if (nchan < 4) {
738	showMatte = 0;
739    }
740    block.offset[3] = showMatte? 3: 0;
741
742    block.pixelPtr = pixbuf + srcX * nchan;
743
744    rle     = (type == RAS_TYPE_RLE);
745    linepad = (fileWidth*nchan) % 2;
746
747    if (rle)
748        rle_startread (ifp);  	/* Initialize RLE-buffer */
749
750    stopY = srcY + height;
751    outY  = destY;
752
753    for (y=0; y<stopY; y++) {
754	dest = pixbuf;
755	memset ((char *)dest, 0, nchan*fileWidth);
756	err = ((rle ? rle_fread ((char *)dest, nchan, fileWidth, ifp) :
757		      sun_fread ((char *)dest, nchan, fileWidth, ifp)) != fileWidth);
758	if (err && (y != height -1)) {
759	    sprintf(errMsg, "Unexpected EOF while reading scanline %d", y);
760	    Tcl_AppendResult(interp, errMsg, (char *) NULL);
761	    ckfree ((char *)pixbuf);
762	    return FALSE;
763	}
764	if (linepad) {
765	    err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0);
766	    if (err) {
767		sprintf(errMsg, "Unexpected EOF while reading scanline %d", y);
768		Tcl_AppendResult(interp, errMsg, (char *) NULL);
769		ckfree ((char *)pixbuf);
770		return FALSE;
771	    }
772	}
773
774	if (y >= srcY) {
775	    dest = pixbuf + srcX * nchan;
776	    if (type != 3) {
777		if (nchan == 3) {		/* GBR Format. Swap to RGB. */
778		    for (x=0; x<width; x++) {
779			tmp = dest[0];
780			dest[0] = dest[2];
781			dest[2] = tmp;
782
783			dest += 3;
784		    }
785		} else { 			/* AGBR Format. Swap to RGBA. */
786		    for (x=0; x<width; x++) {
787			tmp = dest[0];
788			dest[0] = dest[3];
789			dest[3] = tmp;
790
791			tmp = dest[1];
792			dest[1] = dest[2];
793			dest[2] = tmp;
794
795			dest += 4;
796		    }
797		}
798	    }
799	    if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, showMatte? TK_PHOTO_COMPOSITE_OVERLAY: TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
800		result = FALSE;
801		break;
802	    }
803	    outY++;
804	}
805    }
806    ckfree ((char *)pixbuf);
807    return result;
808}
809
810/*
811 * Here is the start of the standard functions needed for every image format.
812 */
813
814/*
815 * Prototypes for local procedures defined in this file:
816 */
817
818static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format,
819	int *comp, int *verb, int *matte);
820static int CommonMatch(tkimg_MFile *handle, int *widthPtr,
821	int *heightPtr, SUNHEADER *sunHeaderPtr);
822static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
823	const char *filename, Tcl_Obj *format,
824	Tk_PhotoHandle imageHandle, int destX, int destY,
825	int width, int height, int srcX, int srcY);
826static int CommonWrite(Tcl_Interp *interp,
827	const char *filename, Tcl_Obj *format,
828	tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
829
830static int ParseFormatOpts (interp, format, comp, verb, matte)
831    Tcl_Interp *interp;
832    Tcl_Obj *format;
833    int *comp;
834    int *verb;
835    int *matte;
836{
837    static const char *const sunOptions[] = {"-compression", "-verbose", "-matte"};
838    int objc, length, c, i, index;
839    Tcl_Obj **objv;
840    const char *compression, *verbose, *transp;
841
842    *comp = 1;
843    *verb = 0;
844    *matte = 1;
845    if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK)
846	return TCL_ERROR;
847    if (objc) {
848	compression = "rle";
849	verbose     = "0";
850	transp      = "1";
851	for (i=1; i<objc; i++) {
852	    if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)sunOptions,
853		    "format option", 0, &index) != TCL_OK) {
854		return TCL_ERROR;
855	    }
856	    if (++i >= objc) {
857		Tcl_AppendResult(interp, "No value for option \"",
858			Tcl_GetStringFromObj (objv[--i], (int *) NULL),
859			"\"", (char *) NULL);
860		return TCL_ERROR;
861	    }
862	    switch(index) {
863		case 0:
864		    compression = Tcl_GetStringFromObj(objv[i], (int *) NULL);
865		    break;
866		case 1:
867		    verbose = Tcl_GetStringFromObj(objv[i], (int *) NULL);
868		    break;
869		case 2:
870		    transp = Tcl_GetStringFromObj(objv[i], (int *) NULL);
871		    break;
872	    }
873	}
874
875	c = compression[0]; length = strlen (compression);
876	if ((c == 'n') && (!strncmp (compression, "none", length))) {
877	    *comp = 0;
878	} else if ((c == 'r') && (!strncmp (compression, "rle",length))) {
879	    *comp = 1;
880	} else {
881	    Tcl_AppendResult(interp, "invalid compression mode \"",
882		    compression, "\": should be rle or none", (char *) NULL);
883	    return TCL_ERROR;
884	}
885
886	c = verbose[0]; length = strlen (verbose);
887	if (!strncmp (verbose, "1", length) || \
888	    !strncmp (verbose, "true", length) || \
889	    !strncmp (verbose, "on", length)) {
890	    *verb = 1;
891	} else if (!strncmp (verbose, "0", length) || \
892	    !strncmp (verbose, "false", length) || \
893	    !strncmp (verbose, "off", length)) {
894	    *verb = 0;
895	} else {
896	    Tcl_AppendResult(interp, "invalid verbose mode \"", verbose,
897                              "\": should be 1 or 0, on or off, true or false",
898			      (char *) NULL);
899	    return TCL_ERROR;
900	}
901
902        c = transp[0]; length = strlen (transp);
903        if (!strncmp (transp, "1", length) || \
904            !strncmp (transp, "true", length) || \
905            !strncmp (transp, "on", length)) {
906            *matte = 1;
907        } else if (!strncmp (transp, "0", length) || \
908            !strncmp (transp, "false", length) || \
909            !strncmp (transp, "off", length)) {
910            *matte = 0;
911        } else {
912            Tcl_AppendResult(interp, "invalid alpha (matte) mode \"", verbose,
913                              "\": should be 1 or 0, on or off, true or false",
914                              (char *) NULL);
915            return TCL_ERROR;
916        }
917    }
918    return TCL_OK;
919}
920
921static int ChnMatch(
922    Tcl_Channel chan,
923    const char *filename,
924    Tcl_Obj *format,
925    int *widthPtr,
926    int *heightPtr,
927    Tcl_Interp *interp
928) {
929    tkimg_MFile handle;
930
931#ifdef DEBUG_LOCAL
932	printf("ChnMatch\n"); fflush(stdout);
933#endif
934
935    handle.data = (char *) chan;
936    handle.state = IMG_CHAN;
937
938    return CommonMatch(&handle, widthPtr, heightPtr, NULL);
939}
940
941static int ObjMatch(
942    Tcl_Obj *data,
943    Tcl_Obj *format,
944    int *widthPtr,
945    int *heightPtr,
946    Tcl_Interp *interp
947) {
948    tkimg_MFile handle;
949
950#ifdef DEBUG_LOCAL
951        printf("ObjMatch\n"); fflush(stdout);
952#endif
953
954    if (!tkimg_ReadInit(data, 'Y', &handle)) {
955	return 0;
956    }
957    return CommonMatch(&handle, widthPtr, heightPtr, NULL);
958}
959
960static int CommonMatch(handle, widthPtr, heightPtr, sunHeaderPtr)
961    tkimg_MFile *handle;
962    int   *widthPtr;
963    int   *heightPtr;
964    SUNHEADER *sunHeaderPtr;
965{
966    SUNHEADER sh;
967
968    if (!read_sun_header (handle, &sh))
969	return 0;
970
971    *widthPtr  = sh.ras_width;
972    *heightPtr = sh.ras_height;
973    if (sunHeaderPtr)
974	*sunHeaderPtr = sh;
975    return 1;
976}
977
978static int ChnRead(interp, chan, filename, format, imageHandle,
979	            destX, destY, width, height, srcX, srcY)
980    Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
981    Tcl_Channel chan;		/* The image channel, open for reading. */
982    const char *filename;	/* The name of the image file. */
983    Tcl_Obj *format;		/* User-specified format object, or NULL. */
984    Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
985    int destX, destY;		/* Coordinates of top-left pixel in
986			         * photo image to be written to. */
987    int width, height;		/* Dimensions of block of photo image to
988			         * be written to. */
989    int srcX, srcY;		/* Coordinates of top-left pixel to be used
990				 * in image being read. */
991{
992    tkimg_MFile handle;
993
994    handle.data = (char *) chan;
995    handle.state = IMG_CHAN;
996
997    return CommonRead (interp, &handle, filename, format,
998		       imageHandle, destX, destY,
999		       width, height, srcX, srcY);
1000}
1001
1002static int ObjRead (interp, data, format, imageHandle,
1003	            destX, destY, width, height, srcX, srcY)
1004    Tcl_Interp *interp;
1005    Tcl_Obj *data;
1006    Tcl_Obj *format;
1007    Tk_PhotoHandle imageHandle;
1008    int destX, destY;
1009    int width, height;
1010    int srcX, srcY;
1011{
1012    tkimg_MFile handle;
1013
1014    tkimg_ReadInit (data, 'Y', &handle);
1015    return CommonRead (interp, &handle, "InlineData", format, imageHandle,
1016		       destX, destY, width, height, srcX, srcY);
1017}
1018
1019static int CommonRead (interp, handle, filename, format, imageHandle,
1020		       destX, destY, width, height, srcX, srcY)
1021    Tcl_Interp *interp;         /* Interpreter to use for reporting errors. */
1022    tkimg_MFile *handle;        /* The image file, open for reading. */
1023    const char *filename;       /* The name of the image file. */
1024    Tcl_Obj *format;            /* User-specified format object, or NULL. */
1025    Tk_PhotoHandle imageHandle; /* The photo image to write into. */
1026    int destX, destY;           /* Coordinates of top-left pixel in
1027				 * photo image to be written to. */
1028    int width, height;          /* Dimensions of block of photo image to
1029			         * be written to. */
1030    int srcX, srcY;             /* Coordinates of top-left pixel to be used
1031			         * in image being read. */
1032{
1033    int nchan;
1034    int fileWidth, fileHeight;
1035    int outWidth, outHeight;
1036    int retCode = TCL_OK;
1037    SUNHEADER sh;
1038    UByte *suncolmap = NULL;
1039    int compr, verbose, matte;
1040    char errMsg[200];
1041
1042    if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) {
1043        return TCL_ERROR;
1044    }
1045
1046    CommonMatch(handle, &fileWidth, &fileHeight, &sh);
1047    if (verbose)
1048        printImgInfo (&sh, filename, "Reading image:");
1049
1050    if ((srcX + width) > fileWidth) {
1051	outWidth = fileWidth - srcX;
1052    } else {
1053	outWidth = width;
1054    }
1055    if ((srcY + height) > fileHeight) {
1056	outHeight = fileHeight - srcY;
1057    } else {
1058	outHeight = height;
1059    }
1060    if ((outWidth <= 0) || (outHeight <= 0)
1061	|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
1062	return TCL_OK;
1063    }
1064
1065    if (sh.ras_type > 5) {
1066	sprintf(errMsg, "Unknown Sun Raster type: %d", sh.ras_type);
1067	Tcl_AppendResult(interp, errMsg, (char *)NULL);
1068	return TCL_ERROR;
1069    }
1070
1071    if (sh.ras_type == RAS_TYPE_RLE)
1072	tkimg_ReadBuffer (1);
1073
1074    /* Is there a RGB colourmap ? */
1075    if ((sh.ras_maptype == 1) && (sh.ras_maplength > 0)) {
1076	suncolmap = (UByte *)ckalloc (sh.ras_maplength);
1077	if (!suncolmap) {
1078	    sprintf(errMsg, "Can't allocate memory of size %d",
1079			      sh.ras_maplength);
1080	    Tcl_AppendResult(interp, errMsg, (char *)NULL);
1081	    tkimg_ReadBuffer (0);
1082            return TCL_ERROR;
1083	}
1084
1085	if (!read_sun_cols (handle, &sh, suncolmap)) {
1086	    Tcl_AppendResult(interp, "Unable to read color map", (char *)NULL);
1087	    ckfree ((char *)suncolmap);
1088	    tkimg_ReadBuffer (0);
1089            return TCL_ERROR;
1090	}
1091#ifdef DEBUG
1092	{
1093	    int j, ncols;
1094	    printf("Colormap values:\n");
1095	    ncols = sh.ras_maplength/3;
1096	    for (j=0; j < ncols; j++)
1097		printf("Entry 0x%08x: 0x%04x,  0x%04x, 0x%04x\n",
1098                       j,suncolmap[j],suncolmap[j+ncols],suncolmap[j+2*ncols]);
1099	}
1100#endif
1101    } else if (sh.ras_maplength > 0) {
1102	UByte dummy[1];
1103	int d, length;
1104
1105	/* This type of colourmap is not supported. Ignore it. */
1106	length = (sizeof (SUNHEADER)/sizeof (UInt)) * 4 + sh.ras_maplength;
1107	for (d=0; d<length; d++) {
1108	    (void) readUByte (handle, dummy);
1109	}
1110    }
1111
1112    if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
1113	if (suncolmap) {
1114	    ckfree ((char *)suncolmap);
1115	}
1116	tkimg_ReadBuffer(0);
1117	return TCL_ERROR;
1118    }
1119
1120    nchan = (sh.ras_depth == 32? 4: 3);
1121
1122    switch (sh.ras_depth)
1123    {
1124	case 1:    /* 2 colors B/W */
1125            if (!load_sun_d1 (interp, handle, imageHandle, destX, destY,
1126			      outWidth, outHeight, srcX, srcY,
1127			      fileWidth, fileHeight, sh.ras_type))
1128		retCode = TCL_ERROR;
1129	    break;
1130
1131	case 8:    /* 256 colours */
1132            if (!load_sun_d8 (interp, handle, imageHandle, destX, destY,
1133			      outWidth, outHeight, srcX, srcY,
1134			      fileWidth, fileHeight, sh.ras_type,
1135			      suncolmap, sh.ras_maplength))
1136		retCode = TCL_ERROR;
1137	    break;
1138
1139	case 24:   /* True color */
1140	case 32:   /* True color with matte channel */
1141            if (!load_rgb (interp, handle, imageHandle, destX, destY,
1142			   outWidth, outHeight, srcX, srcY,
1143			   fileWidth, fileHeight,
1144			   nchan, sh.ras_type, matte))
1145		retCode = TCL_ERROR;
1146	    break;
1147
1148	default:
1149	    sprintf(errMsg, "Image has invalid pixel depth: %d", sh.ras_depth);
1150	    Tcl_AppendResult(interp, errMsg, (char *)NULL);
1151	    retCode = TCL_ERROR;
1152	    break;
1153    }
1154    if (suncolmap)
1155	ckfree ((char *)suncolmap);
1156    tkimg_ReadBuffer (0);
1157    return retCode;
1158}
1159
1160static int ChnWrite (interp, filename, format, blockPtr)
1161    Tcl_Interp *interp;
1162    const char *filename;
1163    Tcl_Obj *format;
1164    Tk_PhotoImageBlock *blockPtr;
1165{
1166    Tcl_Channel chan;
1167    tkimg_MFile handle;
1168    int result;
1169
1170    chan = tkimg_OpenFileChannel (interp, filename, 0644);
1171    if (!chan) {
1172	return TCL_ERROR;
1173    }
1174
1175    handle.data = (char *) chan;
1176    handle.state = IMG_CHAN;
1177
1178    result = CommonWrite (interp, filename, format, &handle, blockPtr);
1179    if (Tcl_Close(interp, chan) == TCL_ERROR) {
1180	return TCL_ERROR;
1181    }
1182    return result;
1183}
1184
1185static int StringWrite(
1186    Tcl_Interp *interp,
1187    Tcl_Obj *format,
1188    Tk_PhotoImageBlock *blockPtr
1189) {
1190    tkimg_MFile handle;
1191    int result;
1192    Tcl_DString data;
1193
1194    Tcl_DStringInit(&data);
1195    tkimg_WriteInit(&data, &handle);
1196    result = CommonWrite(interp, "InlineData", format, &handle, blockPtr);
1197    tkimg_Putc(IMG_DONE, &handle);
1198
1199    if (result == TCL_OK) {
1200	Tcl_DStringResult(interp, &data);
1201    } else {
1202	Tcl_DStringFree(&data);
1203    }
1204    return result;
1205}
1206
1207static int CommonWrite (interp, filename, format, handle, blockPtr)
1208    Tcl_Interp *interp;
1209    const char *filename;
1210    Tcl_Obj *format;
1211    tkimg_MFile *handle;
1212    Tk_PhotoImageBlock *blockPtr;
1213{
1214    int     x, y, nchan, nBytes, linepad;
1215    int     redOffset, greenOffset, blueOffset, alphaOffset;
1216    UByte   *pixelPtr, *pixRowPtr;
1217    SUNHEADER sh;
1218    UByte *row, *rowPtr;
1219    int compr, verbose, matte; /* Format options */
1220    char errMsg[200];
1221
1222    if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) {
1223        return TCL_ERROR;
1224    }
1225
1226    redOffset   = 0;
1227    greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1228    blueOffset  = blockPtr->offset[2] - blockPtr->offset[0];
1229    alphaOffset = blockPtr->offset[0];
1230
1231    if (alphaOffset < blockPtr->offset[2]) {
1232        alphaOffset = blockPtr->offset[2];
1233    }
1234    if (++alphaOffset < blockPtr->pixelSize) {
1235        alphaOffset -= blockPtr->offset[0];
1236    } else {
1237        alphaOffset = 0;
1238    }
1239
1240    nchan   = ((matte && alphaOffset)? 4: 3);
1241    nBytes  = blockPtr->width * nchan;
1242    linepad = nBytes % 2;
1243
1244    /* Fill the Sun header struct and write the header to the channel. */
1245    sh.ras_magic     = RAS_MAGIC;
1246    sh.ras_width     = blockPtr->width;
1247    sh.ras_height    = blockPtr->height;
1248    sh.ras_depth     = 8 * nchan;
1249    sh.ras_length    = (nBytes + linepad) * blockPtr->height;
1250    sh.ras_type      = (compr) ? RAS_TYPE_RLE : RAS_TYPE_STD;
1251    sh.ras_maptype   = 0;   		/* No colourmap */
1252    sh.ras_maplength = 0; 		/* Length of colourmap */
1253
1254    write_sun_header (handle, &sh);
1255
1256    /* Now write out the image data. */
1257    pixRowPtr = blockPtr->pixelPtr + blockPtr->offset[0];
1258    if (!compr) {
1259	row = (UByte *) ckalloc (nBytes);
1260	if (!row) {
1261	    sprintf(errMsg, "Can't allocate memory of size %d", nBytes);
1262	    Tcl_AppendResult(interp, errMsg, (char *)NULL);
1263            return TCL_ERROR;
1264	}
1265	for (y=0; y<blockPtr->height; y++) {
1266	    rowPtr = row;
1267	    pixelPtr = pixRowPtr;
1268	    for (x=0; x<blockPtr->width; x++) {
1269		if (nchan == 4) {
1270		    /* Have a matte channel and write it. */
1271		    *(rowPtr++) = pixelPtr[alphaOffset];
1272		}
1273		*(rowPtr++) = pixelPtr[blueOffset];
1274		*(rowPtr++) = pixelPtr[greenOffset];
1275		*(rowPtr++) = pixelPtr[redOffset];
1276		pixelPtr += blockPtr->pixelSize;
1277	    }
1278	    if (nBytes != tkimg_Write(handle, (const char *)row, nBytes)) {
1279		sprintf(errMsg, "Can't write %d bytes to image file", nBytes);
1280		Tcl_AppendResult(interp, errMsg, (char *)NULL);
1281		ckfree ((char *)row);
1282		return TCL_ERROR;
1283	    }
1284	    for (x=0; x<linepad; x++) {
1285		writeUByte (handle, 0);
1286	    }
1287	    pixRowPtr += blockPtr->pitch;
1288	}
1289	ckfree ((char *)row);
1290    } else { 			/* RLE compression */
1291	rle_startwrite (handle);
1292	for (y = 0; y < blockPtr->height; y++) {
1293	    pixelPtr = pixRowPtr;
1294	    for (x = 0; x < blockPtr->width; x++) {
1295		if (nchan == 4) {
1296		    /* Have a matte channel and write it. */
1297		    rle_fputc (pixelPtr[alphaOffset], handle);
1298		}
1299		rle_fputc (pixelPtr[blueOffset], handle);
1300		rle_fputc (pixelPtr[greenOffset], handle);
1301		rle_fputc (pixelPtr[redOffset], handle);
1302		pixelPtr += blockPtr->pixelSize;
1303	    }
1304	    for (x=0; x<linepad; x++) {
1305		rle_fputc (0, handle);
1306	    }
1307	    pixRowPtr += blockPtr->pitch;
1308	}
1309	rle_endwrite (handle);
1310    }
1311    if (verbose)
1312        printImgInfo (&sh, filename, "Saving image:");
1313    return TCL_OK;
1314}
1315