diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-01-08 21:04:08 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-01-08 21:04:08 (GMT) |
commit | 6dac183be1cdcc0a13966343e2c40c3ff56a3810 (patch) | |
tree | 802a6a87435da647463c945cf3571d52a84aad55 /tkimg/sun/sun.c | |
parent | dbbb38af9cefef9e9e1a03c97945ee59063aa782 (diff) | |
download | blt-6dac183be1cdcc0a13966343e2c40c3ff56a3810.zip blt-6dac183be1cdcc0a13966343e2c40c3ff56a3810.tar.gz blt-6dac183be1cdcc0a13966343e2c40c3ff56a3810.tar.bz2 |
update tkimg 1.4.7
Diffstat (limited to 'tkimg/sun/sun.c')
-rw-r--r-- | tkimg/sun/sun.c | 1318 |
1 files changed, 1318 insertions, 0 deletions
diff --git a/tkimg/sun/sun.c b/tkimg/sun/sun.c new file mode 100644 index 0000000..03a5f63 --- /dev/null +++ b/tkimg/sun/sun.c @@ -0,0 +1,1318 @@ +/* STARTHEADER + * + * File : sun.c + * + * Author : Paul Obermeier (paul@poSoft.de) + * + * Date : Mon Jan 22 21:32:48 CET 2001 + * + * Copyright : (C) 2001-2002 Paul Obermeier + * + * Description : + * + * A photo image handler for SUN's Raster file format. + * + * The following image types are supported: + * + * 1-bit pixels: Black and White. + * 8-bit pixels: Grayscale or indexed. + * 24-bit pixels: True-color (RGB, each channel 8 bit). + * 32-bit pixels: True-color with alpha channel (RGBA, each channel 8 bit). + * + * List of currently supported features: + * + * Type | Read | Write | + * | -file | -data | -file | -data | + * ---------------------------------------- + * 1-bit | Yes | Yes | No | No | + * 8-bit | Yes | Yes | No | No | + * 24-bit | Yes | Yes | Yes | Yes | + * 32-bit | Yes | Yes | Yes | Yes | + * + * All images types may be either uncompressed or run-length encoded. + * + * + * The following format options are available: + * + * Read SUN image: "sun -matte <bool> -verbose <bool>" + * Write SUN image: "sun -matte <bool> -verbose <bool> -compression <type>" + * + * -matte <bool>: If set to false, a matte (alpha) channel is ignored + * during reading or writing. Default is true. + * -verbose <bool>: If set to true, additional information about the file + * format is printed to stdout. Default is false. + * -compression <type>: Set the compression mode to either "none" or "rle". + * Default is "rle". + * + * Notes: + * + * - The "UNIX" encoding of SUN's "imagetool" is not supported. + * + * - Part of this code was taken from the "sunras" GIMP plugin: + * + * >> The GIMP -- an image manipulation program + * >> Copyright (C) 1995 Spencer Kimball and Peter Mattis + * >> SUN raster reading and writing code Copyright (C) 1996 Peter Kirchgessner + * >> (email: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg) + * + * ENDHEADER + * + */ + +/* + * Generic initialization code, parameterized via CPACKAGE and PACKAGE. + */ + +#include "init.c" + + +/* #define DEBUG_LOCAL */ + +/* Some defines and typedefs. */ +#define TRUE 1 +#define FALSE 0 +typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */ +typedef char Byte; /* Signed 8 bit integer */ +typedef unsigned char UByte; /* Unsigned 8 bit integer */ +typedef unsigned short UShort; /* Unsigned 16 bit integer */ +typedef unsigned int UInt; /* Unsigned 32 bit integer */ + +/* SunRaster magic number */ +#define RAS_MAGIC 0x59a66a95 + +/* Supported SunRaster types */ +#define RAS_TYPE_STD 1 /* Standard uncompressed format */ +#define RAS_TYPE_RLE 2 /* Runlength compression format */ + +/* The SunRaster header structure */ +typedef struct { + UInt ras_magic; /* Magic Number */ + UInt ras_width; /* Width */ + UInt ras_height; /* Height */ + UInt ras_depth; /* Number of bits per pixel (1,8,24,32) */ + UInt ras_length; /* Length of image data (but may also be 0) */ + UInt ras_type; /* Encoding */ + UInt ras_maptype; /* Type of colormap */ + UInt ras_maplength;/* Number of bytes for colormap */ +} SUNHEADER; + +/* Buffer for run-length encoding and decoding. */ +typedef struct { + int val; /* The value that is to be repeated */ + int n; /* How many times it is repeated */ +} RLEBUF; + +/* Forward declarations of static functions. */ +/* This function is commented out because it is not used anywhere +static void byte2bit (UByte *byteline, int width, UByte *bitline, int invert); +*/ + +static void rle_startread (tkimg_MFile *ifp); +static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp); +static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp); +static int rle_fgetc (tkimg_MFile *ifp); +static int sun_getc (tkimg_MFile *ifp); +#define rle_getc(fp) ((rlebuf.n > 0) ? (rlebuf.n)--,rlebuf.val : rle_fgetc (fp)) + +static void rle_startwrite (tkimg_MFile *ofp); +/* This function is commented out because it is not used anywhere +static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp); +*/ +static int rle_fputc (int val, tkimg_MFile *ofp); +static int rle_putrun (int n, int val, tkimg_MFile *ofp); +static void rle_endwrite (tkimg_MFile *ofp); + +static Boln read_sun_header (tkimg_MFile *ifp, SUNHEADER *sunhdr); +static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr); +static Boln read_sun_cols (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap); +/* This function is commented out because it is not used anywhere +static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap); +*/ + +static RLEBUF + rlebuf; + +#ifdef DEBUG_LOCAL +static Boln readUByte (tkimg_MFile *handle, UByte *b) +{ + char buf[1]; + if (1 != tkimg_Read(handle, buf, 1)) + return FALSE; + *b = (UByte) buf[0]; + return TRUE; +} +#else + /* Use this macro for better performance, esp. when reading RLE files. */ +# define readUByte(h,b) (1 == tkimg_Read((h),(char *)(b),1)) +#endif + +static Boln writeUByte (tkimg_MFile *handle, UByte b) +{ + UByte buf[1]; + buf[0] = b; + if (1 != tkimg_Write(handle, (const char *)buf, 1)) + return FALSE; + return TRUE; +} + +static Boln readUInt (tkimg_MFile *ifp, UInt *i) +{ + UByte buf[4]; + UInt c; + + if (4 != tkimg_Read(ifp, (char *)buf, 4)) { + return FALSE; + } + + c = (((UInt)(buf[0])) << 24); + c |= (((UInt)(buf[1])) << 16); + c |= (((UInt)(buf[2])) << 8); + c |= ((UInt)(buf[3])); + *i = c; + return TRUE; +} + +static Boln writeUInt (tkimg_MFile *ofp, UInt c) +{ + UByte buf[4]; + + buf[0] = (c >> 24) & 0xff; + buf[1] = (c >> 16) & 0xff; + buf[2] = (c >> 8) & 0xff; + buf[3] = (c ) & 0xff; + if (4 != tkimg_Write(ofp, (const char *)buf, 4)) { + return FALSE; + } + return TRUE; +} + +/* Convert n bytes of 0/1 to a line of bits */ +/* This function is commented out because it is not used anywhere +static void byte2bit (UByte *byteline, int width, + UByte *bitline, int invert) +{ + UByte bitval; + UByte rest[8]; + + while (width >= 8) { + bitval = 0; + if (*(byteline++)) bitval |= 0x80; + if (*(byteline++)) bitval |= 0x40; + if (*(byteline++)) bitval |= 0x20; + if (*(byteline++)) bitval |= 0x10; + if (*(byteline++)) bitval |= 0x08; + if (*(byteline++)) bitval |= 0x04; + if (*(byteline++)) bitval |= 0x02; + if (*(byteline++)) bitval |= 0x01; + *(bitline++) = invert ? ~bitval : bitval; + width -= 8; + } + if (width > 0) { + memset (rest, 0, 8); + memcpy (rest, byteline, width); + bitval = 0; + byteline = rest; + if (*(byteline++)) bitval |= 0x80; + if (*(byteline++)) bitval |= 0x40; + if (*(byteline++)) bitval |= 0x20; + if (*(byteline++)) bitval |= 0x10; + if (*(byteline++)) bitval |= 0x08; + if (*(byteline++)) bitval |= 0x04; + if (*(byteline++)) bitval |= 0x02; + *bitline = invert ? ~bitval : bitval; + } +} +*/ + +/* Start reading Runlength Encoded Data */ +static void rle_startread (tkimg_MFile *ifp) +{ + (void) ifp; + rlebuf.val = rlebuf.n = 0; +} + +/* Read pixels from RLE-stream */ +static int rle_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp) +{ + int elem_read, cnt, val, err = 0; + + for (elem_read = 0; elem_read < nelem; elem_read++) + { + for (cnt = 0; cnt < sz; cnt++) + { + val = rle_getc (ifp); + if (val < 0) { + err = 1; + break; + } + *(ptr++) = (char)val; + } + if (err) + break; + } + return elem_read; +} + +/* Read uncompressed pixels from input stream "ifp" */ +static int sun_fread (char *ptr, int sz, int nelem, tkimg_MFile *ifp) +{ + if (nelem*sz != tkimg_Read(ifp, ptr, nelem*sz)) { + return -1; + } + return nelem; +} + +/* Get one pixel from RLE-stream */ +static int rle_fgetc (tkimg_MFile *ifp) +{ + UByte flag, runcnt, runval; + + if (rlebuf.n > 0) /* Something in the buffer ? */ + { + (rlebuf.n)--; + return rlebuf.val; + } + + /* Nothing in the buffer. We have to read something */ + if (!readUByte (ifp, &flag)) + return -1; + if (flag != 0x80) return flag; /* Single byte run ? */ + + if (!readUByte (ifp, &runcnt)) + return -1; + if (runcnt == 0) return (0x80); /* Single 0x80 ? */ + + /* The run */ + if (!readUByte (ifp, &runval)) + return -1; + rlebuf.n = runcnt; + rlebuf.val = runval; + return runval; +} + +/* Read one byte from input stream "ifp" */ +static int sun_getc (tkimg_MFile *ifp) +{ + UByte val; + if (!readUByte (ifp, &val)) + return -1; + return val; +} + +/* Start writing Runlength Encoded Data */ +static void rle_startwrite (tkimg_MFile *ofp) +{ + (void) ofp; + rlebuf.val = 0; + rlebuf.n = 0; +} + +/* Write uncompressed elements to RLE-stream */ +/* This function is commented out because it is not used anywhere +static int rle_fwrite (char *ptr, int sz, int nelem, tkimg_MFile *ofp) +{ + int elem_write, cnt, val, err = 0; + UByte *pixels = (UByte *)ptr; + + for (elem_write = 0; elem_write < nelem; elem_write++) { + for (cnt = 0; cnt < sz; cnt++) { + val = rle_fputc (*(pixels++), ofp); + if (val < 0) { + err = 1; + break; + } + } + if (err) + break; + } + return elem_write; +} +*/ + +/* Write uncompressed character to RLE-stream */ +static int rle_fputc (int val, tkimg_MFile *ofp) +{ + int retval; + + if (rlebuf.n == 0) { /* Nothing in the buffer ? Save the value */ + rlebuf.n = 1; + rlebuf.val = val; + return val; + } + + /* Something in the buffer */ + if (rlebuf.val == val) { /* Same value in the buffer ? */ + rlebuf.n++; + if (rlebuf.n == 257) { /* Can not be encoded in a single run ? */ + retval = rle_putrun (256, rlebuf.val, ofp); + if (retval < 0) + return retval; + rlebuf.n -= 256; + } + return val; + } + + /* Something different in the buffer ? Write out the run */ + retval = rle_putrun (rlebuf.n, rlebuf.val, ofp); + if (retval < 0) + return retval; + + /* Save the new value */ + rlebuf.n = 1; + return (rlebuf.val = val); +} + +/* Write out a run with 0 < n < 257 */ +static int rle_putrun (int n, int val, tkimg_MFile *ofp) +{ + int retval = 1, + flag = 0x80; + + /* Useful to write a 3 byte run ? */ + if ((n > 2) || ((n == 2) && (val == flag))) { + if (!writeUByte (ofp, flag) || + !writeUByte (ofp, n-1) || + !writeUByte (ofp, val)) { + retval = -1; + } + } + else if (n == 2) { + /* Write two single runs (could not be value 0x80) */ + if (!writeUByte (ofp, val) || !writeUByte (ofp, val)) { + retval = -1; + } + } else { /* Write a single run */ + if (val == flag) { + if (!writeUByte (ofp, flag) || !writeUByte (ofp, 0)) { + retval = -1; + } + } else { + if (!writeUByte (ofp, val)) { + retval = -1; + } + } + } + + return ((retval < 0) ? retval : val); +} + +/* End writing Runlength Encoded Data */ +static void rle_endwrite (tkimg_MFile *ofp) +{ + if (rlebuf.n > 0) { + rle_putrun (rlebuf.n, rlebuf.val, ofp); + rlebuf.val = rlebuf.n = 0; /* Clear RLE-buffer */ + } +} + +#define OUT Tcl_WriteChars (outChan, str, -1) +static void printImgInfo (SUNHEADER *sh, const char *filename, const char *msg) +{ + Tcl_Channel outChan; + char str[256]; + UInt type = sh->ras_type; + + outChan = Tcl_GetStdChannel (TCL_STDOUT); + if (!outChan) { + return; + } + + sprintf(str, "%s %s\n", msg, filename); OUT; + sprintf(str, "\tSize in pixel : %d x %d\n", sh->ras_width, sh->ras_height); OUT; + sprintf(str, "\tDepth of pixels : %d\n", sh->ras_depth); OUT; + sprintf(str, "\tCompression : %s\n", (type == RAS_TYPE_STD? "None": + (type == RAS_TYPE_RLE? "RLE": + "Unknown"))); OUT; + sprintf(str, "\tColormap type : %d\n", sh->ras_maptype); OUT; + Tcl_Flush(outChan); +} +#undef OUT + +static Boln read_sun_header (tkimg_MFile *ifp, SUNHEADER *sunhdr) +{ + int i; + UInt *cp; + + cp = (UInt *)sunhdr; + + /* Read in all 32-bit values of the header and check for byte order */ + for (i=0; i<sizeof(SUNHEADER) / sizeof(sunhdr->ras_magic); i++) { + if (!readUInt (ifp, cp)) + return FALSE; + cp++; + } + if (sunhdr->ras_magic != RAS_MAGIC) + return FALSE; + return TRUE; +} + +/* Write out a SUN-fileheader */ +static Boln write_sun_header (tkimg_MFile *ofp, SUNHEADER *sunhdr) +{ + int i, hdr_entries; + UInt *cp; + + cp = (UInt *)sunhdr; + + hdr_entries = sizeof (SUNHEADER)/sizeof(sunhdr->ras_magic); + /* Write out all 32-bit values of the header and check for byte order */ + for (i=0; i<hdr_entries; i++) { + if (!writeUInt (ofp, *(cp++))) { + return FALSE; + } + } + return TRUE; +} + +/* Read the sun colourmap */ +static Boln read_sun_cols (tkimg_MFile *ifp, SUNHEADER *sunhdr, UByte *colormap) +{ + int ncols; + + /* Read in SUN-raster Colormap */ + ncols = sunhdr->ras_maplength / 3; + if (ncols <= 0) + return FALSE; + + if (3*ncols != tkimg_Read(ifp, (char *)colormap, 3*ncols)) { + return FALSE; + } + return TRUE; +} + +/* Write a sun colourmap */ +/* This function is commented out because it is not used anywhere +static Boln write_sun_cols (tkimg_MFile *ofp, SUNHEADER *sunhdr, UByte *colormap) +{ + int ncols; + + ncols = sunhdr->ras_maplength / 3; + if (3*ncols != tkimg_Write(ofp, (const char *)colormap, 3*ncols)) { + return FALSE; + } + return TRUE; +} +*/ + +/* Load SUN Raster file with depth 1 */ +static Boln load_sun_d1 (Tcl_Interp *interp, tkimg_MFile *ifp, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY, + int fileWidth, int fileHeight, int type) +{ + UByte *dest, bit2byte[256*8]; + UByte *pixbuf; + Tk_PhotoImageBlock block; + int pix8; + int linepad; + int x, y; + int stopY, outY; + int i, j; + int err = 0, rle; + char errMsg[200]; + Boln result = TRUE; + + pixbuf = (UByte *) ckalloc (fileWidth); + if (!pixbuf) { + sprintf(errMsg, "Can't allocate memory of size %d", fileWidth); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + return TCL_ERROR; + } + + block.pixelSize = 1; + block.pitch = fileWidth; + block.width = width; + block.height = 1; + block.offset[0] = 0; + block.offset[1] = 0; + block.offset[2] = 0; + block.offset[3] = 0; + block.pixelPtr = pixbuf + srcX; + + rle = (type == RAS_TYPE_RLE); + linepad = ((fileWidth+7)/8) % 2; /* Check for 16bit align */ + + if (rle) + rle_startread (ifp); + + /* Get an array for mapping 8 bits in a byte to 8 bytes */ + dest = bit2byte; + for (j=0; j<256; j++) { + for (i=7; i>=0; i--) { + *(dest++) = ((j & (1 << i)) == 0) * 255; + } + } + + stopY = srcY + height; + outY = destY; + + for (y=0; y<stopY; y++) { + dest = pixbuf; + x = fileWidth; + while (x >= 8) { + pix8 = rle ? rle_getc (ifp) : sun_getc (ifp); + if (pix8 < 0) { err = 1; pix8 = 0; } + + memcpy (dest, bit2byte + pix8*8, 8); + dest += 8; + x -= 8; + } + + if (x>0) { + pix8 = rle ? rle_getc (ifp) : sun_getc (ifp); + if (pix8 < 0) { err = 1; pix8 = 0; } + + memcpy (dest, bit2byte + pix8*8, x); + dest += x; + } + + if (linepad) + err |= ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); + + if (err) { + sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); + Tcl_AppendResult(interp, errMsg, (char *) NULL); + return FALSE; + } + if (y >= srcY) { + if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { + result = FALSE; + break; + } + outY++; + } + } + return result; +} + +/* Load SUN Raster file with depth 8 */ +static Boln load_sun_d8 (Tcl_Interp *interp, tkimg_MFile *ifp, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY, + int fileWidth, int fileHeight, + int type, UByte *suncolmap, int maplength) +{ + UByte *dest, *indData = NULL, *src; + UByte *pixbuf = NULL; + Tk_PhotoImageBlock block; + int linepad; + int x, y; + int stopY, outY; + int ncols; + int greyscale, nchan; + int err, rle; + char errMsg[200]; + Boln result = TRUE; + + rle = (type == RAS_TYPE_RLE); + linepad = fileWidth % 2; + ncols = maplength / 3; + + /* Check, if it's a greyscale or color indexed image. */ + greyscale = 1; + nchan = 1; + if ((ncols > 0) && (suncolmap != NULL)) { + greyscale = 0; + nchan = 3; + } + + if (!greyscale) { + pixbuf = (UByte *) ckalloc (fileWidth * nchan); + if (!pixbuf) { + sprintf(errMsg, "Can't allocate memory of size %d", + fileWidth * nchan); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + if (suncolmap) + ckfree ((char *)suncolmap); + return TCL_ERROR; + } + } + + /* This buffer contains either the color indices or the greyscale value. */ + indData = (UByte *)ckalloc (fileWidth * sizeof (UByte)); + if (!indData) { + sprintf(errMsg, "Can't allocate memory of size %d", + (int) (fileWidth * sizeof (UByte))); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + return TCL_ERROR; + } + + block.pixelSize = nchan; + block.pitch = fileWidth * nchan; + block.width = width; + block.height = 1; + block.offset[0] = 0; + block.offset[1] = greyscale? 0: 1; + block.offset[2] = greyscale? 0: 2; + block.offset[3] = 0; + block.pixelPtr = (greyscale? + (indData + srcX * nchan): + (pixbuf + srcX * nchan)); + + if (rle) + rle_startread (ifp); /* Initialize RLE-buffer */ + + stopY = srcY + height; + outY = destY; + + for (y=0; y<stopY; y++) { + src = indData; + memset ((char *)src, 0, fileWidth); + err = ((rle ? rle_fread ((char *)src, 1, fileWidth, ifp) : + sun_fread ((char *)src, 1, fileWidth, ifp)) != fileWidth); + if (err && (y != height -1)) { + sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); + Tcl_AppendResult(interp, errMsg, (char *) NULL); + ckfree ((char *)indData); + return FALSE; + } + if (linepad) { + err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); + if (err) { + sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); + Tcl_AppendResult(interp, errMsg, (char *) NULL); + ckfree ((char *)indData); + return FALSE; + } + } + + if (!greyscale) { + src = indData; + dest = pixbuf; + for (x=0; x<width; x++) { + *dest++ = suncolmap[*src]; + *dest++ = suncolmap[*src + ncols]; + *dest++ = suncolmap[*src + 2*ncols]; + src++; + } + } + + if (y >= srcY) { + if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { + result = FALSE; + break; + } + outY++; + } + } + ckfree ((char *)indData); + return result; +} + +/* Load SUN Raster file with true color image: depth = 24 or 32 */ + +static Boln load_rgb (Tcl_Interp *interp, tkimg_MFile *ifp, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY, + int fileWidth, int fileHeight, + int nchan, int type, int showMatte) +{ + UByte *dest, tmp; + UByte *pixbuf; + Tk_PhotoImageBlock block; + int linepad; + int x, y; + int stopY, outY; + int err, rle; + char errMsg[200]; + Boln result = TRUE; + + pixbuf = (UByte *) ckalloc (fileWidth * nchan); + if (!pixbuf) { + sprintf(errMsg, "Can't allocate memory of size %d", + fileWidth * nchan); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + return TCL_ERROR; + } + + block.pixelSize = nchan; + block.pitch = fileWidth * nchan; + block.width = width; + block.height = 1; + block.offset[0] = 0; + block.offset[1] = 1; + block.offset[2] = 2; + if (nchan < 4) { + showMatte = 0; + } + block.offset[3] = showMatte? 3: 0; + + block.pixelPtr = pixbuf + srcX * nchan; + + rle = (type == RAS_TYPE_RLE); + linepad = (fileWidth*nchan) % 2; + + if (rle) + rle_startread (ifp); /* Initialize RLE-buffer */ + + stopY = srcY + height; + outY = destY; + + for (y=0; y<stopY; y++) { + dest = pixbuf; + memset ((char *)dest, 0, nchan*fileWidth); + err = ((rle ? rle_fread ((char *)dest, nchan, fileWidth, ifp) : + sun_fread ((char *)dest, nchan, fileWidth, ifp)) != fileWidth); + if (err && (y != height -1)) { + sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); + Tcl_AppendResult(interp, errMsg, (char *) NULL); + ckfree ((char *)pixbuf); + return FALSE; + } + if (linepad) { + err = ((rle ? rle_getc (ifp) : sun_getc (ifp)) < 0); + if (err) { + sprintf(errMsg, "Unexpected EOF while reading scanline %d", y); + Tcl_AppendResult(interp, errMsg, (char *) NULL); + ckfree ((char *)pixbuf); + return FALSE; + } + } + + if (y >= srcY) { + dest = pixbuf + srcX * nchan; + if (type != 3) { + if (nchan == 3) { /* GBR Format. Swap to RGB. */ + for (x=0; x<width; x++) { + tmp = dest[0]; + dest[0] = dest[2]; + dest[2] = tmp; + + dest += 3; + } + } else { /* AGBR Format. Swap to RGBA. */ + for (x=0; x<width; x++) { + tmp = dest[0]; + dest[0] = dest[3]; + dest[3] = tmp; + + tmp = dest[1]; + dest[1] = dest[2]; + dest[2] = tmp; + + dest += 4; + } + } + } + if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1, showMatte? TK_PHOTO_COMPOSITE_OVERLAY: TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { + result = FALSE; + break; + } + outY++; + } + } + ckfree ((char *)pixbuf); + return result; +} + +/* + * Here is the start of the standard functions needed for every image format. + */ + +/* + * Prototypes for local procedures defined in this file: + */ + +static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, + int *comp, int *verb, int *matte); +static int CommonMatch(tkimg_MFile *handle, int *widthPtr, + int *heightPtr, SUNHEADER *sunHeaderPtr); +static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle, + const char *filename, Tcl_Obj *format, + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY); +static int CommonWrite(Tcl_Interp *interp, + const char *filename, Tcl_Obj *format, + tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr); + +static int ParseFormatOpts (interp, format, comp, verb, matte) + Tcl_Interp *interp; + Tcl_Obj *format; + int *comp; + int *verb; + int *matte; +{ + static const char *const sunOptions[] = {"-compression", "-verbose", "-matte", NULL}; + int objc, length, c, i, index; + Tcl_Obj **objv; + const char *compression, *verbose, *transp; + + *comp = 1; + *verb = 0; + *matte = 1; + if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) + return TCL_ERROR; + if (objc) { + compression = "rle"; + verbose = "0"; + transp = "1"; + for (i=1; i<objc; i++) { + if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)sunOptions, + "format option", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + if (++i >= objc) { + Tcl_AppendResult(interp, "No value for option \"", + Tcl_GetStringFromObj (objv[--i], (int *) NULL), + "\"", (char *) NULL); + return TCL_ERROR; + } + switch(index) { + case 0: + compression = Tcl_GetStringFromObj(objv[i], (int *) NULL); + break; + case 1: + verbose = Tcl_GetStringFromObj(objv[i], (int *) NULL); + break; + case 2: + transp = Tcl_GetStringFromObj(objv[i], (int *) NULL); + break; + } + } + + c = compression[0]; length = strlen (compression); + if ((c == 'n') && (!strncmp (compression, "none", length))) { + *comp = 0; + } else if ((c == 'r') && (!strncmp (compression, "rle",length))) { + *comp = 1; + } else { + Tcl_AppendResult(interp, "invalid compression mode \"", + compression, "\": should be rle or none", (char *) NULL); + return TCL_ERROR; + } + + c = verbose[0]; length = strlen (verbose); + if (!strncmp (verbose, "1", length) || \ + !strncmp (verbose, "true", length) || \ + !strncmp (verbose, "on", length)) { + *verb = 1; + } else if (!strncmp (verbose, "0", length) || \ + !strncmp (verbose, "false", length) || \ + !strncmp (verbose, "off", length)) { + *verb = 0; + } else { + Tcl_AppendResult(interp, "invalid verbose mode \"", verbose, + "\": should be 1 or 0, on or off, true or false", + (char *) NULL); + return TCL_ERROR; + } + + c = transp[0]; length = strlen (transp); + if (!strncmp (transp, "1", length) || \ + !strncmp (transp, "true", length) || \ + !strncmp (transp, "on", length)) { + *matte = 1; + } else if (!strncmp (transp, "0", length) || \ + !strncmp (transp, "false", length) || \ + !strncmp (transp, "off", length)) { + *matte = 0; + } else { + Tcl_AppendResult(interp, "invalid alpha (matte) mode \"", verbose, + "\": should be 1 or 0, on or off, true or false", + (char *) NULL); + return TCL_ERROR; + } + } + return TCL_OK; +} + +static int ChnMatch( + Tcl_Channel chan, + const char *filename, + Tcl_Obj *format, + int *widthPtr, + int *heightPtr, + Tcl_Interp *interp +) { + tkimg_MFile handle; + +#ifdef DEBUG_LOCAL + printf("ChnMatch\n"); fflush(stdout); +#endif + + handle.data = (char *) chan; + handle.state = IMG_CHAN; + + return CommonMatch(&handle, widthPtr, heightPtr, NULL); +} + +static int ObjMatch( + Tcl_Obj *data, + Tcl_Obj *format, + int *widthPtr, + int *heightPtr, + Tcl_Interp *interp +) { + tkimg_MFile handle; + +#ifdef DEBUG_LOCAL + printf("ObjMatch\n"); fflush(stdout); +#endif + + if (!tkimg_ReadInit(data, 'Y', &handle)) { + return 0; + } + return CommonMatch(&handle, widthPtr, heightPtr, NULL); +} + +static int CommonMatch(handle, widthPtr, heightPtr, sunHeaderPtr) + tkimg_MFile *handle; + int *widthPtr; + int *heightPtr; + SUNHEADER *sunHeaderPtr; +{ + SUNHEADER sh; + + if (!read_sun_header (handle, &sh)) + return 0; + + *widthPtr = sh.ras_width; + *heightPtr = sh.ras_height; + if (sunHeaderPtr) + *sunHeaderPtr = sh; + return 1; +} + +static int ChnRead(interp, chan, filename, format, imageHandle, + destX, destY, width, height, srcX, srcY) + Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ + Tcl_Channel chan; /* The image channel, open for reading. */ + const char *filename; /* The name of the image file. */ + Tcl_Obj *format; /* User-specified format object, or NULL. */ + Tk_PhotoHandle imageHandle; /* The photo image to write into. */ + int destX, destY; /* Coordinates of top-left pixel in + * photo image to be written to. */ + int width, height; /* Dimensions of block of photo image to + * be written to. */ + int srcX, srcY; /* Coordinates of top-left pixel to be used + * in image being read. */ +{ + tkimg_MFile handle; + + handle.data = (char *) chan; + handle.state = IMG_CHAN; + + return CommonRead (interp, &handle, filename, format, + imageHandle, destX, destY, + width, height, srcX, srcY); +} + +static int ObjRead (interp, data, format, imageHandle, + destX, destY, width, height, srcX, srcY) + Tcl_Interp *interp; + Tcl_Obj *data; + Tcl_Obj *format; + Tk_PhotoHandle imageHandle; + int destX, destY; + int width, height; + int srcX, srcY; +{ + tkimg_MFile handle; + + tkimg_ReadInit (data, 'Y', &handle); + return CommonRead (interp, &handle, "InlineData", format, imageHandle, + destX, destY, width, height, srcX, srcY); +} + +static int CommonRead (interp, handle, filename, format, imageHandle, + destX, destY, width, height, srcX, srcY) + Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ + tkimg_MFile *handle; /* The image file, open for reading. */ + const char *filename; /* The name of the image file. */ + Tcl_Obj *format; /* User-specified format object, or NULL. */ + Tk_PhotoHandle imageHandle; /* The photo image to write into. */ + int destX, destY; /* Coordinates of top-left pixel in + * photo image to be written to. */ + int width, height; /* Dimensions of block of photo image to + * be written to. */ + int srcX, srcY; /* Coordinates of top-left pixel to be used + * in image being read. */ +{ + int nchan; + int fileWidth, fileHeight; + int outWidth, outHeight; + int retCode = TCL_OK; + SUNHEADER sh; + UByte *suncolmap = NULL; + int compr, verbose, matte; + char errMsg[200]; + + if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) { + return TCL_ERROR; + } + + if (!CommonMatch(handle, &fileWidth, &fileHeight, &sh)) { + if (interp){ + Tcl_AppendResult(interp, "Cannot read image data", + (char *) NULL); + } + return TCL_ERROR; + } + if (verbose) + printImgInfo (&sh, filename, "Reading image:"); + + if ((srcX + width) > fileWidth) { + outWidth = fileWidth - srcX; + } else { + outWidth = width; + } + if ((srcY + height) > fileHeight) { + outHeight = fileHeight - srcY; + } else { + outHeight = height; + } + if ((outWidth <= 0) || (outHeight <= 0) + || (srcX >= fileWidth) || (srcY >= fileHeight)) { + return TCL_OK; + } + + if (sh.ras_type > 5) { + sprintf(errMsg, "Unknown Sun Raster type: %d", sh.ras_type); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + return TCL_ERROR; + } + + if (sh.ras_type == RAS_TYPE_RLE) + tkimg_ReadBuffer (1); + + /* Is there a RGB colourmap ? */ + if ((sh.ras_maptype == 1) && (sh.ras_maplength > 0)) { + suncolmap = (UByte *)ckalloc (sh.ras_maplength); + if (!suncolmap) { + sprintf(errMsg, "Can't allocate memory of size %d", + sh.ras_maplength); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + tkimg_ReadBuffer (0); + return TCL_ERROR; + } + + if (!read_sun_cols (handle, &sh, suncolmap)) { + Tcl_AppendResult(interp, "Unable to read color map", (char *)NULL); + ckfree ((char *)suncolmap); + tkimg_ReadBuffer (0); + return TCL_ERROR; + } +#ifdef DEBUG + { + int j, ncols; + printf("Colormap values:\n"); + ncols = sh.ras_maplength/3; + for (j=0; j < ncols; j++) + printf("Entry 0x%08x: 0x%04x, 0x%04x, 0x%04x\n", + j,suncolmap[j],suncolmap[j+ncols],suncolmap[j+2*ncols]); + } +#endif + } else if (sh.ras_maplength > 0) { + UByte dummy[1]; + int d, length; + + /* This type of colourmap is not supported. Ignore it. */ + length = (sizeof (SUNHEADER)/sizeof (UInt)) * 4 + sh.ras_maplength; + for (d=0; d<length; d++) { + (void) readUByte (handle, dummy); + } + } + + if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) { + if (suncolmap) { + ckfree ((char *)suncolmap); + } + tkimg_ReadBuffer(0); + return TCL_ERROR; + } + + nchan = (sh.ras_depth == 32? 4: 3); + + switch (sh.ras_depth) + { + case 1: /* 2 colors B/W */ + if (!load_sun_d1 (interp, handle, imageHandle, destX, destY, + outWidth, outHeight, srcX, srcY, + fileWidth, fileHeight, sh.ras_type)) + retCode = TCL_ERROR; + break; + + case 8: /* 256 colours */ + if (!load_sun_d8 (interp, handle, imageHandle, destX, destY, + outWidth, outHeight, srcX, srcY, + fileWidth, fileHeight, sh.ras_type, + suncolmap, sh.ras_maplength)) + retCode = TCL_ERROR; + break; + + case 24: /* True color */ + case 32: /* True color with matte channel */ + if (!load_rgb (interp, handle, imageHandle, destX, destY, + outWidth, outHeight, srcX, srcY, + fileWidth, fileHeight, + nchan, sh.ras_type, matte)) + retCode = TCL_ERROR; + break; + + default: + sprintf(errMsg, "Image has invalid pixel depth: %d", sh.ras_depth); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + retCode = TCL_ERROR; + break; + } + if (suncolmap) + ckfree ((char *)suncolmap); + tkimg_ReadBuffer (0); + return retCode; +} + +static int ChnWrite (interp, filename, format, blockPtr) + Tcl_Interp *interp; + const char *filename; + Tcl_Obj *format; + Tk_PhotoImageBlock *blockPtr; +{ + Tcl_Channel chan; + tkimg_MFile handle; + int result; + + chan = tkimg_OpenFileChannel (interp, filename, 0644); + if (!chan) { + return TCL_ERROR; + } + + handle.data = (char *) chan; + handle.state = IMG_CHAN; + + result = CommonWrite (interp, filename, format, &handle, blockPtr); + if (Tcl_Close(interp, chan) == TCL_ERROR) { + return TCL_ERROR; + } + return result; +} + +static int StringWrite( + Tcl_Interp *interp, + Tcl_Obj *format, + Tk_PhotoImageBlock *blockPtr +) { + tkimg_MFile handle; + int result; + Tcl_DString data; + + Tcl_DStringInit(&data); + tkimg_WriteInit(&data, &handle); + result = CommonWrite(interp, "InlineData", format, &handle, blockPtr); + tkimg_Putc(IMG_DONE, &handle); + + if (result == TCL_OK) { + Tcl_DStringResult(interp, &data); + } else { + Tcl_DStringFree(&data); + } + return result; +} + +static int CommonWrite (interp, filename, format, handle, blockPtr) + Tcl_Interp *interp; + const char *filename; + Tcl_Obj *format; + tkimg_MFile *handle; + Tk_PhotoImageBlock *blockPtr; +{ + int x, y, nchan, nBytes, linepad; + int redOffset, greenOffset, blueOffset, alphaOffset; + UByte *pixelPtr, *pixRowPtr; + SUNHEADER sh; + UByte *row, *rowPtr; + int compr, verbose, matte; /* Format options */ + char errMsg[200]; + + if (ParseFormatOpts(interp, format, &compr, &verbose, &matte) != TCL_OK) { + return TCL_ERROR; + } + + redOffset = 0; + greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; + blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; + alphaOffset = blockPtr->offset[0]; + + if (alphaOffset < blockPtr->offset[2]) { + alphaOffset = blockPtr->offset[2]; + } + if (++alphaOffset < blockPtr->pixelSize) { + alphaOffset -= blockPtr->offset[0]; + } else { + alphaOffset = 0; + } + + nchan = ((matte && alphaOffset)? 4: 3); + nBytes = blockPtr->width * nchan; + linepad = nBytes % 2; + + /* Fill the Sun header struct and write the header to the channel. */ + sh.ras_magic = RAS_MAGIC; + sh.ras_width = blockPtr->width; + sh.ras_height = blockPtr->height; + sh.ras_depth = 8 * nchan; + sh.ras_length = (nBytes + linepad) * blockPtr->height; + sh.ras_type = (compr) ? RAS_TYPE_RLE : RAS_TYPE_STD; + sh.ras_maptype = 0; /* No colourmap */ + sh.ras_maplength = 0; /* Length of colourmap */ + + write_sun_header (handle, &sh); + + /* Now write out the image data. */ + pixRowPtr = blockPtr->pixelPtr + blockPtr->offset[0]; + if (!compr) { + row = (UByte *) ckalloc (nBytes); + if (!row) { + sprintf(errMsg, "Can't allocate memory of size %d", nBytes); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + return TCL_ERROR; + } + for (y=0; y<blockPtr->height; y++) { + rowPtr = row; + pixelPtr = pixRowPtr; + for (x=0; x<blockPtr->width; x++) { + if (nchan == 4) { + /* Have a matte channel and write it. */ + *(rowPtr++) = pixelPtr[alphaOffset]; + } + *(rowPtr++) = pixelPtr[blueOffset]; + *(rowPtr++) = pixelPtr[greenOffset]; + *(rowPtr++) = pixelPtr[redOffset]; + pixelPtr += blockPtr->pixelSize; + } + if (nBytes != tkimg_Write(handle, (const char *)row, nBytes)) { + sprintf(errMsg, "Can't write %d bytes to image file", nBytes); + Tcl_AppendResult(interp, errMsg, (char *)NULL); + ckfree ((char *)row); + return TCL_ERROR; + } + for (x=0; x<linepad; x++) { + writeUByte (handle, 0); + } + pixRowPtr += blockPtr->pitch; + } + ckfree ((char *)row); + } else { /* RLE compression */ + rle_startwrite (handle); + for (y = 0; y < blockPtr->height; y++) { + pixelPtr = pixRowPtr; + for (x = 0; x < blockPtr->width; x++) { + if (nchan == 4) { + /* Have a matte channel and write it. */ + rle_fputc (pixelPtr[alphaOffset], handle); + } + rle_fputc (pixelPtr[blueOffset], handle); + rle_fputc (pixelPtr[greenOffset], handle); + rle_fputc (pixelPtr[redOffset], handle); + pixelPtr += blockPtr->pixelSize; + } + for (x=0; x<linepad; x++) { + rle_fputc (0, handle); + } + pixRowPtr += blockPtr->pitch; + } + rle_endwrite (handle); + } + if (verbose) + printImgInfo (&sh, filename, "Saving image:"); + return TCL_OK; +} |