jedi-outcast/utils/roq2/libim/imrle.c
2013-04-22 15:25:59 +10:00

2979 lines
44 KiB
C

/**
** $Header: /roq/libim/imrle.c 1 11/02/99 4:38p Zaphod $
** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC)
** a division of General Atomics, San Diego, California, USA
**
** Users and possessors of this source code are hereby granted a
** nonexclusive, royalty-free copyright and design patent license to
** use this code in individual software. License is not granted for
** commercial resale, in whole or in part, without prior written
** permission from SDSC. This source is provided "AS IS" without express
** or implied warranty of any kind.
**
** For further information contact:
** E-Mail: info@sds.sdsc.edu
**
** Surface Mail: Information Center
** San Diego Supercomputer Center
** P.O. Box 85608
** San Diego, CA 92138-5608
** (619) 534-5000
**/
#define HEADER " $Header: /roq/libim/imrle.c 1 11/02/99 4:38p Zaphod $"
/**
** FILE
** imrle.c - Utah Raster Toolkit RLE file output
**
** PROJECT
** libim - SDSC image manipulation library
**
** DESCRIPTION
** imrle.c contains read and write routines to handle RLE files for
** the image manipulation library.
**
** PUBLIC CONTENTS
** d =defined constant
** f =function
** m =defined macro
** t =typedef/struct/union
** v =variable
** ? =other
**
** none
**
** PRIVATE CONTENTS
** imRleRead f read a RLE file
** imRleWrite f write a RLE file
**
** IMDumpRun m dump a run of identical pixels
** IMDumpLitRun m dump a run of literal pixels
**
** HISTORY
** $Log: /roq/libim/imrle.c $
*
* 1 11/02/99 4:38p Zaphod
** Revision 1.14 1995/06/29 00:28:04 bduggan
** updated copyright year
**
** Revision 1.13 1995/06/15 21:13:09 bduggan
** ByteOrder to Byte Order
**
** Revision 1.12 1995/04/03 21:36:46 bduggan
** took out #ifdef NEWMAGIC
**
** Revision 1.11 1995/01/10 23:43:21 bduggan
** made read/write routines static
** put in IMMULTI, IMPIPE instead of TRUE/FALSE
**
** Revision 1.10 94/10/03 11:30:55 nadeau
** Updated to ANSI C and C++ compatibility.
** Removed all use of register keyword.
** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char)
** Changed all float arguments to double.
** Added forward declarations.
** Added misc. casts to passify SGI and DEC compilers.
** Changed all macros and defined constants to have names
** starting with IM.
** Rearranged magic number structures for format handlers.
** Made format handler routines static (i.e., local to file).
** Updated comments, adding format descriptions and references.
** Updated indenting on some code.
** Updated copyright message.
**
** Revision 1.9 92/12/03 01:52:20 nadeau
** Corrected info messages.
**
** Revision 1.8 92/11/04 12:07:24 groening
** put ImFIleFormat info and magic number info
** from imfmt.c into this file.
**
** Revision 1.7 92/10/19 14:10:48 groening
** added ImInfo statements
**
** Revision 1.6 92/08/31 17:35:07 vle
** Updated copyright notice.
**
** Revision 1.5 91/10/03 09:20:12 nadeau
** Fixed #includes.
**
** Revision 1.4 91/03/14 13:44:40 nadeau
** Fixed bug in reading comments from RLE files.
**
** Revision 1.3 91/03/08 14:31:52 nadeau
** Complete rewrite. There might be a comment or two left from
** the original. Restructured. Reorganized. Rewrote.
** Optimized some (though it is still very slow).
**
** Revision 1.2 91/01/21 15:40:16 doeringd
** Changes in imRleWrite to use ImMalloc for cmaptr to work-around
** Y/MP ANSI compiler (scc) problem.
**
**/
#include <string.h>
#include <sys/types.h>
#include "unistd.h"
#include "iminternal.h"
/*
** FORMAT
** RLE - Utah Raster Toolkit Run-Length Encoded image file
**
** AKA
**
** FORMAT REFERENCES
** Design of the Utah RLE Format, Spencer W. Thomas, University
** of Utah
**
** CODE CREDITS
** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991.
**
** DESCRIPTION
** RLE files start with a fixed size header giving:
**
** magic number
** image location (offset from (0,0) of bottom left corner)
** image size
** flags
** number of channels per image pixel
** number of bits in a channel
** number of channels per color map entry
** number of color map entries
**
** See the imRleHeaderInfo comments below for more details.
**
** Following the fixed size header is an optional background color
** for flooding the image prior to loading in pixel data.
**
** Next is a variable length color map.
**
** Next are zero or more comments.
**
** Finally comes the image data. Image data contains a variety of
** opcodes telling how to uncompress the data. Opcodes and their
** operands come in one of two flavors: short and long.
**
** short form: byte 0: opcode
** byte 1: operand
**
** long form: byte 0: opcode
** byte 1: unused
** byte 2-3: LBF operand
**
** The long form is used if the operand is a value too large to fit
** in one byte. The long form is flagged by the setting of the IMLONGOP
** bit in the opcode's byte.
**
** The image opcodes control three state variables:
** x = current X position on scanline
** y = current Y position in image
** channel = current channel
**
** x varies from 0 (left) to the image size - 1 (right).
**
** y varies from 0 (bottom) to the image size - 1 (top). Note that
** this is the opposite of VFB's where line 0 is the top line, not
** the bottom.
**
** IMOPSetColor
** Select which channel of the image all following data is to
** be put into. operand is the channel number. By convention:
** 0 = color index, or red
** 1 = green
** 2 = blue
** 255 = alpha
** This also sets the current 'x' position in the image to 0.
**
** IMOPSkipLines
** The current 'y' position is incremented by the operand.
** This also sets the current 'x' position in the image to 0.
**
** IMOPSkipPixels
** The current 'x' position is incremented by the operand.
**
** IMOPPixelData
** The operand is one less than the count of literal 1-byte
** pixel values that follow. If that count (operand + 1) is
** odd, a single pad byte is added to bring the literal pixel
** run to a 16-bit boundary. After processing the run of literal
** pixel values, the current 'x' position will be (operand+1)
** pixels further to the right.
**
** IMOPRunData
** The operand is one less than the count of image pixels that
** should be filled with the single 1-byte pixel value that
** follows. A single pad byte is added to bring things back to
** a 16-bit boundary. After processing the run of identical pixel
** values, the current 'x' position will be (operand+1) pixels
** further to the right.
**
** IMOPEOF
** The end of the file. An I/O EOF also ends things.
**
** USE OF BACKGROUND COLOR
** The background color scheme of RLE reasons that most images have a
** background color that occurs quite a bit. Rather than represent
** that color as a run, disk space can be saved by using the IMOPSkipPixels
** opcode to skip over those background pixels.
**
** If an incomming image has a background color, the IMRLE_CLEARFIRST bit
** will be set in the header's flags word, and the IMRLE_NOBACKGROUND bit
** not set. The background color to use then follows the header.
**
** One of two approaches may be used in handling background colors:
**
** Initialize the image to the background color then treat
** IMOPSkipPixels as a skip.
**
** Don't initialize the image but treat an IMOPSkipPixels as a
** fill with the background color.
**
** We have chosen to do the first approach.
**
** COLOR MAP STORAGE
** The color map is stored as a series of color channel's (red, green,
** blue). With each of these color channels, the entry value is stored
** starting with entry 0.
**
** Each color map entry value is 16-bits wide. RLE documentation says
** the upper 8-bits should be used on systems that only support 8-bit
** color channels (virtually all current graphics hardware). The lower
** 8-bits should be ignored.
**
** So, a color map is a series of nested structures that can be
** read out with looping like:
**
** for each color channel (header's ncmap field),
** for each entry (header's cmaplen field sorta),
** entry value = short << 8
**
** Note that the header gives the color map length by specifying the
** power of 2 to use. So, for a 256 entry color map, cmaplen would be 8.
**
** RESTRICTIONS
** The RLE format can support image configurations that don't really
** make sense, such as a 5-channel pixel image where each channel has
** a 42-channel color map. Huh?
**
** In the interests of simplicity and to reduce the RLE format to the
** common use case, we only support the following combinations:
**
** 1 channel per pixel
** with or without a 3-channel (RGB) color map
** with or without an alpha plane
** mapped to an IMVFBINDEX8 ( | IMVFBALPHA)
**
** 3 channels per pixel
** with or without a color map
** with or without an alpha plane
** mapped to an IMVFBRGB ( | IMVFBALPHA)
**
** Any other combination is complained about and rejected.
**
** When writing images, we do not set the IMRLE_CLEARFIRST bit in the header
** and do not worry about a background color. To do so would require
** us to scan the VFB and make some sort of guess about what would be
** a good color to use as the background. This is timeconsuming and
** really not all that worthwhile. Consider:
**
** An IMOPSkipPixels opcode/operand takes 2-4 bytes.
**
** An IMOPRunData opcode/operand and pixel value takes 4-6 bytes.
**
** We are talking about a savings of (hold your hats now), 2 bytes per
** use. Sorry, not worth the effort or time of scanning the image first.
**
** When writing color maps, VFB CLT's can be any length, but RLE CLT's
** are constrained to be a power of 2 in length. So, when we have to
** write a color map bigger than the VFB's CLT, we fill in with black.
**/
/*
* RLE - Utah Raster Toolkit run-length encoded image file
* For information on these structures, how to use them, etc. please
* see imfmt.c.
*/
#ifdef __STDC__
static int imRleRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
static int imRleWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
#else
static int imRleRead( );
static int imRleWrite( );
#endif
static char *imRleNames[ ] = { "rle", NULL };
static unsigned char imRleMagicNumber[ ] = { 0xCC, 0x52 };
static ImFileFormatReadMap imRleReadMap[ ] =
{
/* in out */
/* type,ch,dep, attr. VFB type attr. */
{ IN,1,8, RLE, IMVFBINDEX8, 0 },
{ IN,1,8, RLE|C, IMVFBINDEX8, C },
{ IN,1,8, RLE|A, IMVFBINDEX8, A },
{ IN,1,8, RLE|C|A,IMVFBINDEX8, C|A },
{ RGB,3,8, RLE, IMVFBRGB, 0 },
{ RGB,3,8, RLE|A, IMVFBRGB, A },
{ -1, 0, -1, 0 },
};
static ImFileFormatWriteMap imRleWriteMap[ ] =
{
/* in out */
/* VFB type, attr., type,ch,dep, attr., func */
{ IMVFBINDEX8, C, IN,1,8, RLE|C, imRleWrite },
{ IMVFBINDEX8, C|A, IN,1,8, RLE|C|A,imRleWrite },
{ IMVFBINDEX8, A, IN,1,8, RLE|A, imRleWrite },
{ IMVFBINDEX8, 0, IN,1,8, RLE, imRleWrite },
{ IMVFBRGB, 0, RGB,3,8, RLE, imRleWrite },
{ IMVFBRGB, A, RGB,3,8, RLE|A, imRleWrite },
{ -1, 0, -1, 0, NULL },
};
static ImFileMagic imFileRleMagic[]=
{
{ 0, 2, imRleMagicNumber },
{ 0, 0, NULL},
};
ImFileFormat ImFileRleFormat =
{
imRleNames, "Utah Run length encoded image file",
"Utah Raster Toolkit, University of Utah",
"8-bit RLE-compressed color index image files, with or without CLT,\n\
with or without 8-bit Alpha plane. 24-bit RGB RLE-compressed image\n\
files, with or without 8-bit Alpha plane.",
"8-bit RLE-compressed color index image files, with or without CLT,\n\
with or without 8-bit Alpha plane. 24-bit RGB RLE-compressed image\n\
files, with or without 8-bit Alpha plane.",
imFileRleMagic,
IMNOMULTI, IMPIPE,
IMNOMULTI, IMPIPE,
imRleRead, imRleReadMap, imRleWriteMap
};
/*
* TYPEDEF & STRUCT
* imRleHeaderInfo - RLE file header
*
* DESCRIPTION
* RLE files start with a fixed size file header.
*
* rle_magic is the RLE magic number, and is always 0xCC52.
*
* rle_xpos, _ypos, _xsize, and _ysize give the position and size of
* the image. We ignore the position, and use the size to determine
* how big a VFB to allocate.
*
* rle_flags indicates how the background of the VFB should be treated.
*
* rle_ncolors is the number of color channels saved, between 0 and 254.
* A color-index image would be 1, while an RGB image would be 3.
*
* rle_pixelbits is the number of bits per pixel per color channel.
* This must be 8 (original Utah RLE code makes the same restriction).
*
* rle_ncmap is the number of channels in the color map. In most cases
* this will be 3, for R, G, and B.
*
* rle_cmaplen is the log base 2 of the length of the color map for
* each channel. For instance, for a 256 entry CLT, rle_cmaplen would
* be 8.
*
* EXAMPLES
* An 8-bit color index image:
* rle_ncolors = 1 -- just a CLT index
* rle_pixelbits = 8 -- 8-bits wide CLT index
* rle_ncmap = 3 -- RGB for each CLT entry
* rle_cmaplen = 8 -- 256 entries long
*
* A 24-bit RGB image:
* rle_ncolors = 3 -- R, G, and B per pixel
* rle_pixelbits = 8 -- 8-bits for R, for G, and for B
* rle_ncmap = 0 -- No CLT
* rle_cmaplen = 0 -- No CLT
*
* A 24-bit RGB image with a CLT:
* rle_ncolors = 3 -- R, G, and B per pixel
* rle_pixelbits = 8 -- 8-bits for R, for G, and for B
* rle_ncmap = 3 -- RGB for each cLT entry
* rle_cmaplen = 8 -- 256 entries long
*/
typedef struct imRleHeaderInfo
{
unsigned short rle_magic; /* Magic number */
unsigned short rle_xpos; /* X position of image */
unsigned short rle_ypos; /* Y position of image */
unsigned short rle_xsize; /* Width of image */
unsigned short rle_ysize; /* Height of image */
unsigned char rle_flags; /* Flags */
unsigned char rle_ncolors; /* # of color channels saved */
unsigned char rle_pixelbits; /* # of bits in a pixel */
unsigned char rle_ncmap; /* # of color channels in CLT */
unsigned char rle_cmaplen; /* Length of CLT */
} imRleHeaderInfo;
static BinField imRleHeaderFields[] =
{
USHORT, 2, 1, /* rle_magic */
USHORT, 2, 1, /* rle_xpos */
USHORT, 2, 1, /* rle_ypos */
USHORT, 2, 1, /* rle_xsize */
USHORT, 2, 1, /* rle_ysize */
UCHAR, 1, 1, /* rle_flags */
UCHAR, 1, 1, /* rle_ncolors */
UCHAR, 1, 1, /* rle_pixelbits */
UCHAR, 1, 1, /* rle_ncmap */
UCHAR, 1, 1, /* rle_cmaplen */
0, 0, 0
};
/*
* imRleHeaderInfo field values and flags:
*/
#define IMRLEMAGIC (0xCC52) /* RLE magic number */
#define IMRLE_CLEARFIRST ( 0x1 ) /* If set, clear framebuffer */
#define IMRLE_NOBACKGROUND ( 0x2 ) /* If set, no bg color supplied */
#define IMRLE_ALPHA ( 0x4 ) /* If set, alpha channel present*/
#define IMRLE_COMMENT ( 0x8 ) /* If set, comments present */
/*
* Opcodes
*/
#define IMOPSkipLines 1
#define IMOPSetColor 2
#define IMOPSkipPixels 3
#define IMOPPixelData 5
#define IMOPRunData 6
#define IMOPEOF 7
#define IMLONGOP 0x40
/*
* Which channel we are reading into.
*/
#define IMONINDEX8 0
#define IMONRED 1
#define IMONGREEN 2
#define IMONBLUE 3
#define IMONALPHA 4
/*
* FUNCTION
* imRleRead - read an RLE file
*
* DESCRIPTION
* The fixed size header is read in and the magic number checked.
* The header is also checked for reasonable and supported variations
* on number of image and color map channels.
*
* If present, a background color is read in.
*
* If present, a color map is read in.
*
* If present, comments are read in and tossed.
*
* If present, an image is read in and the various opcodes processed.
*/
static int /* Returns status */
#ifdef __STDC__
imRleRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
#else
imRleRead( ioType, fd, fp, flagsTable, tagTable )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file buffer pointer */
TagTable *flagsTable; /* Input parameter/flags */
TagTable *tagTable; /* Tag table to add to */
#endif
{
imRleHeaderInfo header; /* File header */
char msg[80]; /* Error message */
unsigned char bgColor[3]; /* Background color (if any) */
int fields; /* Fields for VFB */
unsigned int len; /* Length of comments */
int i, j; /* Counters */
int x, y; /* Row and column counters */
int cmaplen; /* Length of color map */
unsigned short *cmap; /* Color map buffer */
unsigned short *pRed; /* Pointer into color map buffer*/
unsigned short *pGreen; /* Pointer into color map buffer*/
unsigned short *pBlue; /* Pointer into color map buffer*/
ImVfb *vfb; /* New image */
ImVfbPtr pPixel; /* Pointer to pixel in VFB */
ImClt *clt; /* New color table */
ImCltPtr pColor; /* Pointer to color in CLT */
unsigned char *buffer; /* Generic buffer */
unsigned char *pBuffer; /* Buffer pointer */
unsigned int operand; /* Operand of opcode */
int chan; /* Current VFB channel */
char message[256]; /* buffer for use with ImInfo */
/*
* Read in the fixed size header and check for a good magic number
* and reasonable header values.
*/
BinByteOrder( BINLBF );
if ( ImBinReadStruct( ioType, fd, fp, &header, imRleHeaderFields) == -1)
{
ImReturnBinError();
}
if ( header.rle_magic != IMRLEMAGIC )
{
ImErrNo = IMEMAGIC;
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
if ( header.rle_pixelbits != 8 )
{
ImErrorFatal( "RLE images with other than 8-bit pixel channels are not supported", -1, IMESYNTAX );
}
/* following lines are printouts for Iminfo if imfile
or inconv have the -verbose flags attached */
ImInfo ("Byte Order", "Least Significant Byte First");
sprintf (message, "%d x %d",header.rle_xsize,header.rle_ysize);
ImInfo ("Resolution",message);
if (header.rle_ncolors==1)
sprintf (message, "%d-bit Color Indexed",header.rle_pixelbits);
else if (header.rle_ncolors==3)
sprintf (message, "%d-bit RGB",3*header.rle_pixelbits);
ImInfo ("Type",message);
if (header.rle_ncmap!=0)
{
sprintf (message, "%d Entries", header.rle_cmaplen);
ImInfo ("Color Table", message);
}
ImInfo ("Compression Type","Run Length Encoded (RLE)");
if (header.rle_flags & IMRLE_ALPHA)
sprintf (message, "8-bit");
else sprintf (message, "none");
ImInfo ("Alpha Channel",message);
switch ( header.rle_ncolors )
{
case 0:
/*
* No channels per pixel. This means there is no image in
* the file, but there might be a color map. If so, we
* read that in and we're done.
*/
fields = 0;
switch ( header.rle_ncmap )
{
case 0: /* No color map. Nothing in file! */
return ( 0 );
case 3: /* 3 color channels. RGB CLT. */
break;
default:
sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap );
ImErrorFatal( msg, -1, IMESYNTAX );
}
break;
case 1:
/*
* 1 channel per pixel. It is assumed this is a color index
* image. The color map can be non-existant, or have 3
* color channels (RGB). Anything else is weird.
*/
fields = IMVFBINDEX8;
switch ( header.rle_ncmap )
{
case 0: /* No color map. Grayscale image. */
break;
case 3: /* 3 color channels. RGB CLT. */
break;
default:
sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap );
ImErrorFatal( msg, -1, IMESYNTAX );
}
break;
case 3:
/*
* 3 channels per pixel. It is assumed this is an RGB image.
* The color map must be non-existant. Other combinations
* are weird.
*/
fields = IMVFBRGB;
switch ( header.rle_ncmap )
{
case 0: /* No color map. Normal RGB image. */
break;
case 3: /* 3 color channels. RGB CLT. */
break;
default:
sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap );
ImErrorFatal( msg, -1, IMESYNTAX );
}
break;
default:
/*
* Strange number of channels. Cannot support.
*/
sprintf( msg, "RLE images with %d channels are not supported",
header.rle_ncolors );
ImErrorFatal( msg, -1, IMESYNTAX );
}
/*
* At this point we've narrowed things down to:
* 8-bit color index or 24-bit RGB image
* Optional RGB CLT
*/
/*
* Allocate a VFB if there is an image or alpha plane in the file.
*/
if ( header.rle_flags & IMRLE_ALPHA )
fields |= IMVFBALPHA;
vfb = IMVFBNULL;
if ( fields != 0 )
{
vfb = ImVfbAlloc( header.rle_xsize, header.rle_ysize, fields);
if ( vfb == IMVFBNULL )
{
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
}
/*
* Read in the variable length background color following the
* header. There will be one 8-bit value for each channel in the
* image (rle_ncolors). We've already restricted our RLE handling to:
*
* rle_ncolors = 0 no image, so no background color
* rle_ncolors = 1 index image, 1 8-bit value
* rle_ncolors = 3 RGB image, 3 8-bit values
*
* For the rle_ncolors = 0 case, a single pad byte is present.
*
* Clear the VFB to the given background color, if the IMRLE_CLEARFIRST
* flag is set in the header.
*
* The background color for the alpha plane, if present, is always 0.
*/
switch ( header.rle_ncolors )
{
case 0:
if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
break; /* bgColor is pad byte */
case 1:
if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 )
{
ImVfbFree( vfb );
ImReturnBinError( );
}
if ( header.rle_flags & IMRLE_NOBACKGROUND )
break; /* bgColor is pad byte */
if ( !(header.rle_flags & IMRLE_CLEARFIRST) )
break; /* Nothing to do */
if ( fields & IMVFBALPHA )
{
if ( bgColor[0] == 0 )
ImVfbClear( IMVFBALL, bgColor[0], vfb );
else
{
ImVfbClear( IMVFBINDEX8, bgColor[0], vfb );
ImVfbClear( IMVFBALPHA, 0, vfb );
}
}
else
ImVfbClear( IMVFBALL, bgColor[0], vfb );
break;
case 3:
if ( header.rle_flags & IMRLE_NOBACKGROUND )
{
if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 )
{
ImVfbFree( vfb );
ImReturnBinError( );
}
break; /* bgColor is pad byte */
}
if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 3 ) == -1 )
{
ImVfbFree( vfb );
ImReturnBinError( );
}
if ( !(header.rle_flags & IMRLE_CLEARFIRST) )
break; /* Nothing to do */
if ( bgColor[0] == bgColor[1] && bgColor[0] == bgColor[2] )
{
if ( fields & IMVFBALPHA )
{
if ( bgColor[0] == 0 )
ImVfbClear( IMVFBALL, bgColor[0], vfb );
else
{
ImVfbClear( IMVFBRGB, bgColor[0], vfb );
ImVfbClear( IMVFBALPHA, 0, vfb );
}
}
else
ImVfbClear( IMVFBALL, bgColor[0], vfb );
break;
}
if ( fields & IMVFBALPHA )
ImVfbClear( IMVFBALPHA, 0, vfb );
pPixel = ImVfbQFirst( vfb );
for ( y = 0; y < header.rle_ysize; y++ )
{
for ( x = 0; x < header.rle_xsize; x++ )
{
ImVfbSRed( vfb, pPixel, bgColor[0] );
ImVfbSGreen( vfb, pPixel, bgColor[1] );
ImVfbSBlue( vfb, pPixel, bgColor[2] );
ImVfbSInc( vfb, pPixel );
}
}
break;
}
/*
* Read in the variable length color map. We constrain things to:
*
* rle_ncmap = 0 no color map
* rle_ncmap = 3 RGB color map
*/
clt = IMCLTNULL;
if ( header.rle_ncmap == 3 )
{
/*
* Color map entries are 16-bit values. When mapping to
* display hardware (or a VFB's CLT), the upper bits are
* the most useful. Since we limit CLT color channel
* values to 8-bits each, we shift each color map entry
* to the right by 8 bits, and use what's left.
*
* Color map values are stored in the order:
* RRR...GGG...BBB...
*/
cmaplen = 1 << header.rle_cmaplen;
ImMalloc( cmap, unsigned short *, sizeof( ushort ) * cmaplen * 3 );
if ( ImBinRead( ioType, fd, fp, cmap, USHORT, 2, cmaplen * 3 )== -1 )
{
free( (char *)cmap );
if ( vfb != IMVFBNULL )
ImVfbFree( vfb );
ImReturnBinError( );
}
if ( (clt = ImCltAlloc( cmaplen )) == IMCLTNULL )
{
free( (char *)cmap );
if ( vfb != IMVFBNULL )
ImVfbFree( vfb );
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
pColor = ImCltQFirst( clt );
pRed = cmap;
pGreen = pRed + cmaplen;
pBlue = pGreen + cmaplen;
for ( i = 0; i < cmaplen; i++ )
{
ImCltSRed( pColor, ((*pRed++ >> 8)) & 0xFF );
ImCltSGreen( pColor, ((*pGreen++ >> 8)) & 0xFF );
ImCltSBlue( pColor, ((*pBlue++ >> 8)) & 0xFF );
ImCltSInc( clt, pColor );
}
free( (char *) cmap );
ImVfbSClt( vfb, clt );
TagTableAppend( tagTable,
TagEntryAlloc( "image clt", POINTER, &clt ) );
}
/*
* Read in and ignore comments, if present.
*/
if ( header.rle_flags & IMRLE_COMMENT )
{
/*
* Comments are preceded by a 2-byte value giving the
* length, in bytes, of the comments. If this length
* is odd, an extra pad byte is added.
*
* We don't handle these comments, so we just toss them.
*/
if ( ImBinRead( ioType, fd, fp, &len, UINT, 2, 1 )== -1 )
{
if ( vfb != IMVFBNULL )
ImVfbFree( vfb );
ImReturnBinError( );
}
len = (len + 1) & ~0x1; /* Round to 16-bits */
/*
* If reading from a file, just seek past the comments.
* Otherwise we have to read them in and toss them.
*/
if ( ioType & IMFILEIOFILE )
ImSeek( ioType, fd, fp, len, 1 );
else
{
ImMalloc( buffer, unsigned char *, len );
if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, len )== -1 )
{
free( (char *)buffer );
if ( vfb != IMVFBNULL )
ImVfbFree( vfb );
ImReturnBinError( );
}
free( (char *)buffer );
}
}
/*
* If there is no image, stop.
*/
if ( header.rle_ncolors == 0 && !(header.rle_flags & IMRLE_ALPHA) )
return ( (clt == IMCLTNULL) ? 0 : 1 );
/*
* Read in the image. Each two byte opcode/operand pair is read
* in and checked to see if the opcode has its "long form" bit set.
* If so, the byte paired with the opcode is ignored and another
* two bytes read in and treated as a 16-bit operand.
*
* The opcode is then processed. IMOPSkipLines and IMOPSkipPixels both
* skip over pixels, setting nothing. IMOPSetColor switches to a
* different channel in the image. IMOPPixelData has literal pixel
* values for the current channel, while IMOPRunData has a run of a
* single pixel value for the current channel.
*
* Encountering file EOF, or the IMOPEOF opcode stops the image read.
*
* NOTE: VFB's have (0,0) in the upper-left. RLE images have it
* in the lower left. So, we walk from the bottom of the image, up,
* as we read it in.
*/
i = (header.rle_xsize + 1) & ~0x1;
ImMalloc( buffer, unsigned char *, i * sizeof( unsigned char ) );
x = 0;
y = header.rle_ysize - 1;
switch ( header.rle_ncolors )
{
case 0: chan = IMONALPHA; break;
case 1: chan = IMONINDEX8; break;
case 3: chan = IMONRED; break;
}
for ( ; ; )
{
switch ( (j = ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, 2 )))
{
case -1: /* Error */
free( (char *)buffer );
ImVfbFree( vfb );
ImReturnBinError( );
case 0: /* EOF */
free( (char *)buffer );
TagTableAppend( tagTable,
TagEntryAlloc( "image vfb", POINTER, &vfb ) );
return ( 1 );
}
operand = buffer[1];
if ( buffer[0] & IMLONGOP )
{
if ( ImBinRead( ioType, fd, fp, &operand, UINT, 2, 1 ) == -1 )
{
free( (char *)buffer );
ImVfbFree( vfb );
ImReturnBinError( );
}
}
switch ( buffer[0] )
{
case IMOPSkipLines | IMLONGOP:
case IMOPSkipLines:
/* Skip to the start of the next line. */
y -= operand;
x = 0;
continue;
case IMOPSetColor:
/* Start on a different channel. */
x = 0;
switch ( operand )
{
case 0: chan = (header.rle_ncolors==1)?IMONINDEX8:IMONRED;
break;
case 1: chan = IMONGREEN; break;
case 2: chan = IMONBLUE; break;
case 255:chan = IMONALPHA; break;
default:
free( (char *)buffer );
ImVfbFree( vfb );
sprintf( msg, "RLE image data selects channel %d, which doesn't exist!", operand );
ImErrorFatal( msg, -1, IMESYNTAX );
}
continue;
case IMOPSkipPixels | IMLONGOP:
case IMOPSkipPixels:
/* Skip forward on the scanline. */
x += operand;
continue;
case IMOPPixelData | IMLONGOP:
case IMOPPixelData:
/* Read in literal pixel values. */
operand++;
i = (operand + 1) & ~0x1;
if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, i ) == -1 )
{
free( (char *)buffer );
ImVfbFree( vfb );
ImReturnBinError( );
}
pBuffer = buffer;
pPixel = ImVfbQPtr( vfb, x, y );
x += operand;
switch ( chan )
{
case IMONINDEX8:
for ( i = 0; i < operand; i++ )
{
ImVfbSIndex8( vfb, pPixel, *pBuffer++ );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONRED:
for ( i = 0; i < operand; i++ )
{
ImVfbSRed( vfb, pPixel, *pBuffer++ );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONGREEN:
for ( i = 0; i < operand; i++ )
{
ImVfbSGreen( vfb, pPixel, *pBuffer++ );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONBLUE:
for ( i = 0; i < operand; i++ )
{
ImVfbSBlue( vfb, pPixel, *pBuffer++ );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONALPHA:
for ( i = 0; i < operand; i++ )
{
ImVfbSAlpha( vfb, pPixel, *pBuffer++ );
ImVfbSInc( vfb, pPixel );
}
break;
}
continue;
case IMOPRunData | IMLONGOP:
case IMOPRunData:
/* Read in and process a run of the same pixel value*/
if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, 2 ) == -1 )
{
free( (char *)buffer );
ImVfbFree( vfb );
ImReturnBinError( );
}
operand++;
pPixel = ImVfbQPtr( vfb, x, y );
x += operand;
switch ( chan )
{
case IMONINDEX8:
for ( i = 0; i < operand; i++ )
{
ImVfbSIndex8( vfb, pPixel, *buffer );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONRED:
for ( i = 0; i < operand; i++ )
{
ImVfbSRed( vfb, pPixel, *buffer );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONGREEN:
for ( i = 0; i < operand; i++ )
{
ImVfbSGreen( vfb, pPixel, *buffer );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONBLUE:
for ( i = 0; i < operand; i++ )
{
ImVfbSBlue( vfb, pPixel, *buffer );
ImVfbSInc( vfb, pPixel );
}
break;
case IMONALPHA:
for ( i = 0; i < operand; i++ )
{
ImVfbSAlpha( vfb, pPixel, *buffer );
ImVfbSInc( vfb, pPixel );
}
break;
}
continue;
case IMOPEOF:
free( (char *)buffer );
TagTableAppend( tagTable,
TagEntryAlloc( "image vfb", POINTER, &vfb ) );
return ( 1 );
default:
free( (char *)buffer );
ImVfbFree( vfb );
sprintf( msg, "Unknown RLE image opcode: 0x%02x",
buffer[0] );
ImErrorFatal( msg, -1, IMESYNTAX );
}
}
}
/*
* MACROS
* IMDumpRun - dump a run of identical pixels
* IMDumpLitRun - dump a run of literal pixels
*
* DESCRIPTIONS
* Both of these macros assume a variety of local variables. These
* algorithms are encapsulated in macros in order to simplify the
* write routines below. They are *not* made into subroutines to
* avoid the extra expense of subroutine calls, argument passing, and
* global variable referencing that that would entail.
*
* IMDumpRun outputs a RunData opcode in either short or long form,
* followed by a single byte of pixel value for the pixel value to run
* across several image pixels. A pad byte is added to bring the
* opcode and run operand to a 16-bit boundary.
*
* IMDumpLitRun outputs a PixelData opcode in either short or long form,
* followed by a string of literal pixel values to write into adjacent
* image pixels. If the number of pixel values is odd, a pad byte is
* added to bring the opcode and literal run to a 16-bit boundary.
*/
#define IMDumpRun() \
{ \
/* \
* Write opcode (possibly in long form). \
* Operand is runtCount - 1. \
*/ \
if ( --runCount <= 255 ) \
{ \
command[0] = IMOPRunData; \
command[1] = runCount; \
if ( ImBinWrite( ioType, fd, fp, command, \
UCHAR, 1, 2 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
} \
else \
{ \
command[0] = IMOPRunData | IMLONGOP; \
command[1] = 0; \
if ( ImBinWrite( ioType, fd, fp, command, \
UCHAR, 1, 2 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
if ( ImBinWrite( ioType, fd, fp, &runCount, \
UINT, 2, 1 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
} \
\
/* \
* Output single run byte, plus pad byte to round to \
* next highest 16-bit boundary. \
*/ \
buffer[1] = 0; /* Pad */ \
if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, 2 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
}
#define IMDumpLitRun() \
{ \
/* \
* Write opcode (possibly in long form). \
* Operand is litCount - 1. \
*/ \
if ( --litCount <= 255 ) \
{ \
command[0] = IMOPPixelData; \
command[1] = litCount; \
if ( ImBinWrite( ioType, fd, fp, \
command, UCHAR, 1, 2 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
} \
else \
{ \
command[0] = IMOPPixelData | IMLONGOP; \
command[1] = 0; \
if ( ImBinWrite( ioType, fd, fp, \
command, UCHAR, 1, 2 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
if ( ImBinWrite( ioType, fd, fp, &litCount, \
UINT, 2, 1 ) == -1 ) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
} \
\
/* \
* Output buffer built up so far. Pad to \
* next highest 16-bit boundary. \
*/ \
if ( ImBinWrite( ioType, fd, fp, buffer, \
UCHAR, 1, (litCount + 2) & ~0x1 ) == -1) \
{ \
free( (char *)buffer ); \
ImReturnBinError(); \
} \
}
/*
* FUNCTION
* imRleWrite - write an RLE file
*
* DESCRIPTION
* This same routine handles INDEX8 and RGB VFB's, with or without
* alpha channels, and with or without CLT's.
*
* The file header is set up and written out.
*
* No background color.
*
* A color map is constructed and written out, if necessary.
*
* No comments.
*
* The image is processed one scanline at a time, run-length encoding
* each channel and writing it out.
*/
static int
#ifdef __STDC__
imRleWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
#else
imRleWrite( pMap, ioType, fd, fp, flagsTable, tagTable )
ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */
int ioType; /* I/O flags */
int fd; /* Output file descriptor */
FILE *fp; /* Output file buffer pointer */
TagTable *flagsTable; /* Output parameter/flags */
TagTable *tagTable; /* Tag table to read from */
#endif
{
imRleHeaderInfo header; /* File header */
int i, j, k; /* Counters */
int x, y; /* Row and column counters */
ImVfb *vfb; /* Image to output */
ImVfbPtr pPixel; /* VFB pixel pointer */
int fields; /* VFB fields */
unsigned char *buffer; /* Pixel buffer */
unsigned char *pBuffer; /* Buffer pointer */
unsigned char command[4]; /* Command buffer */
ImClt *clt; /* CLT to output */
ImCltPtr pColor; /* CLT color pointer */
int cltLen; /* Length of CLT */
int cltPadLen; /* Length of Pad entries for CLT*/
unsigned short *cmap; /* Color map buffer */
unsigned short *pRed; /* Pointer into color map buffer*/
unsigned short *pGreen; /* Pointer into color map buffer*/
unsigned short *pBlue; /* Pointer into color map buffer*/
int index; /* Current color index */
int oldindex; /* Old color index */
unsigned int runCount; /* Run count */
ImVfbPtr pLitPixel; /* Literal pixel pointer */
unsigned int litCount; /* Literal pixel count */
char message[256]; /* buffer to be printed in ImInfo */
/*
* Set up and write out the file header.
*/
TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb );
fields = ImVfbQFields( vfb );
clt = ImVfbQClt( vfb );
header.rle_magic = IMRLEMAGIC;
header.rle_xpos = 0;
header.rle_ypos = 0;
header.rle_xsize = ImVfbQWidth( vfb );
header.rle_ysize = ImVfbQHeight( vfb );
header.rle_flags = IMRLE_NOBACKGROUND;
if ( (fields & IMVFBALPHA) && (pMap->map_outAttributes & IMALPHAYES) )
header.rle_flags |= IMRLE_ALPHA;
header.rle_pixelbits = 8;
if ( fields & IMVFBINDEX8 )
{
header.rle_ncolors = 1;
if ( (clt != IMCLTNULL) && (pMap->map_outAttributes & IMCLTYES))
{
/*
* Compute the next higher power of 2 to use for
* the number of CLT entries.
*/
cltLen = ImCltQNColors( clt );
for ( j = 0, i = cltLen; i > 1; j++ )
i >>= 1;
if ( (1 << j) < cltLen )
j++;
cltPadLen = (1 << j) - cltLen;
header.rle_cmaplen = j;
header.rle_ncmap = 3;
}
else
{
/*
* Don't output a color map.
*/
header.rle_cmaplen = 0;
header.rle_ncmap = 0;
}
}
else
{
header.rle_ncolors = 3;
header.rle_cmaplen = 0;
header.rle_ncmap = 0;
}
BinByteOrder( BINLBF );
if ( ImBinWriteStruct( ioType, fd, fp, &header, imRleHeaderFields)== -1)
{
ImReturnBinError();
}
/* following lines are printouts for Iminfo if imfile
or inconv have the -verbose flags attached */
ImInfo ("Byte Order", "Least Significant Byte First");
sprintf (message, "%d x %d",header.rle_xsize,header.rle_ysize);
ImInfo ("Resolution",message);
if (header.rle_ncolors==1)
sprintf (message, "%d-bit Color Indexed",header.rle_pixelbits);
else if (header.rle_ncolors==3)
sprintf (message, "%d-bit RGB",3*header.rle_pixelbits);
ImInfo ("Type",message);
if (header.rle_ncmap!=0)
{
sprintf (message, "%d Entries", header.rle_cmaplen);
ImInfo ("Color Table", message);
}
ImInfo ("Compression Type","Run Length Encoded (RLE)");
if (header.rle_flags & IMRLE_ALPHA)
sprintf (message, "8-bit");
else sprintf (message, "none");
ImInfo ("Alpha Channel",message);
/*
* No need for a background color because we set the IMRLE_NOBACKGROUND
* bit in the header flags word. Just output a pad byte to bring
* the header to a 16-bit boundary.
*/
i = 0;
if ( ImBinWrite( ioType, fd, fp, &i, INT, 1, 1 ) == -1 )
{
ImReturnBinError();
}
/*
* Write out the CLT. The red, then green, then blue channels, in
* their entirity, are written out one at a time. Each channel
* value is shifted into the high byte of a 16-bit word. The lower
* byte is set to zeroes.
*
* Since the RLE header requires that CLT's be a power of 2 in length,
* but VFB's don't, we might have a CLT that is shorter than a
* power of 2. In such cases a batch of "pad" black CLT entries are
* output.
*/
if ( header.rle_ncmap != 0 )
{
k = cltLen + cltPadLen;
ImMalloc( cmap, unsigned short *, sizeof( ushort ) * 3 * k );
pColor = ImCltQFirst( clt );
pRed = cmap;
pGreen = pRed + k;
pBlue = pGreen + k;
for ( i = 0; i < cltLen; i++ )
{
*pRed++ = (ImCltQRed( pColor ) << 8);
*pGreen++ = (ImCltQGreen( pColor ) << 8);
*pBlue++ = (ImCltQBlue( pColor ) << 8);
ImCltSInc( clt, pColor );
}
for ( i = 0; i < cltPadLen; i++ )
{
*pRed++ = 0;
*pGreen++ = 0;
*pBlue++ = 0;
}
if ( ImBinWrite( ioType, fd, fp, cmap, USHORT, 2, 3 * k ) == -1)
{
free( (char *)cmap );
ImReturnBinError();
}
free( (char *)cmap );
}
/*
* We left the IMRLE_COMMENT flag off in the header, so there are no
* comments in the file.
*/
/*
* Output the image. Process pixels from the bottom of the image, up.
*/
ImMalloc( buffer, unsigned char *, (header.rle_xsize + 1) & ~1 );
/* Process each scanline. */
for ( y = header.rle_ysize - 1; y >= 0; y-- )
{
if ( fields & IMVFBRGB )
k = 3;
else
k = 1;
if ( header.rle_flags & IMRLE_ALPHA )
k++;
for ( j = 0; j < k; j++ )
{
if ( j == 1 && (fields & IMVFBINDEX8) )
j = 3;
/* Select channel. */
command[0] = IMOPSetColor;
if ( j == 3 )
command[1] = 255; /* Alpha */
else
command[1] = j;
if ( ImBinWrite( ioType, fd, fp, command,
UCHAR, 1, 2 ) == -1 )
{
ImReturnBinError();
}
pPixel = ImVfbQPtr( vfb, 0, y );
runCount = 0;
litCount = 1;
switch ( j )
{
case 0: if ( fields & IMVFBINDEX8 )
oldindex = ImVfbQIndex8( vfb, pPixel );
else
oldindex = ImVfbQRed( vfb, pPixel );
break;
case 1: oldindex = ImVfbQGreen( vfb, pPixel ); break;
case 2: oldindex = ImVfbQBlue( vfb, pPixel ); break;
case 3: oldindex = ImVfbQAlpha( vfb, pPixel ); break;
}
pBuffer = buffer;
*pBuffer++ = oldindex;
ImVfbSInc( vfb, pPixel );
for ( x = 1; x < header.rle_xsize; x++ )
{
switch ( j )
{
case 0: if ( fields & IMVFBINDEX8 )
index=ImVfbQIndex8( vfb,pPixel);
else
index=ImVfbQRed( vfb, pPixel );
break;
case 1: index=ImVfbQGreen( vfb, pPixel ); break;
case 2: index=ImVfbQBlue( vfb, pPixel ); break;
case 3: index=ImVfbQAlpha( vfb, pPixel ); break;
}
ImVfbSInc( vfb, pPixel );
if ( index == oldindex )
{
if ( ++runCount != 1 )
continue; /* Cont run*/
/*
* Start of run. Dump previous batch
* of literal pixels, if any.
*/
runCount++;
if ( --litCount == 0 )
continue; /* No lit*/
IMDumpLitRun( );
pBuffer = buffer;
*pBuffer++ = index;
litCount = 0;
continue;
}
oldindex = index;
if ( ++litCount != 1 )
{
*pBuffer++ = index;
continue; /* Cont literal run*/
}
/*
* Start of literal run. Dump previous batch
* of run pixels.
*/
IMDumpRun( );
pBuffer = buffer;
*pBuffer++ = index;
runCount = 0;
}
if ( litCount != 0 )
{
IMDumpLitRun( );
}
else if ( runCount != 0 )
IMDumpRun( );
}
command[0] = IMOPSkipLines;
command[1] = 1;
if ( ImBinWrite( ioType, fd, fp, command, UCHAR, 1, 2 ) == -1 )
{
ImReturnBinError();
}
}
free( (char *)buffer );
command[0] = IMOPEOF;
command[1] = 0;
if ( ImBinWrite( ioType, fd, fp, command, UCHAR, 1, 2 ) == -1 )
{
ImReturnBinError();
}
return ( 1 );
}