/* * Utah RLE Toolkit library routines. * * Read image support only. * * Cobbled from Utah RLE include and library source files. * * By Graeme Gill * 30/5/90 * */ #include #include #if 0 #include #endif #include #include "image.h" /* need ZFILE definition */ #include "rle.h" /* SUPPRESS 530y */ /* SUPPRESS 558 */ /* SUPPRESS 590 */ #define zeof(zfp) feof((zfp)->stream) #define zclearerr(zfp) clearerr((zfp)->stream) /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. */ /* * Runsv.h - Definitions for Run Length Encoding. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Mon Aug 9 1982 * Copyright (c) 1982 Spencer W. Thomas */ #ifndef XTNDRUNSV #define XTNDRUNSV /* * Opcode definitions */ #define LONG 0x40 #define RSkipLinesOp 1 #define RSetColorOp 2 #define RSkipPixelsOp 3 #define RByteDataOp 5 #define RRunDataOp 6 #define REOFOp 7 #define H_CLEARFIRST 0x1 /* clear framebuffer flag */ #define H_NO_BACKGROUND 0x2 /* if set, no bg color supplied */ #define H_ALPHA 0x4 /* if set, alpha channel (-1) present */ #define H_COMMENT 0x8 /* if set, comments present */ struct XtndRsetup { short h_xpos, h_ypos, h_xlen, h_ylen; char h_flags, h_ncolors, h_pixelbits, h_ncmap, h_cmaplen; }; #define SETUPSIZE ((4*2)+5) /* "Old" RLE format magic numbers */ #define RMAGIC ('R' << 8) /* top half of magic number */ #define WMAGIC ('W' << 8) /* black&white rle image */ #define XtndRMAGIC ((short)0xcc52) /* RLE file magic number */ #endif /* XTNDRUNSV */ /* "svfb.h" */ /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. */ /* * svfb.h - Definitions and a few global variables for svfb. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Mon Aug 9 1982 * Copyright (c) 1982 Spencer W. Thomas */ /* **************************************************************** * Dispatch table for different output types. */ typedef void sv_fn(); struct sv_dispatch_tab { char *magic; /* magic type flags */ sv_fn *setup, /* startup function */ *skipBlankLines, *setColor, *skipPixels, *newScanLine, *putdat, /* put a set of differing pixels */ *putrn, /* put a run all the same */ *blockHook, /* hook called at start of new */ /* output block */ *putEof; /* write EOF marker (if possible) */ }; struct sv_dispatch_tab sv_DTable[]; /* * These definitions presume the existence of a variable called * "fileptr", declared "long * fileptr". *fileptr should be * initialized to 0 before calling Setup(). * A pointer "globals" declared "struct sv_globals * globals" is also * presumed to exist. */ #define sv_magic (sv_DTable[(int)globals->sv_dispatch].magic) #define Setup() (*sv_DTable[(int)globals->sv_dispatch].setup)(globals) #define SkipBlankLines(n) (*sv_DTable[(int)globals->sv_dispatch].skipBlankLines)(n, globals) #define SetColor(c) (*sv_DTable[(int)globals->sv_dispatch].setColor)(c, globals) #define SkipPixels(n, l, r) (*sv_DTable[(int)globals->sv_dispatch].skipPixels)(n,l,r, globals) #define NewScanLine(flag) (*sv_DTable[(int)globals->sv_dispatch].newScanLine)(flag, globals) #define putdata(buf, len) (*sv_DTable[(int)globals->sv_dispatch].putdat)(buf, len, globals) #define putrun(val, len, f) (*sv_DTable[(int)globals->sv_dispatch].putrn)(val,len,f, globals) #define BlockHook() (*sv_DTable[(int)globals->sv_dispatch].blockHook)(globals) #define PutEof() (*sv_DTable[(int)globals->sv_dispatch].putEof)(globals) /* * States for run detection */ #define DATA 0 #define RUN2 1 #define RUN3 2 #define RUN4 3 #define INRUN -1 /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. * * Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire * to have all "void" functions so declared. */ /* * svfb_global.c - Global variable initialization for svfb routines. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Thu Apr 25 1985 * Copyright (c) 1985,1986 Spencer W. Thomas */ void RunSetup(), RunSkipBlankLines(), RunSetColor(), RunSkipPixels(), RunNewScanLine(), Runputdata(), Runputrun(), RunputEof(); void DefaultBlockHook(); void NullputEof(); struct sv_dispatch_tab sv_DTable[] = { { " OB", RunSetup, RunSkipBlankLines, RunSetColor, RunSkipPixels, RunNewScanLine, Runputdata, Runputrun, DefaultBlockHook, RunputEof }, }; static int sv_bg_color[3] = { 0, 0, 0 }; struct sv_globals sv_globals = { RUN_DISPATCH, /* dispatch value */ 3, /* 3 colors */ sv_bg_color, /* background color */ 0, /* (alpha) if 1, save alpha channel */ 2, /* (background) 0->just save pixels, */ /* 1->overlay, 2->clear to bg first */ 0, 511, /* (xmin, xmax) X bounds to save */ 0, 479, /* (ymin, ymax) Y bounds to save */ 0, /* ncmap (if != 0, save color map) */ 8, /* cmaplen (log2 of length of color map) */ NULL, /* pointer to color map */ NULL, /* pointer to comment strings */ NULL, /* output file */ { 7 } /* RGB channels only */ /* Can't initialize the union */ }; /* ARGSUSED */ void NullputEof(globals) struct sv_globals * globals; { /* do nothing */ } /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. * * Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire * to have all "void" functions so declared. */ /* * Runsv.c - General purpose Run Length Encoding for svfb. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Mon Aug 9 1982 * Copyright (c) 1982,1986 Spencer W. Thomas */ /* THIS IS WAY OUT OF DATE. See rle.5. * The output file format is: * * Word 0: A "magic" number. The top byte of the word contains * the letter 'R' or the letter 'W'. 'W' indicates that * only black and white information was saved. The bottom * byte is one of the following: * ' ': Means a straight "box" save, -S flag was given. * 'B': Image saved with background color, clear screen to * background before restoring image. * 'O': Image saved in overlay mode. * * Words 1-6: The structure * { short xpos, * Lower left corner * ypos, * xsize, * Size of saved box * ysize; * char rgb[3]; * Background color * char map; * flag for map presence * } * * If the map flag is non-zero, then the color map will follow as * 3*256 16 bit words, first the red map, then the green map, and * finally the blue map. * * Following the setup information is the Run Length Encoded image. * Each instruction consists of a 4-bit opcode, a 12-bit datum and * possibly one or more following words (all words are 16 bits). The * instruction opcodes are: * * SkipLines (1): The bottom 10 bits are an unsigned number to be added to * current Y position. * * SetColor (2): The datum indicates which color is to be loaded with * the data described by the following ByteData and * RunData instructions. 0->red, 1->green, 2->blue. The * operation also resets the X position to the initial * X (i.e. a carriage return operation is performed). * * SkipPixels (3): The bottom 10 bits are an unsigned number to be * added to the current X position. * * ByteData (5): The datum is one less than the number of bytes of * color data following. If the number of bytes is * odd, a filler byte will be appended to the end of * the byte string to make an integral number of 16-bit * words. The bytes are in PDP-11 order. The X * position is incremented to follow the last byte of * data. * * RunData (6): The datum is one less than the run length. The * following word contains (in its lower 8 bits) the * color of the run. The X position is incremented to * follow the last byte in the run. */ #define UPPER 255 /* anything bigger ain't a byte */ /* Predefine LITTLE_ENDIAN for vax and pdp11 machines */ #if defined(vax) || defined(pdp11) #define LITTLE_ENDIAN #endif /* * Macros to make writing instructions with correct byte order easier. */ union { short s; char c[2]; } arg; #ifdef LITTLE_ENDIAN #define put16(a) arg.s=a,putc(arg.c[0],sv_fd), putc(arg.c[1],sv_fd) #else #define put16(a) arg.s=a,putc(arg.c[1],sv_fd), putc(arg.c[0],sv_fd) #endif /* short instructions */ #define mk_short_1(oper,a1) /* one argument short */ \ putc(oper,sv_fd), putc((char)a1,sv_fd) #define mk_short_2(oper,a1,a2) /* two argument short */ \ putc(oper,sv_fd), putc((char)a1,sv_fd), put16(a2) /* long instructions */ #define mk_long_1(oper,a1) /* one argument long */ \ putc((char)(LONG|oper),sv_fd), putc('\0', sv_fd), put16(a1) #define mk_long_2(oper,a1,a2) /* two argument long */ \ putc((char)(LONG|oper),sv_fd), putc('\0', sv_fd), \ put16(a1), put16(a2) /* choose between long and short format instructions */ /* NOTE: these macros can only be used where a STATEMENT is legal */ #define mk_inst_1(oper,a1) /* one argument inst */ \ if (a1>UPPER) (mk_long_1(oper,a1)); else (mk_short_1(oper,a1)) #define mk_inst_2(oper,a1,a2) /* two argument inst */ \ if (a1>UPPER) (mk_long_2(oper,a1,a2)); else (mk_short_2(oper,a1,a2)) /* * Opcode definitions */ #define RSkipLines(n) mk_inst_1(RSkipLinesOp,(n)) #define RSetColor(c) mk_short_1(RSetColorOp,(c)) /* has side effect of performing */ /* "carriage return" action */ #define RSkipPixels(n) mk_inst_1(RSkipPixelsOp,(n)) #define RNewLine RSkipLines(1) #define RByteData(n) mk_inst_1(RByteDataOp,n) /* followed by ((n+1)/2)*2 bytes */ /* of data. If n is odd, last */ /* byte will be ignored */ /* "cursor" is left at pixel */ /* following last pixel written */ #define RRunData(n,c) mk_inst_2(RRunDataOp,(n),(c)) /* next word contains color data */ /* "cursor" is left at pixel after */ /* end of run */ #define REOF mk_inst_1(REOFOp,0) /* Really opcode only */ /***************************************************************** * TAG( RunSetup ) * Put out initial setup data for RLE svfb files. */ void RunSetup(globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( RunSkipBlankLines ) * Skip one or more blank lines in the RLE file. */ void RunSkipBlankLines(nblank, globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( RunSetColor ) * Select a color and do carriage return. * color: 0 = Red, 1 = Green, 2 = Blue. */ void RunSetColor(c, globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( RunSkipPixels ) * Skip a run of background. */ /* ARGSUSED */ void RunSkipPixels(nskip, last, wasrun, globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( RunNewScanLine ) * Perform a newline action. Since CR is implied by the Set Color * operation, only generate code if the newline flag is true. */ void RunNewScanLine(flag, globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( Runputdata ) * Put one or more pixels of byte data into the output file. */ void Runputdata(buf, n, globals) rle_pixel * buf; register struct sv_globals * globals; { } /***************************************************************** * TAG( Runputrun ) * Output a single color run. */ /* ARGSUSED */ void Runputrun(color, n, last, globals) register struct sv_globals * globals; { } /***************************************************************** * TAG( RunputEof ) * Output an EOF opcode */ void RunputEof( globals ) register struct sv_globals * globals; { } /*ARGSUSED*/ void DefaultBlockHook(globals) struct sv_globals * globals; { } /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. */ /* * buildmap.c - Build a color map from the RLE file color map. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Sat Jan 24 1987 * Copyright (c) 1987, University of Utah */ /***************************************************************** * TAG( buildmap ) * * Returns a color map that can easily be used to map the pixel values in * an RLE file. Map is built from the color map in the input file. * Inputs: * globals: sv_globals structure containing color map. * minmap: Minimum number of channels in output map. * gamma: Adjust color map for this image gamma value * (1.0 means no adjustment). * Outputs: * Returns an array of pointers to arrays of rle_pixels. The array * of pointers contains max(sv_ncolors, sv_ncmap) elements, each * array of pixels contains 2^sv_cmaplen elements. The pixel arrays * should be considered read-only. * Assumptions: * [None] * Algorithm: * Ensure that there are at least sv_ncolors rows in the map, and * that each has at least 256 elements in it (largest map that can * be addressed by an rle_pixel). */ rle_pixel ** buildmap( globals, minmap, gamma ) struct sv_globals *globals; int minmap; double gamma; { rle_pixel ** cmap, * gammap; register int i, j; int maplen, cmaplen, ncmap, nmap; if ( globals->sv_ncmap == 0 ) /* make identity map */ { nmap = (minmap < globals->sv_ncolors) ? globals->sv_ncolors : minmap; cmap = (rle_pixel **)lmalloc( nmap * sizeof(rle_pixel *) ); cmap[0] = (rle_pixel *)lmalloc( 256 * sizeof(rle_pixel) ); for ( i = 0; i < 256; i++ ) cmap[0][i] = i; for ( i = 1; i < nmap; i++ ) cmap[i] = cmap[0]; maplen = 256; ncmap = 1; /* number of unique rows */ } else /* make map from globals */ { /* Map is at least 256 long */ cmaplen = (1 << globals->sv_cmaplen); if ( cmaplen < 256 ) maplen = 256; else maplen = cmaplen; if ( globals->sv_ncmap == 1 ) /* make "b&w" map */ { nmap = (minmap < globals->sv_ncolors) ? globals->sv_ncolors : minmap; cmap = (rle_pixel **)lmalloc( nmap * sizeof(rle_pixel *) ); cmap[0] = (rle_pixel *)lmalloc( maplen * sizeof(rle_pixel) ); for ( i = 0; i < maplen; i++ ) if ( i < cmaplen ) cmap[0][i] = globals->sv_cmap[i] >> 8; else cmap[0][i] = i; for ( i = 1; i < nmap; i++ ) cmap[i] = cmap[0]; ncmap = 1; } else if ( globals->sv_ncolors <= globals->sv_ncmap ) { nmap = (minmap < globals->sv_ncmap) ? globals->sv_ncmap : minmap; cmap = (rle_pixel **)lmalloc( nmap * sizeof(rle_pixel *) ); for ( j = 0; j < globals->sv_ncmap; j++ ) { cmap[j] = (rle_pixel *)lmalloc( maplen * sizeof(rle_pixel) ); for ( i = 0; i < maplen; i++ ) if ( i < cmaplen ) cmap[j][i] = globals->sv_cmap[j*cmaplen + i] >> 8; else cmap[j][i] = i; } for ( i = j, j--; i < nmap; i++ ) cmap[i] = cmap[j]; ncmap = globals->sv_ncmap; } else /* ncolors > ncmap */ { nmap = (minmap < globals->sv_ncolors) ? globals->sv_ncolors : minmap; cmap = (rle_pixel **)lmalloc( nmap * sizeof(rle_pixel *) ); for ( j = 0; j < globals->sv_ncmap; j++ ) { cmap[j] = (rle_pixel *)lmalloc( maplen * sizeof(rle_pixel) ); for ( i = 0; i < maplen; i++ ) if ( i < cmaplen ) cmap[j][i] = globals->sv_cmap[j*cmaplen + i] >> 8; else cmap[j][i] = i; } for( i = j, j--; i < nmap; i++ ) cmap[i] = cmap[j]; ncmap = globals->sv_ncmap; } } /* Gamma compensate if requested */ if ( gamma != 1.0 ) { gammap = (rle_pixel *)lmalloc( 256 * sizeof(rle_pixel) ); for ( i = 0; i < 256; i++ ) { #ifdef BYTEBUG int byteb1; byteb1 = (int)(0.5 + 255.0 * pow( i / 255.0, gamma )); gammap[i] = byteb1; #else gammap[i] = (int)(0.5 + 255.0 * pow( i / 255.0, gamma )); #endif } for ( i = 0; i < ncmap; i++ ) for ( j = 0; j < maplen; j++ ) cmap[i][j] = gammap[cmap[i][j]]; } return cmap; } /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. */ /* * rle_getcom.c - Get specific comments from globals structure. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Sun Jan 25 1987 * Copyright (c) 1987, University of Utah */ /***************************************************************** * TAG( match ) * * Match a name against a test string for "name=value" or "name". * If it matches name=value, return pointer to value part, if just * name, return pointer to NUL at end of string. If no match, return NULL. * * Inputs: * n: Name to match. May also be "name=value" to make it easier * to replace comments. * v: Test string. * Outputs: * Returns pointer as above. * Assumptions: * [None] * Algorithm: * [None] */ static char * match( n, v ) register char *n; register char *v; { for ( ; *n != '\0' && *n != '=' && *n == *v; n++, v++ ) ; if (*n == '\0' || *n == '=') if ( *v == '\0' ) return v; else if ( *v == '=' ) return ++v; return NULL; } /***************************************************************** * TAG( rle_getcom ) * * Return a pointer to the value part of a name=value pair in the comments. * Inputs: * name: Name part of the comment to search for. * globals: sv_globals structure. * Outputs: * Returns pointer to value part of comment or NULL if no match. * Assumptions: * [None] * Algorithm: * [None] */ char * rle_getcom( name, globals ) char *name; struct sv_globals *globals; { char ** cp; char * v; if ( globals->sv_comments == NULL ) return NULL; for ( cp = globals->sv_comments; *cp; cp++ ) if ( (v = match( name, *cp )) != NULL ) return v; return NULL; } /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. * * Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire * to have all "void" functions so declared. */ /* * rle_getrow.c - Read an RLE file in. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Wed Apr 10 1985 * Copyright (c) 1985 Spencer W. Thomas * */ /* * Automatically define LITTLE_ENDIAN on vax and pdp11 machines */ #if defined(vax) || defined(pdp11) #define LITTLE_ENDIAN #endif struct inst { unsigned opcode:8, datum:8; }; #define BREAD(type, var, len)\ zread( infile, (byte *)&var,len ) #define OPCODE(inst) (inst.opcode & ~LONG) #define LONGP(inst) (inst.opcode & LONG) #define DATUM(inst) (0x00ff & inst.datum) static int debug_f; /* if non-zero, print debug info */ static void bfill(); /***************************************************************** * TAG( rle_get_setup ) * * Read the initialization information from an RLE file. * Inputs: * globals: Contains pointer to the input file. * Outputs: * globals: Initialized with information from the * input file. * Returns 0 on success, -1 if the file is not an RLE file, * -2 if malloc of the color map failed, -3 if an immediate EOF * is hit (empty input file), and -4 if an EOF is encountered reading * the setup information. * Assumptions: * infile points to the "magic" number in an RLE file (usually * byte 0 in the file). * Algorithm: * Read in the setup info and fill in sv_globals. */ int rle_get_setup( globals ) struct sv_globals * globals; { struct XtndRsetup setup; short magic; /* assume 16 bits */ register ZFILE *infile = globals->svfb_fd; rle_pixel * bg_color; register int i; char * comment_buf; zclearerr(infile); BREAD( short, magic, sizeof magic ); SWAB(magic); if ( zeof( infile ) ) return -3; if ( magic != XtndRMAGIC ) return -1; BREAD( struct XtndRsetup, setup, SETUPSIZE ); /* assume VAX packing */ if ( zeof( infile ) ) return -4; SWAB( setup.h_xpos ); SWAB( setup.h_ypos ); SWAB( setup.h_xlen ); SWAB( setup.h_ylen ); /* Extract information from setup */ globals->sv_ncolors = setup.h_ncolors; for ( i = 0; i < globals->sv_ncolors; i++ ) SV_SET_BIT( *globals, i ); if ( !(setup.h_flags & H_NO_BACKGROUND) ) { globals->sv_bg_color = (int *)lmalloc( (unsigned)(sizeof(int) * setup.h_ncolors) ); bg_color = (rle_pixel *)lmalloc( (unsigned)(1 + (setup.h_ncolors / 2) * 2) ); zread( infile, (byte *)bg_color, 1 + (setup.h_ncolors / 2) * 2 ); for ( i = 0; i < setup.h_ncolors; i++ ) globals->sv_bg_color[i] = bg_color[i]; lfree( bg_color ); } else zgetc( infile ); /* skip filler byte */ if ( setup.h_flags & H_NO_BACKGROUND ) globals->sv_background = 0; else if ( setup.h_flags & H_CLEARFIRST ) globals->sv_background = 2; else globals->sv_background = 1; if ( setup.h_flags & H_ALPHA ) { globals->sv_alpha = 1; SV_SET_BIT( *globals, SV_ALPHA ); } else globals->sv_alpha = 0; globals->sv_xmin = setup.h_xpos; globals->sv_ymin = setup.h_ypos; globals->sv_xmax = globals->sv_xmin + setup.h_xlen - 1; globals->sv_ymax = globals->sv_ymin + setup.h_ylen - 1; globals->sv_ncmap = setup.h_ncmap; globals->sv_cmaplen = setup.h_cmaplen; if ( globals->sv_ncmap > 0 ) { register int maplen = globals->sv_ncmap * (1 << globals->sv_cmaplen); globals->sv_cmap = (rle_map *)lmalloc( (unsigned)(sizeof(rle_map) * maplen) ); if ( globals->sv_cmap == NULL ) { fprintf( stderr, "Malloc failed for color map of size %d*%d in rle_get_setup\n", globals->sv_ncmap, (1 << globals->sv_cmaplen) ); return -2; } zread( infile, (byte *)globals->sv_cmap, sizeof(short) * maplen ); #ifndef LITTLE_ENDIAN /* Swap bytes on bigendian machines */ for ( i = 0; i < maplen; i++ ) SWAB( globals->sv_cmap[i] ); #endif } /* Check for comments */ if ( setup.h_flags & H_COMMENT ) { short comlen, evenlen; register char * cp; BREAD( short, comlen, sizeof comlen ); /* get comment length */ SWAB( comlen ); evenlen = (comlen + 1) & ~1; /* make it even */ comment_buf = (char *)lmalloc( (unsigned) evenlen ); if ( comment_buf == NULL ) { fprintf( stderr, "Malloc failed for comment buffer of size %d in rle_get_setup\n", comlen ); return -2; } zread( infile, (byte *)comment_buf, evenlen ); /* Count the comments */ for ( i = 0, cp = comment_buf; cp < comment_buf + comlen; cp++ ) if ( *cp == 0 ) i++; i++; /* extra for NULL pointer at end */ /* Get space to put pointers to comments */ globals->sv_comments = (char **)lmalloc( (unsigned)(i * sizeof(char *)) ); if ( globals->sv_comments == NULL ) { fprintf( stderr, "Malloc failed for %d comment pointers in rle_get_setup\n", i ); return -2; } /* Get pointers to the comments */ *globals->sv_comments = comment_buf; for ( i = 1, cp = comment_buf + 1; cp < comment_buf + comlen; cp++ ) if ( *(cp - 1) == 0 ) globals->sv_comments[i++] = cp; globals->sv_comments[i] = NULL; } else globals->sv_comments = NULL; /* Initialize state for rle_getrow */ globals->sv_private.get.scan_y = globals->sv_ymin; globals->sv_private.get.vert_skip = 0; globals->sv_private.get.is_eof = 0; globals->sv_private.get.is_seek = 0; /* Can't do seek on zfile */ debug_f = 0; if ( !zeof( infile ) ) return 0; /* success! */ else { globals->sv_private.get.is_eof = 1; return -4; } } /***************************************************************** * TAG( rle_get_error ) * * Print an error message for the return code from rle_get_setup * Inputs: * code: The return code from rle_get_setup. * pgmname: Name of this program (argv[0]). * fname: Name of the input file. * Outputs: * Prints an error message on standard output. * Returns code. */ int rle_get_error( code, pgmname, fname ) int code; char *pgmname; char *fname; { switch( code ) { case 0: /* success */ break; case -1: /* Not an RLE file */ fprintf( stderr, "%s: %s is not an RLE file\n", pgmname, fname ); break; case -2: /* malloc failed */ fprintf( stderr, "%s: Malloc failed reading header of file %s\n", pgmname, fname ); break; case -3: fprintf( stderr, "%s: %s is an empty file\n", pgmname, fname ); break; case -4: fprintf( stderr, "%s: RLE header of %s is incomplete (premature EOF)\n", pgmname, fname ); break; default: fprintf( stderr, "%s: Error encountered reading header of %s\n", pgmname, fname ); break; } return code; } /***************************************************************** * TAG( rle_get_setup_ok ) * * Read the initialization information from an RLE file. * Inputs: * globals: Contains pointer to the input file. * prog_name: Program name to be printed in the error message. * file_name: File name to be printed in the error message. * If NULL, the string "stdin" is generated. * Outputs: * globals: Initialized with information from the * input file. * If reading the globals fails, it prints an error message * and exits with the appropriate status code. * Algorithm: * sv_get_setup does all the work. */ void rle_get_setup_ok( globals, prog_name, file_name ) struct sv_globals * globals; char *prog_name; char *file_name; { int code; if (! file_name) file_name = "stdin"; code = rle_get_error( rle_get_setup( globals ), prog_name, file_name ); if (code) cleanup(-1); } /***************************************************************** * TAG( rle_debug ) * * Turn RLE debugging on or off. * Inputs: * on_off: if 0, stop debugging, else start. * Outputs: * Sets internal debug flag. * Assumptions: * [None] * Algorithm: * [None] */ void rle_debug( on_off ) int on_off; { debug_f = on_off; } /***************************************************************** * TAG( rle_getrow ) * * Get a scanline from the input file. * Inputs: * globals: sv_globals structure containing information about * the input file. * Outputs: * scanline: an array of pointers to the individual color * scanlines. Scanline is assumed to have * globals->sv_ncolors pointers to arrays of rle_pixel, * each of which is at least globals->sv_xmax+1 long. * Returns the current scanline number. * Assumptions: * rle_get_setup has already been called. * Algorithm: * If a vertical skip is being executed, and clear-to-background is * specified (globals->sv_background is true), just set the * scanlines to the background color. If clear-to-background is * not set, just increment the scanline number and return. * * Otherwise, read input until a vertical skip is encountered, * decoding the instructions into scanline data. */ int rle_getrow( globals, scanline ) struct sv_globals * globals; rle_pixel *scanline[]; { register rle_pixel * scanc; register int nc; register ZFILE *infile = globals->svfb_fd; int scan_x = globals->sv_xmin, /* current X position */ channel = 0; /* current color channel */ short word, long_data; struct inst inst; /* Clear to background if specified */ if ( globals->sv_background == 2 ) { if ( globals->sv_alpha && SV_BIT( *globals, -1 ) ) bfill( (char *)scanline[-1], globals->sv_xmax + 1, 0 ); for ( nc = 0; nc < globals->sv_ncolors; nc++ ) if ( SV_BIT( *globals, nc ) ) bfill( (char *)scanline[nc], globals->sv_xmax+1, globals->sv_bg_color[nc] ); } /* If skipping, then just return */ if ( globals->sv_private.get.vert_skip > 0 ) { globals->sv_private.get.vert_skip--; globals->sv_private.get.scan_y++; if ( globals->sv_private.get.vert_skip > 0 ) return globals->sv_private.get.scan_y; } /* If EOF has been encountered, return also */ if ( globals->sv_private.get.is_eof ) return ++globals->sv_private.get.scan_y; /* Otherwise, read and interpret instructions until a skipLines * instruction is encountered. */ if ( SV_BIT( *globals, channel ) ) scanc = scanline[channel] + scan_x; else scanc = NULL; for (;;) { BREAD(struct inst, inst, 2 ); if ( zeof(infile) ) { globals->sv_private.get.is_eof = 1; break; /* <--- one of the exits */ } switch( OPCODE(inst) ) { case RSkipLinesOp: if ( LONGP(inst) ) { BREAD( short, long_data, sizeof long_data ); SWAB( long_data ); globals->sv_private.get.vert_skip = long_data; } else globals->sv_private.get.vert_skip = DATUM(inst); if (debug_f) fprintf(stderr, "Skip %d Lines (to %d)\n", globals->sv_private.get.vert_skip, globals->sv_private.get.scan_y + globals->sv_private.get.vert_skip ); break; /* need to break for() here, too */ case RSetColorOp: channel = DATUM(inst); /* select color channel */ if ( channel == 255 ) channel = -1; scan_x = globals->sv_xmin; if ( SV_BIT( *globals, channel ) ) scanc = scanline[channel]+scan_x; if ( debug_f ) fprintf( stderr, "Set color to %d (reset x to %d)\n", channel, scan_x ); break; case RSkipPixelsOp: if ( LONGP(inst) ) { BREAD( short, long_data, sizeof long_data ); SWAB( long_data ); scan_x += long_data; scanc += long_data; if ( debug_f ) fprintf( stderr, "Skip %d pixels (to %d)\n", long_data, scan_x ); } else { scan_x += DATUM(inst); scanc += DATUM(inst); if ( debug_f ) fprintf( stderr, "Skip %d pixels (to %d)\n", DATUM(inst), scan_x ); } break; case RByteDataOp: if ( LONGP(inst) ) { BREAD( short, long_data, sizeof long_data ); SWAB( long_data ); nc = (int)long_data; } else nc = DATUM(inst); nc++; if ( SV_BIT( *globals, channel ) ) { zread( infile, (byte *)scanc, nc ); if ( nc & 1 ) (void)zgetc( infile ); /* throw away odd byte */ } else { /* Emulate a forward fseek */ register int ii; for ( ii = ((nc + 1) / 2) * 2; ii > 0; ii-- ) (void) zgetc( infile ); /* discard it */ } scanc += nc; scan_x += nc; if ( debug_f ) if ( SV_BIT( *globals, channel ) ) { rle_pixel * cp = scanc - nc; fprintf( stderr, "Pixel data %d (to %d):", nc, scan_x ); for ( ; nc > 0; nc-- ) fprintf( stderr, "%02x", *cp++ ); putc( '\n', stderr ); } else fprintf( stderr, "Pixel data %d (to %d)\n", nc, scan_x ); break; case RRunDataOp: if ( LONGP(inst) ) { BREAD( short, long_data, sizeof long_data ); SWAB( long_data ); nc = long_data; } else nc = DATUM(inst); scan_x += nc + 1; BREAD( short, word, sizeof(short) ); SWAB( word ); if ( debug_f ) fprintf( stderr, "Run length %d (to %d), data %02x\n", nc + 1, scan_x, word ); if ( SV_BIT( *globals, channel ) ) { if ( nc >= 10 ) /* break point for 785, anyway */ { bfill( (char *)scanc, nc + 1, word ); scanc += nc + 1; } else for ( ; nc >= 0; nc--, scanc++ ) *scanc = word; } break; case REOFOp: globals->sv_private.get.is_eof = 1; break; default: fprintf( stderr, "rle_getrow: Unrecognized opcode: %d\n", inst.opcode ); cleanup(-1); } if ( OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp ) break; /* <--- the other loop exit */ } return globals->sv_private.get.scan_y; } /* Fill buffer at s with n copies of character c. N must be <= 65535*/ /* ARGSUSED */ static void bfill( s, n, c ) char *s; int n, c; { #ifdef vax asm(" movc5 $0,*4(ap),12(ap),8(ap),*4(ap)"); #else while ( n-- > 0 ) *s++ = c; #endif } /* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. * * Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire * to have all "void" functions so declared. * * Modified to generate an apropriate size dither map (ie 2x2, 4x4, 8x8 * or 16x16) rather than use fixed 16x16 map. Use a large enough map * to give a minimum of 128 effective levels rather than aiming for 256. * This should give less grainy pictures. * Two global variables can modify this: * dith_levels = 128 (default) * dith_np2 = 0 (default). Nonzero to enable non power of 2 dither mapping. * dith_size = actual dither matrix size chosen. * * Graeme Gill 3 June 88 */ /* * dither.c - Functions for RGB color dithering. * * Author: Spencer W. Thomas * Computer Science Dept. * University of Utah * Date: Mon Feb 2 1987 * Copyright (c) 1987, University of Utah */ void make_square(); /* dither globals */ int dith_levels = 128; int dith_np2 = 0; int dith_size = 16; /* basic dithering macro */ #define DMAP(v,x,y) (modN[v]>magic[x][y] ? divN[v] + 1 : divN[v]) /***************************************************************** * TAG( dithermap ) * * Create a color dithering map with a specified number of intensity levels. * Inputs: * levels: Intensity levels per primary. * gamma: Display gamma value. * Outputs: * rgbmap: Generated color map. * divN: "div" function for dithering. * modN: "mod" function for dithering. * Assumptions: * rgbmap will hold levels^3 entries. * Algorithm: * Compute gamma compensation map. * N = 255.0 / (levels - 1) is number of pixel values per level. * Compute rgbmap with red ramping fastest, green slower, and blue * slowest (treat it as if it were rgbmap[levels][levels][levels][3]). * Call make_square to get divN, modN, and magic * * Note: * Call dithergb( x, y, r, g, b, levels, divN, modN, magic ) to get index * into rgbmap for a given color/location pair, or use * row = y % 16; col = x % 16; * DMAP(v,col,row) =def (divN[v] + (modN[v]>magic[col][row] ? 1 : 0)) * DMAP(r,col,row) + DMAP(g,col,row)*levels + DMAP(b,col,row)*levels^2 * if you don't want function call overhead. */ void dithermap( levels, gamma, rgbmap, divN, modN, magic ) double gamma; int rgbmap[][3]; int divN[256]; int modN[256]; int magic[16][16]; { double N; register int i; int levelsq, levelsc; int gammamap[256]; make_gamma(gamma,gammamap); levelsq = levels*levels; /* squared */ levelsc = levels*levelsq; /* and cubed */ N = 255.0 / (levels - 1); /* Get size of each step */ /* * Set up the color map entries. */ for(i = 0; i < levelsc; i++) { rgbmap[i][0] = gammamap[(int)(0.5 + (i%levels) * N)]; rgbmap[i][1] = gammamap[(int)(0.5 + ((i/levels)%levels) * N)]; rgbmap[i][2] = gammamap[(int)(0.5 + ((i/levelsq)%levels) * N)]; } make_square( N, divN, modN, magic ); } /***************************************************************** * TAG( bwdithermap ) * * Create a color dithering map with a specified number of intensity levels. * Inputs: * levels: Intensity levels. * gamma: Display gamma value. * Outputs: * bwmap: Generated black & white map. * divN: "div" function for dithering. * modN: "mod" function for dithering. * Assumptions: * bwmap will hold levels entries. * Algorithm: * Compute gamma compensation map. * N = 255.0 / (levels - 1) is number of pixel values per level. * Compute bwmap for levels entries. * Call make_square to get divN, modN, and magic. * Note: * Call ditherbw( x, y, val, divN, modN, magic ) to get index into * bwmap for a given color/location pair, or use * row = y % 16; col = x % 16; * divN[val] + (modN[val]>magic[col][row] ? 1 : 0) * if you don't want function call overhead. * On a 1-bit display, use * divN[val] > magic[col][row] ? 1 : 0 */ void bwdithermap( levels, gamma, bwmap, divN, modN, magic ) double gamma; int bwmap[]; int divN[256]; int modN[256]; int magic[16][16]; { double N; register int i; int gammamap[256]; make_gamma(gamma,gammamap); N = 255.0 / (levels - 1); /* Get size of each step */ /* * Set up the color map entries. */ for(i = 0; i < levels; i++) bwmap[i] = gammamap[(int)(0.5 + i * N)]; make_square( N, divN, modN, magic ); } int magic16x16[16][16] = { {0,128,32,160,8,136,40,168,2,130,34,162,10,138,42,170}, {192,64,224,96,200,72,232,104,194,66,226,98,202,74,234,106}, {48,176,16,144,56,184,24,152,50,178,18,146,58,186,26,154}, {240,112,208,80,248,120,216,88,242,114,210,82,250,122,218,90}, {12,140,44,172,4,132,36,164,14,142,46,174,6,134,38,166}, {204,76,236,108,196,68,228,100,206,78,238,110,198,70,230,102}, {60,188,28,156,52,180,20,148,62,190,30,158,54,182,22,150}, {252,124,220,92,244,116,212,84,254,126,222,94,246,118,214,86}, {3,131,35,163,11,139,43,171,1,129,33,161,9,137,41,169}, {195,67,227,99,203,75,235,107,193,65,225,97,201,73,233,105}, {51,179,19,147,59,187,27,155,49,177,17,145,57,185,25,153}, {243,115,211,83,251,123,219,91,241,113,209,81,249,121,217,89}, {15,143,47,175,7,135,39,167,13,141,45,173,5,133,37,165}, {207,79,239,111,199,71,231,103,205,77,237,109,197,69,229,101}, {63,191,31,159,55,183,23,151,61,189,29,157,53,181,21,149}, {255,127,223,95,247,119,215,87,253,125,221,93,245,117,213,85} }; /***************************************************************** * TAG( make_magic ) * * Create the magic square. * Inputs: * size: Order of the square * magic: Address of 16 x 16 magic square. * Outputs: * Fills in the 16 x 16 magic square. * Assumptions: * size is between 2 and 16 * Algorithm: * Chose sub cell of 16 by 16 magic square */ static void make_magic( size, magic ) int size; int magic[16][16]; { int j,i,li,bi,bx=0,by=0; int xx,yy; int total; total = size * size; i = 0; li = -1; for(j=0;jli && magic16x16[xx][yy] < bi) { bx = xx; by = yy; bi = magic16x16[xx][yy]; } } } magic[bx][by] = i; i++; li = bi; } } /***************************************************************** * TAG( make_square ) * * Build the magic square for a given number of levels. * Inputs: * N: Pixel values per level (255.0 / (levels-1)). * (global) dith_levels = 128 (default) - number of effective levels to aim for * (global) dith_np2 = 0 (default) - non-zero if non power of two size is permissable. * Outputs: * divN: Integer value of pixval / N * modN: Integer remainder between pixval and divN[pixval]*N * magic: Magic square for dithering to N sublevels. * (global) dith_size = magic square size chosen. * Assumptions: * * Algorithm: * divN[pixval] = (int)(pixval / N) maps pixval to its appropriate level. * modN[pixval] = pixval - (int)(N * divN[pixval]) maps pixval to * its sublevel, and is used in the dithering computation. */ void make_square( N, divN, modN, magic ) double N; int divN[256]; int modN[256]; int magic[16][16] ; { register int i, j, k, l; double magicfact; for ( i = 0; i < 256; i++ ) { divN[i] = (int)(i / N); modN[i] = i - (int)(N * divN[i]); } modN[255] = 0; /* always */ /* figure out how big a square will give */ /* the desired number of levels */ if(dith_np2) for(dith_size= 2;((dith_size * dith_size)+1)<((N*dith_levels)/256);dith_size++ ); else for(dith_size= 2;((dith_size * dith_size)+1)<((N*dith_levels)/256);dith_size *=2); /* make the basic square up */ /* ( will have numbers 0 - size * size ) */ make_magic(dith_size,magic); /* divN gives 0 - levels-1 */ /* modN gives 0 - N-1 */ /* dither is if(modN(pix) > magic[][] so */ /* scale magic it to have levels 0 to N-2 */ /* (ie takes account of magic square size allows size * size +1 levels */ magicfact = (N-2)/((double)((dith_size * dith_size)-1)); for(i=0;i0?0:dith_size;l<16;l += dith_size) { for(i=0;i