/* STARTHEADER * * File : sun.c * * Author : Paul Obermeier (paul@poSoft.de) * * Date : 2001 / 01 / 22 * * Copyright : (C) 2001-2019 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 -verbose " * Write SUN image: "sun -matte -verbose -compression " * * -matte : If set to false, a matte (alpha) channel is ignored * during reading or writing. Default is true. * -verbose : If set to true, additional information about the file * format is printed to stdout. Default is false. * -compression : 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 int Int; /* Signed 32 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; /* Format options structure for use with ParseFormatOpts */ typedef struct { Int compression; Boln verbose; Boln matte; } FMTOPT; /* Forward declarations of static functions. */ 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); 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); static RLEBUF rlebuf; #ifdef DEBUG_LOCAL static Boln readUByte (tkimg_MFile *handle, UByte *b) { char buf[1]; if (1 != tkimg_Read2(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_Read2((h),(char *)(b),1)) #endif static Boln writeUByte (tkimg_MFile *handle, UByte b) { UByte buf[1]; buf[0] = b; if (1 != tkimg_Write2(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_Read2(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_Write2(ofp, (const char *)buf, 4)) { return FALSE; } return TRUE; } /* 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_Read2(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 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; int 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; iras_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; iras_maplength / 3; if (ncols <= 0) return FALSE; if (3*ncols != tkimg_Read2(ifp, (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= 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= 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= srcY) { dest = pixbuf + srcX * nchan; if (type != 3) { if (nchan == 3) { /* GBR Format. Swap to RGB. */ for (x=0; xcompression = 1; opts->verbose = 0; opts->matte = 1; if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) { return TCL_ERROR; } if (objc) { for (i=1; i= objc) { Tcl_AppendResult(interp, "No value for option \"", Tcl_GetStringFromObj (objv[--i], (int *) NULL), "\"", (char *) NULL); return TCL_ERROR; } optionStr = Tcl_GetStringFromObj(objv[i], (int *) NULL); switch(index) { case 0: if (!strncmp (optionStr, "none", strlen ("none"))) { opts->compression = 0; } else if (!strncmp (optionStr, "rle", strlen ("rle"))) { opts->compression = 1; } else { Tcl_AppendResult (interp, "Invalid compression mode \"", optionStr, "\": Must be \"none\" or \"rle\"", (char *) NULL); return TCL_ERROR; } break; case 1: if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) { Tcl_AppendResult (interp, "Invalid verbose mode \"", optionStr, "\": should be 1 or 0, on or off, true or false", (char *) NULL); return TCL_ERROR; } opts->verbose = boolVal; break; case 2: if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) { Tcl_AppendResult (interp, "Invalid matte mode \"", optionStr, "\": should be 1 or 0, on or off, true or false", (char *) NULL); return TCL_ERROR; } opts->matte = boolVal; break; } } } 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; FMTOPT opts; UByte *suncolmap = NULL; char errMsg[200]; if (ParseFormatOpts(interp, format, &opts) != 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 (opts.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_LOCAL { 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; doffset[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 = ((opts.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 = (opts.compression) ? 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 (!opts.compression) { 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; yheight; y++) { rowPtr = row; pixelPtr = pixRowPtr; for (x=0; xwidth; 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_Write2(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; xpitch; } 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; xpitch; } rle_endwrite (handle); } if (opts.verbose) { printImgInfo (&sh, filename, "Saving image:"); } return TCL_OK; }