[comp.graphics] QRT to GIF Image Converter

mgardi@watdcsu.waterloo.edu (M.Gardi - ICR) (03/03/89)

Here is my QRT to GIF conversion program.  With this, you should be able
to create those .RAW files on the fastest machine at your disposal,
GIF the image, and then view it on the machine with the best resolution/
number of colors!

Thanks to Steve Koren for writing QRT, and to Michael Mauldin for
the code for Heckbert Median Cut algorithm that I use.

QRT is a great ray-tracer.  It's script language is MUCH better than
anything else I've seen (such as NFF, etc.  The script language for
DBW render is ATROCIOUS!)

One thing that is lacking, however, is a wide variety of objects
that it can model.  Steve has coded it in a very flexible way, though,
so it should be easy to add new objects, and patterns.

I would like to get the source to DBW Render and merge the marble and
wood patterns and fractal objects to the QRT source -- maybe someone
else would like to add onto QRT as well -- it could evolve into an
extremely powerful raytracer (that is completely hardware independent!)

Steve talks in his notes about mapping bitmaps onto spheres, etc.  One
simple way would be to use GIF files, which would maintain the hardware
independence -- in fact you could do wood and marble by wrapping
GIF images of wood and marble onto the objects.

Anyway -- back to the GIF conversion utility.  The way the QRT source is
currently coded, the .RAW file format is machine-specific.  I changed
my 'Dump_Line' routine to output the scan line number in INTEL format,
same as XRES and YRES.  The GIF conversion utility assumes that the
.RAW file is in this format.  INTEL format isn't necessarily the 'best'
but we may as well standardize on one -- there is no need to have the
.RAW format be machine specific.

Also, don't forget to apply the fix to lexer.c that Steve posted
(replace 'malloc(strlen(str))' with 'malloc(strlen(str)+1)'.


If anyone has any new extensions to QRT, or has fixed the triangle object,
I would appreciate hearing about it...

David Rowley
Mutual Life of Canada
mgardi@watdcsu.waterloo.edu  (Attn: David)

--------------cut here-------------------cut here-----------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by watdcsu!mgardi on Thu Mar  2 21:35:22 EST 1989
# Contents:  bitgen.c bitwgif.c gifcompr.c gifencod.c qrt2gif.c bitmap.h
#	makefile
 
echo x - bitgen.c
sed 's/^@//' > "bitgen.c" <<'@//E*O*F bitgen.c//'

#include <stdio.h>
#include "bitmap.h"

extern char *malloc();
extern FILE *fopen();

#define MAX_IN_MEMORY	500000L

static int Bitmasks[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
static int bitcount = 0;

static int
NumBits( colors )
int colors;
{
	int  val;
	int  bits;

	val = 2;
	bits = 1;

	while( colors > val ) {
		val *= 2;
		bits++;
	}

	return bits;
}


void
Bitmap_GetScanLine( bitmap, scanline, ptr )
Bitmap *bitmap;
int scanline;
char **ptr;
{
	if( bitmap->BFlags == BITMAP_IN_MEMORY ) {
		*ptr = bitmap->lineptr[scanline];
		return;
	}

	if( scanline == bitmap->BCached ) {
		*ptr = (char *)(bitmap->lineptr);
		return;
	}

	if( bitmap->BCached >= 0 ) {
		fseek( bitmap->BFile, 
		       (long)bitmap->BCached * (long)bitmap->BBytes,
		       0 );
		fwrite( (char *)(bitmap->lineptr), 1, 
			bitmap->BBytes, bitmap->BFile );
	}

	fseek( bitmap->BFile, (long)scanline * (long)bitmap->BBytes, 0 );
	fread( (char *)(bitmap->lineptr), 1, 
	       bitmap->BBytes, bitmap->BFile );

	bitmap->BCached = scanline;

	*ptr = bitmap->lineptr;
}

char *
Bitmap_Name( count )
int count;
{
	static char buffer[ 80 ];
	char temp[ 20 ];
	char *ptr;
	extern char *getenv();

	ptr = getenv( "TMPDIR" );
	if( ptr == (char *)0 )
		strcpy( buffer, "" );
	else {
		strcpy( buffer, ptr );
		if( buffer[ strlen( buffer ) - 1 ] != '\\' )
			strcat( buffer, "\\" );
	}
	sprintf( temp, "bitmap%d", bitcount );
	strcat( buffer, temp );

	return buffer;
}

Bitmap *
Bitmap_Create( width, height, colors )
int width, height;
int colors;
{
	int i, j;
	int bytes;
	int bits;
	Bitmap *bitmap;
	long total_size;
	char *bname;
	char *ptr;

	bitmap = (Bitmap *) malloc( sizeof( Bitmap ) );
	if( bitmap == (Bitmap *)0 )
		return (Bitmap *)0;
		
	/* 
	 * Find out how many bits we need for 'colors' colors
	 */
	bits = NumBits( colors );

	/*
	 * Save sizes of bitmap
	 */
	bitmap->BWidth  = width;
	bitmap->BHeight = height;
	bitmap->BBits   = bits;
	bitmap->BColors = colors;

	bitmap->pal = (struct PAL_ENTRY *) malloc( sizeof( struct PAL_ENTRY ) *
			colors );

	if( bitmap->pal == (char *)0 )
		return NIL;

	bzermem( bitmap->pal, sizeof( struct PAL_ENTRY ) * colors );


	bytes = ((width + 7) * bits) / 8;
	bitmap->BBytes = bytes;

	total_size = (long)bytes * (long)height;
	if( total_size > MAX_IN_MEMORY ) {

on_file:
		bname = Bitmap_Name( bitcount );
		printf( "Bitmap_Create: file '%s' for %ld byte bitmap\n", 
				bname, total_size );

		bitmap->BNumber = bitcount++;
		bitmap->BFile = fopen( bname, "wb" );

		if( bitmap->BFile == (FILE *)0 ) {
			free( bitmap->pal );
			free( bitmap );
			return NIL;
		}

		bitmap->lineptr = (char **) malloc( bytes );

		bzermem( bitmap->lineptr, bytes );
		bitmap->BCached = -1;
		bitmap->BFlags = BITMAP_IN_FILE;

		for( i=0; i<height; i++ ) {
			fwrite( bitmap->lineptr, 1, bytes, bitmap->BFile );
		}

		printf( "Created bitmap file.\n" );
		fclose( bitmap->BFile );
		bitmap->BFile = fopen( bname, "rb+" );

	} else {

		bitmap->lineptr = (char **) malloc( sizeof( char * ) * height );
		if( bitmap->lineptr == (char **)0 ) {
			free( bitmap->pal );
			free( bitmap );
			return NIL;
		}

		for( i=0; i<height; i++ ) {
			bitmap->lineptr[i] = (char *) malloc( bytes );
	
			/*
			 * If the malloc failed, free up all the memory 
			 * allocated so far
			 */
			if( bitmap->lineptr[i] == (char *)0 ) {
				for( j=0; j<i; j++ ) {
					free( bitmap->lineptr[j] );
				}
				free( bitmap->lineptr );
				goto on_file;
			}
			bzermem( bitmap->lineptr[i], bytes );
		}
		bitmap->BFlags = BITMAP_IN_MEMORY;
	}
	return bitmap;
}

/*
 * Plots a pixel at (x,y)
 */
Bitmap_Plot( bitmap, x, y, value )
Bitmap *bitmap;
int x, y;
int value;
{
	int byteoffset;
	int mask;
	char *ptr;
	int i;

	if( x >= bitmap->BWidth || x < 0 ) return NIL;
	if( y >= bitmap->BHeight || y < 0 ) return NIL;
		
	x *= bitmap->BBits;
	byteoffset = x / 8;
	mask = Bitmasks[ x % 8 ];

	if( bitmap->BFlags == BITMAP_IN_MEMORY )
		ptr = &bitmap->lineptr[ y ][ byteoffset ];
	else {
		Bitmap_GetScanLine( bitmap, y, &ptr );
		ptr += byteoffset;
	}
	
	i = bitmap->BBits;
	while( i > 0 ) {
		if( value & 1 )
			*ptr |= mask;
		else
			*ptr &= ~mask;

		value >>= 1;
		mask <<= 1;
		if( mask > 128 ) {
			mask = 1;
			ptr++;
		}
		i--;
	}

	return NIL;
}

/*
 * Returns the pixel at (x,y)
 */
int
Bitmap_Get( bitmap, x, y )
Bitmap *bitmap;
int x, y;
{
	int byteoffset;
	int mask;
	char *ptr;
	int value;
	int i, vmask;
	
	if( x >= bitmap->BWidth || x < 0 ) return 0;
	if( y >= bitmap->BHeight || y < 0 ) return 0;
		
	x *= bitmap->BBits;
	byteoffset = x / 8;
	mask = Bitmasks[ x % 8 ];

	ptr = &bitmap->lineptr[ y ][ byteoffset ];

	if( bitmap->BFlags == BITMAP_IN_MEMORY )
		ptr = &bitmap->lineptr[ y ][ byteoffset ];
	else {
		Bitmap_GetScanLine( bitmap, y, &ptr );
		ptr += byteoffset;
	}
	
	value = 0;
	i = bitmap->BBits;
	vmask = 1;
	while( i > 0 ) {
		if( *ptr & mask )
			value |= vmask;

		vmask <<= 1;
		mask <<= 1;
		if( mask > 128 ) {
			mask = 1;
			ptr++;
		}
		i--;
	}

	return value;
}

Bitmap_Free( bitmap )
Bitmap *bitmap;
{
	int i;
	char *ptr;

	if( bitmap->BFlags == BITMAP_IN_MEMORY ) {
		for( i=0; i<bitmap->BHeight; i++ )
			free( bitmap->lineptr[i] );
	} else {
		fclose( bitmap->BFile );
		ptr = Bitmap_Name( bitmap->BNumber );
#if unix || MarkWilliams
		unlink( ptr );
#else
		remove( ptr );
#endif
		/*
		 * Unlink the file
		 */
	}

	free( bitmap->lineptr );
	free( bitmap->pal );
	free( bitmap );
}

bzermem( ptr, size )
register char *ptr;
register unsigned int size;
{
	while( size-- ) *ptr++ = 0;
}	
@//E*O*F bitgen.c//
chmod u=rw,g=r,o=r bitgen.c
 
echo x - bitwgif.c
sed 's/^@//' > "bitwgif.c" <<'@//E*O*F bitwgif.c//'

#include "bitmap.h"

/**************************************************************************
 *
 * METHOD:	WRITE AS GIF
 *
 * FUNCTION:	Write out the bitmap as a GIF file
 *
 * PARAMETERS:	The filename
 *
 * RETURNS:	Nil
 *
 *************************************************************************/ 

static Bitmap *the_bitmap;

static 
int M_W_GIFPixel( x, y )
int x, y;
{
	int c;

	c = Bitmap_Get( the_bitmap, x, y );
	return c;
}

#define MAX_COLORS 256

static int g_red[ MAX_COLORS ], g_green[ MAX_COLORS ], g_blue[ MAX_COLORS ];

Bitmap_WriteAsGIF( bitmap, fname, interlace )
Bitmap *bitmap;		/* Pointer to the bitmap area */
char *fname;
int interlace;
{
	int i;
	the_bitmap = bitmap;

	for( i=0; i<bitmap->BColors; i++ ) {
		g_red[i] = bitmap->pal[i].red;
		g_green[i] = bitmap->pal[i].green;
		g_blue[i] = bitmap->pal[i].blue;
	}

	GIFEncode( fname, bitmap->BWidth, bitmap->BHeight, interlace, 0,
		bitmap->BBits, g_red, g_green, g_blue, M_W_GIFPixel );

}
@//E*O*F bitwgif.c//
chmod u=rw,g=r,o=r bitwgif.c
 
echo x - gifcompr.c
sed 's/^@//' > "gifcompr.c" <<'@//E*O*F gifcompr.c//'
/***************************************************************************
 *
 *  GIFENCOD.C       - GIF Image compression routines
 *
 ***************************************************************************/

/*
 * General DEFINEs
 */
#define min(a,b)        ((a>b) ? b : a)

#define BITS	12
#define MSDOS	1

#define HSIZE  5003            /* 80% occupancy */

/*
 * Pointer to function returning an int
 */
typedef int (* ifunptr)();

/*
 * a code_int must be able to hold 2**BITS values of type int, and also -1
 */
typedef int             code_int;

#ifdef SIGNED_COMPARE_SLOW
typedef unsigned long int count_int;
typedef unsigned short int count_short;
#else
typedef long int          count_int;
#endif

#ifdef NO_UCHAR
 typedef char   char_type;
#else
 typedef        unsigned char   char_type;
#endif /* UCHAR */

/*
 *
 * GIF Image compression - modified 'compress'
 *
 * compress.c - File compression ala IEEE Computer, June 1984.
 *
 * Authors:     Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
 *              Jim McKie               (decvax!mcvax!jim)
 *              Steve Davies            (decvax!vax135!petsd!peora!srd)
 *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
 *              James A. Woods          (decvax!ihnp4!ames!jaw)
 *              Joe Orost               (decvax!vax135!petsd!joe)
 *
 * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $
 * $Log:        compress.c,v $
 * Revision 4.0  85/07/30  12:50:00  joe
 * Removed ferror() calls in output routine on every output except first.
 * Prepared for release to the world.
 * 
 * Revision 3.6  85/07/04  01:22:21  joe
 * Remove much wasted storage by overlaying hash table with the tables
 * used by decompress: tab_suffix[1<<BITS], stack[8000].  Updated USERMEM
 * computations.  Fixed dump_tab() DEBUG routine.
 *
 * Revision 3.5  85/06/30  20:47:21  jaw
 * Change hash function to use exclusive-or.  Rip out hash cache.  These
 * speedups render the megamemory version defunct, for now.  Make decoder
 * stack global.  Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
 *
 * Revision 3.4  85/06/27  12:00:00  ken
 * Get rid of all floating-point calculations by doing all compression ratio
 * calculations in fixed point.
 *
 * Revision 3.3  85/06/24  21:53:24  joe
 * Incorporate portability suggestion for M_XENIX.  Got rid of text on #else
 * and #endif lines.  Cleaned up #ifdefs for vax and interdata.
 *
 * Revision 3.2  85/06/06  21:53:24  jaw
 * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
 * Default to "quiet" output (no compression statistics).
 *
 * Revision 3.1  85/05/12  18:56:13  jaw
 * Integrate decompress() stack speedups (from early pointer mods by McKie).
 * Repair multi-file USERMEM gaffe.  Unify 'force' flags to mimic semantics
 * of SVR2 'pack'.  Streamline block-compress table clear logic.  Increase 
 * output byte count by magic number size.
 * 
 * Revision 3.0   84/11/27  11:50:00  petsd!joe
 * Set HSIZE depending on BITS.  Set BITS depending on USERMEM.  Unrolled
 * loops in clear routines.  Added "-C" flag for 2.0 compatibility.  Used
 * unsigned compares on Perkin-Elmer.  Fixed foreground check.
 *
 * Revision 2.7   84/11/16  19:35:39  ames!jaw
 * Cache common hash codes based on input statistics; this improves
 * performance for low-density raster images.  Pass on #ifdef bundle
 * from Turkowski.
 *
 * Revision 2.6   84/11/05  19:18:21  ames!jaw
 * Vary size of hash tables to reduce time for small files.
 * Tune PDP-11 hash function.
 *
 * Revision 2.5   84/10/30  20:15:14  ames!jaw
 * Junk chaining; replace with the simpler (and, on the VAX, faster)
 * double hashing, discussed within.  Make block compression standard.
 *
 * Revision 2.4   84/10/16  11:11:11  ames!jaw
 * Introduce adaptive reset for block compression, to boost the rate
 * another several percent.  (See mailing list notes.)
 *
 * Revision 2.3   84/09/22  22:00:00  petsd!joe
 * Implemented "-B" block compress.  Implemented REVERSE sorting of tab_next.
 * Bug fix for last bits.  Changed fwrite to putchar loop everywhere.
 *
 * Revision 2.2   84/09/18  14:12:21  ames!jaw
 * Fold in news changes, small machine typedef from thomas,
 * #ifdef interdata from joe.
 *
 * Revision 2.1   84/09/10  12:34:56  ames!jaw
 * Configured fast table lookup for 32-bit machines.
 * This cuts user time in half for b <= FBITS, and is useful for news batching
 * from VAX to PDP sites.  Also sped up decompress() [fwrite->putc] and
 * added signal catcher [plus beef in writeerr()] to delete effluvia.
 *
 * Revision 2.0   84/08/28  22:00:00  petsd!joe
 * Add check for foreground before prompting user.  Insert maxbits into
 * compressed file.  Force file being uncompressed to end with ".Z".
 * Added "-c" flag and "zcat".  Prepared for release.
 *
 * Revision 1.10  84/08/24  18:28:00  turtlevax!ken
 * Will only compress regular files (no directories), added a magic number
 * header (plus an undocumented -n flag to handle old files without headers),
 * added -f flag to force overwriting of possibly existing destination file,
 * otherwise the user is prompted for a response.  Will tack on a .Z to a
 * filename if it doesn't have one when decompressing.  Will only replace
 * file if it was compressed.
 *
 * Revision 1.9  84/08/16  17:28:00  turtlevax!ken
 * Removed scanargs(), getopt(), added .Z extension and unlimited number of
 * filenames to compress.  Flags may be clustered (-Ddvb12) or separated
 * (-D -d -v -b 12), or combination thereof.  Modes and other status is
 * copied with copystat().  -O bug for 4.2 seems to have disappeared with
 * 1.8.
 *
 * Revision 1.8  84/08/09  23:15:00  joe
 * Made it compatible with vax version, installed jim's fixes/enhancements
 *
 * Revision 1.6  84/08/01  22:08:00  joe
 * Sped up algorithm significantly by sorting the compress chain.
 *
 * Revision 1.5  84/07/13  13:11:00  srd
 * Added C version of vax asm routines.  Changed structure to arrays to
 * save much memory.  Do unsigned compares where possible (faster on
 * Perkin-Elmer)
 *
 * Revision 1.4  84/07/05  03:11:11  thomas
 * Clean up the code a little and lint it.  (Lint complains about all
 * the regs used in the asm, but I'm not going to "fix" this.)
 *
 * Revision 1.3  84/07/05  02:06:54  thomas
 * Minor fixes.
 *
 * Revision 1.2  84/07/05  00:27:27  thomas
 * Add variable bit length output.
 *
 */
#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#define ARGVAL() (*++(*argv) || (--argc && *++argv))

static int n_bits;                        /* number of bits/code */
static int maxbits = BITS;                /* user settable max # bits/code */
static code_int maxcode;                  /* maximum code, given n_bits */
static code_int maxmaxcode = (code_int)1 << BITS; /* should NEVER generate this code */
#ifdef COMPATIBLE               /* But wrong! */
# define MAXCODE(n_bits)        ((code_int) 1 << (n_bits) - 1)
#else
# define MAXCODE(n_bits)        (((code_int) 1 << (n_bits)) - 1)
#endif /* COMPATIBLE */

static count_int htab [HSIZE];
static unsigned short codetab [HSIZE];
#define HashTabOf(i)       htab[i]
#define CodeTabOf(i)    codetab[i]

static code_int hsize = HSIZE;                 /* for dynamic table sizing */
static count_int fsize;

/*
 * To save much memory, we overlay the table used by compress() with those
 * used by decompress().  The tab_prefix table is the same size and type
 * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
 * get this from the beginning of htab.  The output stack uses the rest
 * of htab, and contains characters.  There is plenty of room for any
 * possible stack (stack used to be 8000 characters).
 */

#define tab_prefixof(i) CodeTabOf(i)
#define tab_suffixof(i)        ((char_type *)(htab))[i]
#define de_stack               ((char_type *)&tab_suffixof((code_int)1<<BITS))

static code_int free_ent = 0;                  /* first unused entry */
static int exit_stat = 0;

/*
 * block compression parameters -- after all codes are used up,
 * and compression rate changes, start over.
 */
static int clear_flg = 0;

static int offset;
static long int in_count = 1;            /* length of input */
static long int out_count = 0;           /* # of codes output (for debugging) */

/*
 * compress stdin to stdout
 *
 * Algorithm:  use open addressing double hashing (no chaining) on the 
 * prefix code / next character combination.  We do a variant of Knuth's
 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
 * secondary probe.  Here, the modular division first probe is gives way
 * to a faster exclusive-or manipulation.  Also do block compression with
 * an adaptive reset, whereby the code table is cleared when the compression
 * ratio decreases, but after the table fills.  The variable-length output
 * codes are re-sized at this point, and a special CLEAR code is generated
 * for the decompressor.  Late addition:  construct the table according to
 * file size for noticeable speed improvement on small files.  Please direct
 * questions about this implementation to ames!jaw.
 */

static int g_init_bits;
static FILE *g_outfile;

static int ClearCode;
static int EOFCode;

compress( init_bits, outfile, ReadValue )
int init_bits;
FILE *outfile;
ifunptr ReadValue;
{
    register long fcode;
    register code_int i = 0;
    register int c;
    register code_int ent;
    register code_int disp;
    register code_int hsize_reg;
    register int hshift;

    /*
     * Set up the globals:  g_init_bits - initial number of bits
     *                      g_outfile   - pointer to output file
     */
    g_init_bits = init_bits;
    g_outfile = outfile;

    /*
     * Set up the necessary values
     */
    offset = 0;
    out_count = 0;
    clear_flg = 0;
    in_count = 1;
    maxcode = MAXCODE(n_bits = g_init_bits);

    ClearCode = (1 << (init_bits - 1));
    EOFCode = ClearCode + 1;
    free_ent = ClearCode + 2;

    char_init();
    
    ent = GIFNextPixel( ReadValue );

    hshift = 0;
    for ( fcode = (long) hsize;  fcode < 65536L; fcode *= 2L )
        hshift++;
    hshift = 8 - hshift;                /* set hash code range bound */

    hsize_reg = hsize;
    cl_hash( (count_int) hsize_reg);            /* clear hash table */

    output( (code_int)ClearCode );
    
#ifdef SIGNED_COMPARE_SLOW
    while ( (c = GIFNextPixel( ReadValue )) != (unsigned) EOF ) {
#else
    while ( (c = GIFNextPixel( ReadValue )) != EOF ) {
#endif

        in_count++;

        fcode = (long) (((long) c << maxbits) + ent);
        i = (((code_int)c << hshift) ^ ent);    /* xor hashing */

        if ( HashTabOf (i) == fcode ) {
            ent = CodeTabOf (i);
            continue;
        } else if ( (long)HashTabOf (i) < 0 )      /* empty slot */
            goto nomatch;
        disp = hsize_reg - i;           /* secondary hash (after G. Knott) */
        if ( i == 0 )
            disp = 1;
probe:
        if ( (i -= disp) < 0 )
            i += hsize_reg;

        if ( HashTabOf (i) == fcode ) {
            ent = CodeTabOf (i);
            continue;
        }
        if ( (long)HashTabOf (i) > 0 ) 
            goto probe;
nomatch:
        output ( (code_int) ent );
        out_count++;
        ent = c;
#ifdef SIGNED_COMPARE_SLOW
        if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
#else
        if ( free_ent < maxmaxcode ) {
#endif
            CodeTabOf (i) = free_ent++; /* code -> hashtable */
            HashTabOf (i) = fcode;
        } else
		cl_block();
    }
    /*
     * Put out the final code.
     */
    output( (code_int)ent );
    out_count++;
    output( (code_int) EOFCode );

    return;
}

/*****************************************************************
 * TAG( output )
 *
 * Output the given code.
 * Inputs:
 *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
 *              that n_bits =< (long)wordsize - 1.
 * Outputs:
 *      Outputs code to the file.
 * Assumptions:
 *      Chars are 8 bits long.
 * Algorithm:
 *      Maintain a BITS character long buffer (so that 8 codes will
 * fit in it exactly).  Use the VAX insv instruction to insert each
 * code in turn.  When the buffer fills up empty it and start over.
 */

static unsigned long cur_accum = 0;
static int  cur_bits = 0;

static
unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
                                  0x001F, 0x003F, 0x007F, 0x00FF,
                                  0x01FF, 0x03FF, 0x07FF, 0x0FFF,
                                  0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };

static
output( code )
code_int  code;
{
    cur_accum &= masks[ cur_bits ];

    if( cur_bits > 0 )
    	cur_accum |= ((long)code << cur_bits);
    else
        cur_accum = code;
	
    cur_bits += n_bits;

    while( cur_bits >= 8 ) {
    	char_out( (unsigned int)(cur_accum & 0xff) );
	cur_accum >>= 8;
	cur_bits -= 8;
    }

    /*
     * If the next entry is going to be too big for the code size,
     * then increase it, if possible.
     */
   if ( free_ent > maxcode || clear_flg ) {

            if( clear_flg ) {
	    
                maxcode = MAXCODE (n_bits = g_init_bits);
                clear_flg = 0;
		
            } else {
	    
                n_bits++;
                if ( n_bits == maxbits )
                    maxcode = maxmaxcode;
                else
                    maxcode = MAXCODE(n_bits);
            }
        }
	
    if( code == EOFCode ) {
        /*
         * At EOF, write the rest of the buffer.
         */
        while( cur_bits > 0 ) {
    	        char_out( (unsigned int)(cur_accum & 0xff) );
	        cur_accum >>= 8;
	        cur_bits -= 8;
        }

	flush_char();
	
        fflush( g_outfile );

	if( ferror( g_outfile ) )
                writeerr();
    }
}

/*
 * Clear out the hash table
 */
static
cl_block ()             /* table clear for block compress */
{

        cl_hash ( (count_int) hsize );
        free_ent = ClearCode + 2;
        clear_flg = 1;

        output( (code_int)ClearCode );
}

static
cl_hash(hsize)          /* reset code table */
register count_int hsize;
{

        register count_int *htab_p = htab+hsize;

        register long i;
        register long m1 = -1;

        i = hsize - 16;
        do {                            /* might use Sys V memset(3) here */
                *(htab_p-16) = m1;
                *(htab_p-15) = m1;
                *(htab_p-14) = m1;
                *(htab_p-13) = m1;
                *(htab_p-12) = m1;
                *(htab_p-11) = m1;
                *(htab_p-10) = m1;
                *(htab_p-9) = m1;
                *(htab_p-8) = m1;
                *(htab_p-7) = m1;
                *(htab_p-6) = m1;
                *(htab_p-5) = m1;
                *(htab_p-4) = m1;
                *(htab_p-3) = m1;
                *(htab_p-2) = m1;
                *(htab_p-1) = m1;
                htab_p -= 16;
        } while ((i -= 16) >= 0);

	for ( i += 16; i > 0; i-- )
                *--htab_p = m1;
}

static
writeerr()
{
	printf( "error writing output file\n" );
	exit(1);
}

/******************************************************************************
 *
 * GIF Specific routines
 *
 ******************************************************************************/

/*
 * Number of characters so far in this 'packet'
 */
static int a_count;

/*
 * Set up the 'byte output' routine
 */
static
char_init()
{
	a_count = 0;
}

/*
 * Define the storage for the packet accumulator
 */
static char accum[ 256 ];

/*
 * Add a character to the end of the current packet, and if it is 254
 * characters, flush the packet to disk.
 */
static
char_out( c )
int c;
{
	accum[ a_count++ ] = c;
	if( a_count >= 254 ) 
		flush_char();
}

/*
 * Flush the packet to disk, and reset the accumulator
 */
static
flush_char()
{
	if( a_count > 0 ) {
		fputc( a_count, g_outfile );
		fwrite( accum, 1, a_count, g_outfile );
		a_count = 0;
	}
}	

/* The End */
@//E*O*F gifcompr.c//
chmod u=rw,g=r,o=r gifcompr.c
 
echo x - gifencod.c
sed 's/^@//' > "gifencod.c" <<'@//E*O*F gifencod.c//'

/*****************************************************************************
 *
 * GIFENCODE.C    - GIF Image compression interface
 *
 * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, 
 *	      BitsPerPixel, Red, Green, Blue, GetPixel )
 *
 *****************************************************************************/
 
#include <stdio.h>

/*
 * Pointer to function returning an int
 */
typedef int (* ifunptr)();

#define TRUE 1
#define FALSE 0

static int Width, Height;
static int curx, cury;
static long CountDown;
static int Pass = 0;
static int Interlace;

/*
 * Bump the 'curx' and 'cury' to point to the next pixel
 */
static
BumpPixel()
{
	/*
	 * Bump the current X position
	 */
	curx++;

	/*
	 * If we are at the end of a scan line, set curx back to the beginning
	 * If we are interlaced, bump the cury to the appropriate spot,
	 * otherwise, just increment it.
	 */
	if( curx == Width ) {
		curx = 0;

	        if( !Interlace ) 
			cury++;
		else {
		     switch( Pass ) {
	     
	               case 0:
        	          cury += 8;
                	  if( cury >= Height ) {
		  		Pass++;
				cury = 4;
		  	  }
                          break;
		  
	               case 1:
        	          cury += 8;
                	  if( cury >= Height ) {
		  		Pass++;
				cury = 2;
		  	  }
			  break;
		  
	               case 2:
	                  cury += 4;
	                  if( cury >= Height ) {
	                     Pass++;
	                     cury = 1;
	                  }
	                  break;
			  
	               case 3:
	                  cury += 2;
	                  break;
			}
		}
	}
}

/*
 * Return the next pixel from the image
 */
GIFNextPixel( getpixel )
ifunptr getpixel;
{
	int r;

	if( CountDown == 0 )
		return EOF;

	CountDown--;

	r = ( * getpixel )( curx, cury );

	BumpPixel();

	return r;
}

/* public */

GIFEncode( FName, GWidth, GHeight, GInterlace, Background, 
	   BitsPerPixel, Red, Green, Blue, GetPixel )
	 
char *FName;
int GWidth, GHeight;
int GInterlace;
int Background;
int BitsPerPixel;
int Red[], Green[], Blue[];
ifunptr GetPixel;

{
	FILE *fp;
	int B;
	int RWidth, RHeight;
	int LeftOfs, TopOfs;
	int Resolution;
	int ColorMapSize;
	int InitCodeSize;
	int i;

	Interlace = GInterlace;
	
	ColorMapSize = 1 << BitsPerPixel;
	
	RWidth = Width = GWidth;
	RHeight = Height = GHeight;
	LeftOfs = TopOfs = 0;
	
	Resolution = BitsPerPixel;

	/*
	 * Calculate number of bits we are expecting
	 */
	CountDown = (long)Width * (long)Height;

	/*
	 * Indicate which pass we are on (if interlace)
	 */
	Pass = 0;

	/*
	 * The initial code size
	 */
	if( BitsPerPixel <= 1 )
		InitCodeSize = 2;
	else
		InitCodeSize = BitsPerPixel;

	/*
	 * Set up the current x and y position
	 */
	curx = cury = 0;

	/*
	 * Open the GIF file for binary write
	 */
#ifdef unix
	fp = fopen( FName, "wb" );
#else
	fp = fopen( FName, "wb" );
#endif

	if( fp == (FILE *)0 ) {
		printf( "error: could not open output file\n" );
		exit(1);
	}

	/*
	 * Write the Magic header
	 */
	fwrite( "GIF87a", 1, 6, fp );

	/*
	 * Write out the screen width and height
	 */
	Putword( RWidth, fp );
	Putword( RHeight, fp );

	/*
	 * Indicate that there is a global colour map
	 */
	B = 0x80;	/* Yes, there is a color map */

	/*
	 * OR in the resolution
	 */
	B |= (Resolution - 1) << 5;

	/*
	 * OR in the Bits per Pixel
	 */
	B |= (BitsPerPixel - 1);

	/*
	 * Write it out
	 */
	fputc( B, fp );

	/*
	 * Write out the Background colour
	 */
	fputc( Background, fp );

	/*
	 * Byte of 0's (future expansion)
	 */
	fputc( 0, fp );

	/*
	 * Write out the Global Colour Map
	 */
     	for( i=0; i<ColorMapSize; i++ ) {
		fputc( Red[i], fp );
		fputc( Green[i], fp );
		fputc( Blue[i], fp );
	}

	/*
	 * Write an Image separator
	 */
	fputc( ',', fp );

	/*
	 * Write the Image header
	 */

	Putword( LeftOfs, fp );
	Putword( TopOfs, fp );
	Putword( Width, fp );
	Putword( Height, fp );

	/*
	 * Write out whether or not the image is interlaced
	 */
	if( Interlace )
		fputc( 0x40, fp );
	else
		fputc( 0x00, fp );

	/*
	 * Write out the initial code size
	 */
	fputc( InitCodeSize, fp );

	/*
	 * Go and actually compress the data
	 */
	compress( InitCodeSize+1, fp, GetPixel );

	/*
	 * Write out a Zero-length packet (to end the series)
	 */
	fputc( 0, fp );

	/*
	 * Write the GIF file terminator
	 */
	fputc( ';', fp );

	/*
	 * And close the file
	 */
	fclose( fp );
	
}

/*
 * Write out a word to the GIF file
 */
static
Putword( w, fp )
int w;
FILE *fp;
{
	fputc( w & 0xff, fp );
	fputc( (w / 256) & 0xff, fp );
}

@//E*O*F gifencod.c//
chmod u=rw,g=r,o=r gifencod.c
 
echo x - qrt2gif.c
sed 's/^@//' > "qrt2gif.c" <<'@//E*O*F qrt2gif.c//'

/****************************************************************
 *
 * QRT2GIF - QRT Ray Trace Image to GIF Conversion
 *
 * Written by David Rowley, Copyright (C) 1989 by David Rowley
 * Michael's copyright message goes ditto for me...
 *
 * NOTE:  This utility assumes that the .RAW file was written out
 *        in INTEL form (on both INTEL and NON-INTEL machines).
 *        QRT writes the xres and yres to the file in this format
 *        always, but the format of the line number in each scan line
 *        was machine dependant, so I modified my version of QRT to
 *        ALWAYS write the line number in Intel format (low byte, 
 *        high byte).  This allows me to ship .RAW files from
 *        machine to machine without worrying about in which format they
 *        were created.
 *
 * Many thanks to Steve Koren, for writing QRT and giving us something
 * to play with, and to Michael Mauldin, for his Heckbert Median Cut
 * code. (The GIF stuff is mine :-))
 *
 * Derived from:
 *
 * fbquant.c: FBM Library 0.83 (Alpha Test)  22-Feb-89  Michael Mauldin
 *
 * Copyright (C) 1989 by Michael Mauldin.  Permission is granted to
 * use this file in whole or in part provided that you do not sell it
 * for profit and that this copyright notice is retained unchanged.
 *
 * fbquant: Convert an RGB color image to mapped color format (color
 *	    quantization step).  Floyd-Steinberg dithering is used
 *	    to reduce color banding.  The quantization used is a
 *	    modification of Heckbert's median cut.
 *
 ****************************************************************/

# include <stdio.h>
#include "bitmap.h"
extern char *malloc();

#ifdef ATARI

/* Need lots of stack -- change to suit your compiler */
long _stksize = 50000L;
/* define RINDEX as one of 'rindex' or 'strrchr', whichever your compiler
 * supports */
#define RINDEX rindex

#else

#define RINDEX strrchr
unsigned _STACK = 50000;

#endif

int cmp_red(), cmp_grn(), cmp_blu(), cmp_cmap();

# define RD 0
# define GR 1
# define BL 2

# define MAXSHRT 32767

/* Masks used to manipulate RGB values */
unsigned int REDMASK;
unsigned int REDSHFT;
unsigned int GRNMASK;
unsigned int GRNSHFT;
unsigned int BLUMASK;
unsigned int BLUSHFT;
unsigned int CUBITS;
unsigned int CUBIGN;
unsigned int CUBSIZ;

/* HARD - CODED  --  Change these values if you want greater color
 * resolution (ie. more bits of RGB used in the sampling)
 */
#define CUBSID 16	/* 2 ^ max( 4, 4, 4 ) */
#define CUBSIZ 4096	/* 2 ^ (4 + 4 + 4) */

char *
safe_malloc( s )
unsigned int s;
{
	char *ptr;

	ptr = (char *)malloc( s );
	if( ptr == (char *)0 ) {
		printf( "error: allocating memory\n" );
		exit(1);
	}
	return ptr;
}

/*
 * Set up the masks for the given bits of R, G and B
 */
calc_bits( r, g, b )
int r, g, b;
{
	r = g = b = 4;

	BLUSHFT = 0;
	GRNSHFT = g;
	REDSHFT = g+b;

	BLUMASK = (1 << b) - 1;
	GRNMASK = (1 << g) - 1;
	GRNMASK <<= b;
	REDMASK = (1 << r) - 1;
	REDMASK <<= (b+g);

	CUBITS = r;
	CUBIGN = 8 - CUBITS;

}

# define GETR(X) (((X) & REDMASK) >> REDSHFT)
# define GETG(X) (((X) & GRNMASK) >> GRNSHFT)
# define GETB(X)  ((X) & BLUMASK)

# define CLRINDEX(R,G,B)			\
	(((R) << REDSHFT) & REDMASK |		\
	 ((G) << GRNSHFT) & GRNMASK |		\
	 ((B)  & BLUMASK))

# define CLRINDEX8(R,G,B)			\
	(((R) << (REDSHFT-CUBIGN)) & REDMASK |	\
	 ((G) << (GRNSHFT-CUBIGN)) & GRNMASK |	\
	 ((B) >> (CUBIGN))  & BLUMASK)

# define GETR8(X) (((X) & REDMASK) >> (REDSHFT-CUBIGN))
# define GETG8(X) (((X) & GRNMASK) >> (GRNSHFT-CUBIGN))
# define GETB8(X) (((X) & BLUMASK) << CUBIGN)

typedef struct cstruct {
	unsigned char rd, gr, bl, indx;
} COLOR;

COLOR *cmap = NULL;

typedef struct pix_struct {
	short cnt;
	short color;
} PIXEL;

int debug=0, verbose=0, colors=256, showcolor=0;

unsigned int ReadInt( fp )
FILE *fp;
{
	unsigned char low;
	unsigned char high;

	low = (unsigned char)(fgetc(fp));
	high = (unsigned char)(fgetc(fp));
	return high * 256 + low;
}

/****************************************************************
 * main
 ****************************************************************/

main (argc, argv)
char *argv[];
{
  int *hist[CUBSIZ];
  char *str;
  char *fname;
  int   floyd = 0;
  int   have_bw = 0;
  int   interlace = 0;
  int   xscale = 1, yscale = 1;

  printf( "\n" );
  printf( "QRT2GIF v1.0  (c) March 1st, 1989 by David Rowley\n" );
  printf( "12 Bit RGB image conversion using modified Heckbert median cut\n" );
  printf( "for use with the QRT ray tracing package (v1.4)\n" );
  printf( "\n" );
  
  calc_bits( 4, 4, 4 );

  if( argc <= 1 ) {
	printf( "USAGE: qrt2gif [options] input.raw\n" );
	printf( "\n" );
	printf( "   -cNNN      produce a GIF image with NNN colors\n" );
	printf( "              (default is 256)\n" );
	printf( "\n" );
	printf( "   -b         Ensure that pure Black and White are\n" );
	printf( "              in the palette used (sometimes useful)\n" );
	printf( "              For example, use -c2 -b -f to create\n" );
	printf( "              a black and white dithered image\n" );
	printf( "\n" );
	printf( "   -xNNN      scale the width up by a factor of NNN\n" );
	printf( "   -yNNN      scale the height up by a factor of NNN\n" );
	printf( "\n" );
	printf( "   -f         use floyd-steinberg dithering\n" );
	printf( "              (default is to just map to closest color\n" );
	printf( "\n" );
	printf( "   -i         make the GIF image interlaced\n" );
	printf( "\n" );
	exit(1);
  }
  	
  /* Get the options */
  while ( argc > 1 ) {
     str = argv[1];
     if( str[0] == '-' ) {
	switch( str[1] ) {
		case 'c':
		case 'C':
			colors = atoi( str+2 );
			break;
		case 'f':
		case 'F':
			floyd = 1;
			break;
		case 'b':
		case 'B':
			have_bw = 1;
			break;
		case 'i':
		case 'I':
			interlace = 1;
			break;
		case 'x':
		case 'X':
			xscale = atoi( str+2 );
			break;
		case 'y':
		case 'Y':
			yscale = atoi( str+2 );
			break;

	}
     } else
	fname = argv[1];
     argv++;
     argc--;
  }

  /* Allocate space for color map */
  cmap = (COLOR *) safe_malloc ((unsigned) colors * sizeof (COLOR));

  /* Build a histogram of color distribution from the input */
  sample_image (fname, hist);

  /* Select 'colors' different colors for the colormap */  
  build_colormap (hist, cmap, colors, have_bw);
  
  /* Use Floyd-Steinberg error dispersion to quantize using the new cmap */
  clr_quantize ( fname, cmap, colors, floyd, interlace, xscale, yscale);
  
}

/****************************************************************
 * sample_image:
 ****************************************************************/

unsigned char r_vals[1024], g_vals[1024], b_vals[1024];

sample_image (fname, hist)
char *fname;
int *hist;
{ register int i;
  FILE *fp;
  int xres, yres;
  long used = 0;
  int lineno;
 
   fp = fopen( fname, "rb" );
   
   if( fp == (FILE *)0 )
   	printf( "error: could not open %s\n", fname ), exit(1);
   
   xres = ReadInt( fp );
   yres = ReadInt( fp );
   
  /* Clear the histogram */
  for (i=0; i<CUBSIZ; i++) hist[i] = 0;

   printf( "Sampling Bitmap (Resolution: %d by %d)\n", xres, yres );
   
   for( ;; ) {
   	lineno = ReadInt( fp );
   	if( lineno < 0 || lineno > yres )
   		break;
   
   	fread( r_vals, 1, xres, fp );
   	fread( g_vals, 1, xres, fp );
   	fread( b_vals, 1, xres, fp );
   
   	for( i=0; i<xres; i++ ) {
                if (++hist[ CLRINDEX8 (r_vals[i]*4,g_vals[i]*4,b_vals[i]*4) ] 
			== 1)
			 used++;
   	}
   
   	if( feof( fp ) )
   		break;
   }
   
   fclose( fp );
   
}

/****************************************************************
 * build_colormap:
 ****************************************************************/

build_colormap (hist, cmap, colors, have_bw)
int *hist;
COLOR *cmap;
int colors;
int have_bw;
{ register int i, k;
  static PIXEL box[CUBSIZ];
  register PIXEL *b;
  int used=0;
  int cols_to_pick;

  /*
   * If we want to include pure black and white in the palette, then
   * we only have to choose 'colors' - 2.
   */
  if( have_bw )
  	cols_to_pick = colors - 2;
  else
  	cols_to_pick = colors;

  /* Build the first box, encompassing all pixels */  
  for (b=box, i=0; i<CUBSIZ; i++)
  { b->color = i;
    k = hist[i];
    b->cnt = (k > MAXSHRT) ? MAXSHRT : k;
    b++;
  }
  
  /* Move all non-zero count colors to the front of the list */
  for (i=0, used=0; i<CUBSIZ; i++)
  { if (box[i].cnt > 0) box[used++] = box[i]; }

  printf( "%d colors used out of a possible %ld\n", (int)used, (long)CUBSIZ );

  /*-------- Special case if we didnt need all colors --------*/
  if (used <= cols_to_pick)
  {
    /* Copy the colors actually found */
    printf( "since only %d colors were used - no need to reduce palette\n", 
    			(int)used );
    for (i=0; i<used; i++)
    { cmap[i].rd = GETR8 (box[i].color);
      cmap[i].gr = GETG8 (box[i].color);
      cmap[i].bl = GETB8 (box[i].color);
    }

    /* Set the rest to WHITE */
    for (; i<cols_to_pick; i++)
    { cmap[i].rd = 255;
      cmap[i].gr = 255;
      cmap[i].bl = 255;
    }
  }
  
  /*-------- Recursively split the color space and assign colors --------*/
  else
  { split_box (box, used, 0, cols_to_pick, cmap); }
  
  /*----------------------------------------------------------------
   * Now arrange the colors in the desired order.  Sun convention is that
   * color 0 is white and color n-1 is black.  We put the rest of the map
   * in lexicographically sorted r,g,b order from 1 to n-2.
   */

  /* Now sort 1..n-2 according to desired ordering */
  qsort (cmap, cols_to_pick, sizeof (* cmap), cmp_cmap);

  /* Make first color white and last color black */
  if( have_bw ) {
  	cmap[colors-2].rd = cmap[colors-2].gr = cmap[colors-2].bl = 255;
  	cmap[colors-1].rd = 0;
	cmap[colors-1].gr = 0;
  	cmap[colors-1].bl = 0;
  }

  /* Set the output indices */
  for (i=0; i<colors; i++) { cmap[i].indx = i; }
}

/****************************************************************
 * split_box: Basic recursive part of Heckberts adaptive partitioning
 *	      algorithm.
 ****************************************************************/

split_box (box, boxlen, clr, numclr, cmap)
PIXEL *box;
int boxlen, clr, numclr;
COLOR *cmap;
{ int maxv[3], minv[3], numv[3];
  int pcnt[3][CUBSID];
  int sbox, snum, split, half, maxdif, dif;
  register PIXEL *top, *bot;
  int topw, botw;
  int red, grn, blu;
  register int i, c;

  /* If numclr exceeds boxlen, we are in trouble */
  if (numclr > boxlen)
  { fprintf (stderr, "boxlen %d is less numclr %d, panic!\n than",
	     boxlen, numclr);
    fflush (stderr);
    abort ();
  }

  /* Base case: only one color, assign the average for this cell */
  if (numclr <= 1)
  { red = box_avg_red (box, boxlen);
    grn = box_avg_grn (box, boxlen);
    blu = box_avg_blu (box, boxlen);
    
    /* Map x to x+4, because the histogram maps values to multiples of 8 */
    cmap[clr].rd = red + 4;
    cmap[clr].gr = grn + 4;
    cmap[clr].bl = blu + 4;
    
    if (debug)
    { fprintf (stderr, "\t\tassigning color %d  <%d,%d,%d>  (%d)\n",
	       clr, cmap[clr].rd, cmap[clr].gr, cmap[clr].bl,
	       box_weight (box, boxlen));
    }
    
    return;
  }

  /* Gather statistics about the boxes contents */
  minv[RD] = minv[GR] = minv[BL] = CUBSID;
  maxv[RD] = maxv[GR] = maxv[BL] = 0;
  numv[RD] = numv[GR] = numv[BL] = 0;
  for (i=0; i<CUBSID; i++) { pcnt[RD][i] = pcnt[GR][i] = pcnt[BL][i] = 0; }
  
  for (i=0; i<boxlen; i++)
  { c = box[i].color;
    red = GETR(c); grn = GETG(c); blu = GETB(c);
    
    if (red < minv[RD]) minv[RD] = red;
    if (red > maxv[RD]) maxv[RD] = red;
    if (grn < minv[GR]) minv[GR] = grn;
    if (grn > maxv[GR]) maxv[GR] = grn;
    if (blu < minv[BL]) minv[BL] = blu;
    if (blu > maxv[BL]) maxv[BL] = blu;
    
    if (++pcnt[RD][red] == 1) numv[RD]++;
    if (++pcnt[GR][grn] == 1) numv[GR]++;
    if (++pcnt[BL][blu] == 1) numv[BL]++;
  }

  /* Special case, boxlen = numclr, just assign each box one color */
  if (boxlen == numclr)
  { for (i=0; i<boxlen; i++)
    { split_box (box+i, 1, clr+i, 1, cmap); }
    return;
  }

  /* Pick a dimension to split */
  split = -1; maxdif = -1;

  if ((dif = (maxv[RD] - minv[RD])) > maxdif) { maxdif = dif; split = RD; }
  if ((dif = (maxv[GR] - minv[GR])) > maxdif) { maxdif = dif; split = GR; }
  if ((dif = (maxv[BL] - minv[BL])) > maxdif) { maxdif = dif; split = BL; }
  
  /* Sort along the chosen dimension */
  switch (split)
  { case RD:	qsort (box, boxlen, sizeof (*box), cmp_red); break;
    case GR:	qsort (box, boxlen, sizeof (*box), cmp_grn); break;
    case BL:	qsort (box, boxlen, sizeof (*box), cmp_blu); break;
    default:	fprintf (stderr, "panic in split_box, split = -1\n");
		fflush (stderr); fflush (stdout); abort ();
  }
  
  /* 
   * Split at the median, but make sure there are at least numclr/2
   * different colors on each side of the split, to avoid wasting
   * colors.
   *
   * Note: need to keep in mind that when the box is large, dont split
   *       too close to one edge.
   */
   
  half = numclr / 2;
  top = box;		bot = box + (boxlen-1);
  topw = top->cnt;	botw = bot->cnt;
  
  /* Set top and bot to point to min/max feasible splits */
  while ((top-box)+1 < half)		{ top++; topw += top->cnt; }
  while ((boxlen-(bot-box)) < half)	{ bot--; botw += bot->cnt; }

  /* Move top and bottom towards each other 1/8 of remaining distance */
  c = (bot-top) / 8;
  for (i=0; i<c; i++)			{ top++; topw += top->cnt; }
  for (i=0; i<c; i++)			{ bot--; botw += bot->cnt; }

  /* Now search for median */
  while (top < bot)
  { if (topw < botw)			{ top++; topw += top->cnt; }
    else				{ bot--; botw += bot->cnt; }
  }

  /* Decide which half gets the midpoint */
  if (topw > botw)			/* Median wants to go with top */
  { sbox = (top-box) + 1;
    snum = numclr - half;
  }
  else					/* Median wants to go with bottom */
  { sbox = (top - box);
    snum = half;
  }
  
  /* Handle boundary conditions with number of colors vs box size */
  if (sbox == 0) sbox++;
  else if (sbox == boxlen) sbox--;

  while (snum > sbox) snum--;
  while (numclr-snum > boxlen-sbox) snum++;

# ifndef OPTIMIZE
  /* Check for boundary condition errors */
  if (snum <= 0 || snum >= numclr)
  { fprintf (stderr, "panic, using zero colors for box\n");
    fflush (stderr);
    abort ();
  }

  if (boxlen-sbox < numclr-snum)
  { fprintf (stderr, "panic, about to used %d boxes for %d colors\n",
	     boxlen-sbox, numclr-snum);
    fflush (stderr);
    abort ();
  }

  if (sbox < snum)
  { fprintf (stderr, "panic, about to used %d boxes for %d colors\n",
	     sbox, snum);
    fflush (stderr);
    abort ();
  }
# endif

  if (debug)
  { int count = numclr, depth = 8;
    while (count > 0) { depth--; count /= 2; }
    for (i=0; i<depth; i++) fprintf (stderr, "  ");
    
    fprintf (stderr, "box [%d..%d|%d] r(%d,%d,%d) g(%d,%d,%d) b(%d,%d,%d) =>",
	     0, boxlen-1, numclr,
	     minv[RD], maxv[RD], numv[RD],
	     minv[GR], maxv[GR], numv[GR],
	     minv[BL], maxv[BL], numv[BL]);
    fprintf (stderr, " %c [%d..%d|%d] [%d..%d|%d]\n",
	     "RGB"[split], 0, sbox-1, snum, sbox, boxlen-1, numclr-snum);
  }

  /* Now recurse and split each sub-box */
  split_box (box,      sbox,          clr,      snum,        cmap);
  split_box (box+sbox, boxlen - sbox, clr+snum, numclr-snum, cmap);
}

/****************************************************************
 * box_weight: Determine the total count of a box
 ****************************************************************/

box_weight (box, boxlen)
register PIXEL *box;
register int boxlen;
{ register int sum = 0, i;

  for (i=0; i<boxlen; i++)
  { sum += box[i].cnt; }
  
  return (sum);
}

/****************************************************************
 * box_avg_red: Determine the average red value [0..255] of a box
 ****************************************************************/

box_avg_red (box, boxlen)
register PIXEL *box;
register int boxlen;
{ register int sum = 0, n = 0, i;

  for (i=0; i<boxlen; i++)
  { sum += GETR8(box[i].color); n++; }
  
  return (sum / n);
}

/****************************************************************
 * box_avg_grn: Determine the average grn value [0..255] of a box
 ****************************************************************/

box_avg_grn (box, boxlen)
register PIXEL *box;
register int boxlen;
{ register int sum = 0, n = 0, i;

  for (i=0; i<boxlen; i++)
  { sum += GETG8(box[i].color); n++; }
  
  return (sum / n);
}

/****************************************************************
 * box_avg_blu: Determine the average blu value [0..255] of a box
 ****************************************************************/

box_avg_blu (box, boxlen)
register PIXEL *box;
register int boxlen;
{ register int sum = 0, n = 0, i;

  for (i=0; i<boxlen; i++)
  { sum += GETB8(box[i].color); n++; }
  
  return (sum / n);
}


/****************************************************************
 * sort by increasing red ( then green and blue )
 ****************************************************************/

cmp_red (a, b)
PIXEL *a, *b;
{ register r;

  if (r = GETR(a->color) - GETR(b->color))
  { return (r); }
  
  if (r = GETG(a->color) - GETG(b->color))
  { return (r); }

  if (r = GETB(a->color) - GETB(b->color))
  { return (r); }
  
  return (0);
}

/****************************************************************
 * sort by increasing green ( then blue and red )
 ****************************************************************/

cmp_grn (a, b)
PIXEL *a, *b;
{ register r;

  if (r = GETG(a->color) - GETG(b->color))
  { return (r); }

  if (r = GETB(a->color) - GETB(b->color))
  { return (r); }
  
  if (r = GETR(a->color) - GETR(b->color))
  { return (r); }
  
  return (0);
}

/****************************************************************
 * sort by increasing blue ( then red and green )
 ****************************************************************/

cmp_blu (a, b)
PIXEL *a, *b;
{ register r;

  if (r = GETB(a->color) - GETB(b->color))
  { return (r); }
  
  if (r = GETR(a->color) - GETR(b->color))
  { return (r); }
  
  if (r = GETG(a->color) - GETG(b->color))
  { return (r); }

  return (0);
}

/****************************************************************
 * clr_quantize: Do Floyd Steinberg quantizing on the image
 ****************************************************************/

clr_quantize (fname, cmap, colors, floyd, interlace, xscale, yscale )
char *fname;
COLOR *cmap;
int colors;
int floyd;
int interlace;
int xscale, yscale;
{
  register int i, j;
  Bitmap *bit;
  int xres, yres;
  int ii, jj;
  FILE *fp;
  int idx;
  int lineno;
  int **cerr;
  int **lerr;
  int rd, gr, bl;
  int rderr, grerr, blerr;
  int **terr;
  char gif_name[80];
  extern char *RINDEX();
  char *ptr;
  
	strcpy( gif_name, fname );
	ptr = RINDEX( gif_name, '.' );
	if( ptr )
		strcpy( ptr, ".gif" );
	else
		strcat( gif_name, ".gif" );
 		
	fp = fopen( fname, "rb" );

	if( fp == (FILE *)0 )
		printf( "error: could not open %s\n", fname ), exit(1);

	xres = ReadInt( fp );
	yres = ReadInt( fp );

	if( xscale > 1 || yscale > 1 )
		printf( "Scaling up to %d by %d\n",
				xres * xscale, yres * yscale );

	xres *= xscale;
	yres *= yscale;

	bit = Bitmap_Create( xres, yres, colors );
	if( bit == (Bitmap *)0 )
		printf( "error: could not allocate bitmap\n" ), exit(1);

	for( i=0; i<colors; i++ ) {
		bit->pal[i].red = cmap[i].rd;
		bit->pal[i].green = cmap[i].gr;
		bit->pal[i].blue = cmap[i].bl;
	}

	if( floyd ) {
  		cerr = (int **) safe_malloc (3 * sizeof (int *));
  		lerr = (int **) safe_malloc (3 * sizeof (int *));
  		cerr[RD] = (int *) safe_malloc ((xres + 2) * sizeof (int));
  		cerr[GR] = (int *) safe_malloc ((xres + 2) * sizeof (int));
  		cerr[BL] = (int *) safe_malloc ((xres + 2) * sizeof (int));
  		lerr[RD] = (int *) safe_malloc ((xres + 2) * sizeof (int));
  		lerr[GR] = (int *) safe_malloc ((xres + 2) * sizeof (int));
  		lerr[BL] = (int *) safe_malloc ((xres + 2) * sizeof (int));
	}

	init_nearest( cmap, colors );

  	/*-------- Clear error vectors --------*/
  	if( floyd )
  		for (i=0; i<xres+2; i++) {
  			cerr[RD][i] = cerr[GR][i] = cerr[BL][i] = 0;
    			lerr[RD][i] = lerr[GR][i] = lerr[BL][i] = 0;
  		}

  	if( floyd )
  		printf( "Applying Floyd-Steinberg dithering...\n\n" );
  	else
  		printf( "Applying color palette mapping...\n\n" );

	for( ;; ) {
		lineno = ReadInt( fp );
		if( lineno < 0 || lineno > yres )
			break;

		if( (lineno+1) % 50 == 0 )
			printf( "%d", (lineno+1) );
		printf( "." );
		fflush( stdout );

		fread( r_vals, 1, xres / xscale, fp );
		fread( g_vals, 1, xres / xscale, fp );
		fread( b_vals, 1, xres / xscale, fp );

		for( j=lineno * yscale; j<(lineno+1)*yscale; j++ ) {
		for( i=0; i<xres; i++ ) {

			rd = r_vals[i / xscale] * 4;
			gr = g_vals[i / xscale] * 4;
			bl = b_vals[i / xscale] * 4;

			if( floyd ) {

      			/* Sum up errors using Floyd-Steinberg weights */
      
      rderr= cerr[RD][i] + 5*lerr[RD][i] + 7*lerr[RD][i+1] + 3*lerr[RD][i+2];
      grerr= cerr[GR][i] + 5*lerr[GR][i] + 7*lerr[GR][i+1] + 3*lerr[GR][i+2];
      blerr= cerr[BL][i] + 5*lerr[BL][i] + 7*lerr[BL][i+1] + 3*lerr[BL][i+2];

      				rderr >>= 4;	/* Divide by 16 */
      				grerr >>= 4;	/* Divide by 16 */
      				blerr >>= 4;	/* Divide by 16 */

      				/* Chose nearest color to adjusted RGB value */
      				rd += rderr; gr += grerr; bl += blerr;
			}

      			idx = nearest (rd, gr, bl, cmap, colors);

			if( floyd ) {

      				/* Compute accumulated error for this pixel */
      				cerr[RD][i+1] = rd - cmap[idx].rd;
      				cerr[GR][i+1] = gr - cmap[idx].gr;
      				cerr[BL][i+1] = bl - cmap[idx].bl;
			}
			Bitmap_Plot( bit, i, j, idx );
		}

   
		if( floyd ) { 
			/* Swap error vectors */
    			terr = lerr; lerr = cerr; cerr = terr;
		}

		}
		if( feof( fp ) )
			break;
	}

	fclose( fp );

	printf( "\n\nWriting '%s' (%d x %d x %d)\n", gif_name,
		xres, yres, colors );

        Bitmap_WriteAsGIF( bit, gif_name, interlace );

}
/****************************************************************
 * sort colormap by decreasing red ( then green and blue )
 ****************************************************************/

cmp_cmap (a, b)
register COLOR *a, *b;
{ register int r;

  if (r = (a->rd - b->rd)) { return (r); }
  if (r = (a->gr - b->gr)) { return (r); }
  if (r = (a->bl - b->bl)) { return (r); }
  
  return (0);
}

/****************************************************************
 * nearest: Choose nearest color
 ****************************************************************/

int cache[CUBSIZ];

#define SQR(X) 	((X) * (X))

init_nearest( cmap, colors )
COLOR *cmap;
int colors;
{
	int i;

	for( i=0; i<CUBSIZ; i++ )
		cache[i] = -1;
}

calc_entry(i, cmap, colors)
int i;
COLOR *cmap;
int colors;
{ 	register int j;
	long diff, bdiff;
	int idx;
	long tmp;
	int r, g, b;


		r = GETR8( i );
		g = GETG8( i );
		b = GETB8( i );

		bdiff = 100000L;
		for( j=0; j<colors; j++ ) {

			diff =  SQR(cmap[j].rd - r);
			diff += SQR(cmap[j].gr - g);
			diff += SQR(cmap[j].bl - b);

			if( diff < bdiff )
				idx = j, bdiff = diff;
		}
		cache[ i ] = idx;
}

int restrict( c )
int c;
{
	if( c < 0 ) c = 0;
	else if( c > 255 ) c = 255;
	return c;
}

nearest (rd, gr, bl, cmap, colors)
int rd, gr, bl;
COLOR *cmap;
int colors;
{
  int cindx;

  rd = restrict( rd );
  gr = restrict( gr );
  bl = restrict( bl );

  /* Find array index in cache */
  cindx = CLRINDEX8 (rd, gr, bl);
  if( cache[cindx] == -1 )
  	calc_entry( cindx, cmap, colors );
  return cache[ cindx ];
}

@//E*O*F qrt2gif.c//
chmod u=rw,g=r,o=r qrt2gif.c
 
echo x - bitmap.h
sed 's/^@//' > "bitmap.h" <<'@//E*O*F bitmap.h//'

#ifndef FILE
#include <stdio.h>
#endif

#ifndef TRUE
#define TRUE	1
#define FALSE	0
#endif

#define NIL	((char *)0)

struct PAL_ENTRY {
	unsigned int red, green, blue;
	};
	
struct BITMAP {
	int BWidth;		/* Width of bitmap (pixels)    */
	int BHeight;		/* Height of bitmap (pixels)   */
	int BBits;		/* Number of bits of color     */
	int BBytes;		/* Number of bytes wide        */
	int BFlags;		/* Flags                       */
	int BColors;		/* Number of Colors            */
	FILE *BFile;		/* File pointer (if file)      */
	int BCached;		/* Number of scanline cached   */
	int BNumber;		/* File number of bitmap       */
	struct PAL_ENTRY *pal;  /* Palette entries             */
	char  **lineptr;	/* Bitmap scane-line pointers  */
	};

#define BITMAP_IN_MEMORY	1
#define BITMAP_IN_FILE		2

typedef struct BITMAP Bitmap;

extern Bitmap *Bitmap_Create();
extern Bitmap *Bitmap_Copy();
extern Bitmap *Bitmap_Trim();
extern Bitmap *Bitmap_MonoFloyd();
extern Bitmap *Bitmap_MonoDither();
extern Bitmap *Bitmap_ScaleInto();
extern Bitmap *Bitmap_ReadFromGIF();
extern Bitmap *Font_DrawString();	
@//E*O*F bitmap.h//
chmod u=rw,g=r,o=r bitmap.h
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'

files = bitgen.o bitwgif.o gifcompr.o gifencod.o qrt2gif.o

qrt2gif: $(files)
	cc -o qrt2gif $(files)
	
@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
exit 0

koren@hpfelg.HP.COM (03/07/89)

>  QRT is a great ray-tracer.  It's script language is MUCH better than

Thanks!

>  Anyway -- back to the GIF conversion utility.  The way the QRT source is
>  currently coded, the .RAW file format is machine-specific.  I changed
                                            ~~~~~~~~~~~~~~~~
It shouldn't be (if you're talking about the byte ordering problem, that
is).  That's why I explicitly write the bytes in a certain order:

     fputc(((unsigned char)(lineno&(0xff))),THEWORLD.filept);
     fputc(((unsigned char)(lineno>>8)),    THEWORLD.filept);

As long as they are explicity read back in with:

        line  = ((unsigned int)fgetc(in));
        line += ((unsigned int)(fgetc(in) << 8));

or some such, everything should be machine independent.  I have gotten
into the habit of always doing this explicit byte ordering thing; it
greatly simplifies the task of porting stuff.

          - steve