ftp.nice.ch/pub/next/unix/graphics/urt.3.0.s.tar.gz#/urt.3.0.s/tools/rleldmap.c

This is rleldmap.c in view mode; [Download] [Up]

/*
 * 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.
 */
/* 
 * rleldmap.c - Load a color map into an RLE file.
 * 
 * Author:	Spencer W. Thomas
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Thu Jul 10 1986
 * Copyright (c) 1986, Spencer W. Thomas
 */

#include <stdio.h>
#include <math.h>
#include <rle.h>

#ifdef USE_STDLIB_H
#include <stdlib.h>
#else

#ifdef VOID_STAR
extern void *malloc();
#else
extern char *malloc();
#endif
extern void free();

#endif /* USE_STDLIB_H */

/* Check for negative left shift problem (on 68000, at least) */
#if 2 << -1 == 0
#define NO_NEGATIVE_SHIFT
#endif

void get_rle_map(), linmap(), gammap(), filemap(), mfilemap();
void applymap(), shiftmap();

/*****************************************************************
 * TAG( main )
 * 
 * Usage:
 *	rleldmap [-ab] [-n nchan length] [-s bits] [-l [factor]] [-g gamma]
 *		[-{tf} file] [-m files ...] [-r rlefile]
 *		[-o outputfile] [inputfile]
 * Terminology:
 *	input map:	A color map already in the input RLE file.
 *	applied map:	The color map specified by the arguments that will
 *			be applied (loaded) to the input map, producing
 *			the output map.
 *	output map:	Unless -a or -b is specified, this is equal to the
 *			applied map.
 *	map composition:The output map is applied_map[input_map] if -a
 *			(after) is specified, or input_map[applied_map]
 *			if -b (before).  The maps being composed must either
 *			have the same number of channels, or one of them must
 *			have only one channel.  If an entry in the map being
 *			used as a subscript is larger than the length of the
 *			map being subscripted, it remains unchanged.  The
 *			output map will be the same length as the subscript
 *			map and will have the number of channels that is the
 *			larger of the two.  If the input map is used
 *			as a subscript, it will be downshifted the correct
 *			number of bits to serve as a subscript for the applied
 *			map.  This also applies to the applied map if it is
 *			taken from an RLE file (-r option below).
 *	nchan:		Number of separate lookup tables (channels) making up
 *			the color map.  Default 3 (one for red, one for
 *			green, and one for blue).
 *	length:		Number of entries in each channel of the map.  Default
 *			is 256 (8 bits).
 *	bits:		Size of each color map entry in bits.  Default
 *			log2 length.
 *	range:		Maximum value of a color map entry, equal to
 *			2^bits - 1.
 * Inputs:
 * 	-ab:		Compose the applied map with the input map.
 *			If the input file has no map, this flag has no effect.
 *
 *	-n nchan length Gives the size of the applied map  if it is not 3x256.
 *			length should be a power of two and will be rounded
 *			up if necessary.  If applying the map, nchan must be
 *			either 1 or equal to the number of channels in the
 *			input map.
 *
 *	-s bits		Gives the size in bits of each color map entry.
 *
 *	Note: exactly one of -l, -g, -t, -f, -F, or -r must be specified.
 *
 *	-l factor	Generate a linear applied map with entries equal to
 *			range * max(1.0, factor*(n/(length-1))).  Factor
 *			defaults to 1.0.
 *
 *	-g gamma	Generate an applied map with the given gamma.  The
 *			nth entry (out of length) is
 *			range * (n/(length-1))^gamma
 *
 *	-t file		Read color map entries from a file (t for table).
 *			The values for each channel of a particular entry
 *			follow each other in the file.  (Thus, for an RGB
 *			color map, the file would look like:
 *				red0	green0	blue0
 *				red1	green1	blue1
 *				...	...	...
 *			Line breaks in the input file are irrelevant.
 *
 *	-f file		Reads the applied map from a file, with all the
 *			entries for each channel following each other.  Thus,
 *			the input file would appear as
 *				red0 red1 red2 ...
 *				green0 green1 green2 ...
 *				blue0 blue1 blue2 ...
 *			As above, line breaks are irrelevant.
 *
 *	-m files ...	Read the color map for each channel from a separate
 *			file.  The number of files specified must equal the
 *			number of channels in the applied map.  [Note: the
 *			list of files must be followed by another flag or
 *			by the null flag -- to separate it from the inputfile.
 *
 *	-r rlefile	Read the color map from another RLE file.  In this
 *			case, the nchan, length and bits arguments will be
 *			ignored.
 *
 *	inputfile	The input RLE file.  Defaults to stdin.
 * Outputs:
 * 	-o outputfile	The output RLE file.  Defaults to stdout.
 * Assumptions:
 * 	As stated above.
 * Algorithm:
 * 	Compute the desired map, and either replace the input map with it
 *	or compose the maps as specified above.  Copy the image data from
 *	the input file to the output file.  If stdin is empty (no input at
 *	all, an output RLE file with just a color map will be generated).
 */
void
main( argc, argv )
int argc;
char **argv;
{
    int apply = 0, nflag = 0, nchan = 3, length = 256, range, lbits,
	sflag = 0, bits = 8, lflag = 0, gflag = 0,
	tflag = 0, mflag = 0, rflag = 0, oflag = 0, nmfiles = 0;
    int ochan, olength, olbits;	/* output map parameters */
    double factor = 1.0, gamma = 1.0;
    char * mapfname = NULL, ** mfnames = NULL, * rlefname = NULL,
	* outputfname = NULL, * inputfname = NULL;
    FILE *outfile = stdout;
    rle_map ** imap = NULL, ** omap, ** amap, ** allocmap();
    rle_hdr in_hdr, out_hdr, rle_f_hdr;
    int rle_cnt, rle_err;

    if ( scanargs( argc, argv,
	"% ab%- n%-nchan!dlength!d s%-bits!d l%-factor%F g%-gamma!F \n\
\ttf%-file!s m%-files!*s r%-rlefile!s o%-outputfile!s inputfile%s",
		   &apply, &nflag, &nchan, &length, &sflag, &bits,
		   &lflag, &factor, &gflag, &gamma,
		   &tflag, &mapfname, &mflag, &nmfiles, &mfnames,
		   &rflag, &rlefname, &oflag, &outputfname,
		   &inputfname ) == 0 )
	exit( 1 );		/* bad arguments */

    /* Check for exclusive flag use */
    if ( (lflag != 0) + (gflag != 0) + (tflag != 0) + (mflag != 0) +
	 (rflag != 0) != 1 )
    {
	fprintf(stderr,
		"%s: Must specify exactly one of -l -g -t -f -m -r\n",
		 cmd_name( argv ) );
	exit(-1);
    }

    /* Compute color map parameters */
    if ( rflag )		/* from RLE file? */
    {
	if ( lflag )
	    fprintf(stderr, 
	    "%s: Nchan, length and bits ignored, values from rle file used\n",
		    cmd_name( argv ));
	get_rle_map( &rle_f_hdr, rlefname );
	lbits = rle_f_hdr.cmaplen;
	length = 1 << lbits;
	nchan = rle_f_hdr.ncmap;
	bits = 16;
	/* Just use the rle map */
	amap = allocmap( length, nchan, rle_f_hdr.cmap );
    }
    else
    {
	for ( lbits = 0; length > 1<<lbits; lbits++ )
	    ;			/* Get log2 of length */
	if ( length != 1 << lbits )
	{
	    fprintf( stderr,
		    "%s: Length (%d) rounded up to power of 2 (%d)\n",
		     cmd_name( argv ), length, 1 << lbits );
	    length = 1 << lbits;	/* round length to power of 2 */
	}
	/* Allocate space for the applied map */
	amap = allocmap( nchan, length, NULL );
    }
    range = (1 << bits) - 1;

    /* Compute the requested map */
    if ( lflag )
	linmap( factor, nchan, length, range, amap );
    if ( gflag )
	gammap( gamma, nchan, length, range, amap );
    if ( tflag )
	filemap( tflag, mapfname, nchan, length, amap );
    if ( mflag )
	mfilemap( mfnames, nchan, length, amap );

    /* Open input file and verify header */
    in_hdr.rle_file = rle_open_f(cmd_name( argv ), inputfname, "r");

    for ( rle_cnt = 0;
	  (rle_err = rle_get_setup( &in_hdr )) == RLE_SUCCESS ||
	  rle_err == RLE_EMPTY;
	  rle_cnt++ )
    {
	if ( rle_err == RLE_EMPTY )
	{
	    if ( rle_cnt == 0 )
		apply = 0;	/* can't apply to non-existant map */
	    else
		break;
	    /* Build in_hdr for non-existant image (all zeros works fine) */
	    bzero( &in_hdr, sizeof in_hdr );
	}

	/* If apply flag was given, check for compatibility of color maps */
	if ( in_hdr.ncmap == 0 )
	    apply = 0;		/* Can't apply to non-existent map */
	if ( apply )
	{
	    if ( ! ( nchan == 1 || in_hdr.ncmap == 1 ||
		     nchan == in_hdr.ncmap ) )
	    {
		fprintf( stderr,
 "%s: Nchan (%d) and input color map (%d channels) are not compatible\n",
			 cmd_name( argv ), nchan, in_hdr.ncmap );
		exit(-1);
	    }
	    ochan = (nchan > in_hdr.ncmap) ? nchan : in_hdr.ncmap;

	    if ( apply == 1 )	/* "before", omap[i] = imap[amap[i]] */
	    {
		olength = length;
		olbits = lbits;
	    }
	    else		/* "after", omap[i] = amap[imap[i]] */
	    {
		olength = 1 << in_hdr.cmaplen;
		olbits = in_hdr.cmaplen;
	    }
	
	    /* Get convenient access to the input map */
	    imap = allocmap( in_hdr.ncmap, 1 << in_hdr.cmaplen,
			     in_hdr.cmap );

	    /* Allocate an output map */
	    omap = allocmap( ochan, olength, NULL );

	    /* And do the application */
	    if ( apply == 1 )
		applymap( imap, in_hdr.ncmap, 1 << in_hdr.cmaplen, 16,
			  amap, nchan, length, bits,
			  omap );
	    else
		applymap( amap, nchan, length, bits,
			  imap, in_hdr.ncmap, 1 << in_hdr.cmaplen, 16,
			  omap );
	}
	else
	{
	    /* "Copy" the applied map, and left justify it */
	    omap = allocmap( nchan, length, NULL );
	    bcopy( amap[0], omap[0], nchan * length * sizeof(rle_map) );
	    olength = length;
	    olbits = lbits;
	    ochan = nchan;
	    shiftmap( omap, ochan, olength, bits );
	}

	/* Now, open the output */
	out_hdr = in_hdr;	/* start by copying input parameters */
	out_hdr.ncmap = ochan;
	out_hdr.cmaplen = olbits;
	out_hdr.cmap = omap[0];
	if ( rle_cnt == 0 )
	    outfile = rle_open_f(cmd_name( argv ), outputfname, "w");
	out_hdr.rle_file = outfile;

	rle_addhist( argv, &in_hdr, &out_hdr );

	rle_put_setup( &out_hdr );

	/* Copy the rest of the input to the output */
	if ( rle_err != RLE_EMPTY )
	    rle_cp( &in_hdr, &out_hdr );

	/* Free temp storage. */
	free( omap[0] );
	free( omap );
	if ( apply )
	{
	    free( imap );
	}
	if ( rle_err == RLE_EMPTY )
	{
	    rle_err = RLE_SUCCESS;
	    break;
	}
    }

    /* Check for an error.  EOF or EMPTY is ok if at least one image
     * has been read.  Otherwise, print an error message.
     */
    if ( rle_cnt == 0 || (rle_err != RLE_EOF && rle_err != RLE_EMPTY) )
	rle_get_error( rle_err, cmd_name( argv ), inputfname );

    exit( 0 );
}


/*****************************************************************
 * TAG( allocmap )
 * 
 * Allocate a color map of a given size.
 * Inputs:
 * 	nchan:		Number of channels in the map.
 *	length:		Length of each channel of the map.
 *	cmap:		If non-null, the storage to be used.
 * Outputs:
 * 	Returns a pointer to an array[nchan] of pointers to
 *	array[length] of rle_map.  The rle_map array can also
 *  	be addressed contiguously as return[0][chan*length+i].
 * Assumptions:
 *	If cmap is supplied, it is an array of nchan*length rle_maps.
 * Algorithm:
 *	[None]
 */
rle_map **
allocmap( nchan, length, cmap )
int nchan;
int length;
rle_map * cmap;
{
    rle_map ** map;
    register int i;

    map = (rle_map **)malloc( nchan * sizeof( rle_map * ) );
    if ( cmap == NULL )
	map[0] = (rle_map *)malloc( nchan * length * sizeof( rle_map ) );
    else
	map[0] = cmap;
    for ( i = 1; i < nchan; i++ )
	map[i] = &map[i-1][length];
    return map;
}

/*****************************************************************
 * TAG( shiftmap )
 * 
 * Shift the entries in the color map to left justify them.
 * Inputs:
 * 	map:		The color map.
 *	nchan:		Number of color channels in the map.
 *	length:		Number of entries in each channel.
 *	bits:		Number of bits in each entry.
 * Outputs:
 * 	map:		Left justified map (modified in place).
 * Assumptions:
 * 	map[0] points to a contiguous array of nchan*length rle_maps
 *  	(as set up by allocmap).
 * Algorithm:
 *	[None]
 */
void
shiftmap( map, nchan, length, bits )
rle_map **map;
int nchan, length, bits;
{
    register rle_map * e;
    register int i;
    
    bits = 16 - bits;
    if ( bits == 0 )
	return;			/* no work! */

    for ( i = nchan * length, e = map[0]; i > 0; i--, e++ )
	*e <<= bits;
}

/*****************************************************************
 * TAG( applymap )
 * 
 * Compose two maps: map[submap].
 * Inputs:
 *	map:		Map being "applied".
 *	nchan:		Number of channels in map.
 *	length:		Length of each channel in map.
 *	bits:		Number of bits used in each entry of map.
 * 	submap:		Map used as subscript.
 *	subchan:	Number of channels in submap.
 *	sublen:		Length of submap.
 *	subbits:	Number of bits used in each entry of submap.
 * Outputs:
 *	omap:		Result map.
 *			Omap has max(nchan,subchan) channels, and
 *			sublen entries.
 * Assumptions:
 * 	Yeah.
 * Algorithm:
 * 	Basically, assign omap[c][i] = map[c][submap[c][i]].  If map has
 *	only one channel, get omap[c][i] = map[0][submap[c][i]], and if
 *	submap has only one channel, get omap[c][i] = map[c][submap[0][i]].
 *	Extra complications include shifting submap by the right number of
 *	bits so that it will index the full range of map, and left-justifying
 *	the output map.
 */
void
applymap( map, nchan, length, bits, submap, subchan, sublen, subbits, omap )
int nchan, length, bits, subchan, sublen, subbits;
rle_map **submap;
rle_map **map;
rle_map **omap;
{
    register rle_map * s;		/* pointer into submap */
    register rle_map * o;		/* pointer into omap */
    rle_map ** mp, ** sp, ** op;	/* pointer to channel of maps */
    register int subshift;
    int c, i, ochan = ((nchan > subchan) ? nchan : subchan);

    /* Figure out how much to shift subscript */
    if ( (1 << subbits) > length )	/* too many bits in subscript */
	for ( subshift = 0; (1 << (subbits + subshift)) > length; subshift-- )
	    ;
    else			/* not enough bits */
	for ( subshift = 0; (1 << (subbits + subshift)) < length; subshift++ )
	    ;

#ifdef NO_NEGATIVE_SHIFT
    if ( subshift >= 0 )
#endif
	for ( c = ochan, mp = map, sp = submap, op = omap;
	      c > 0;
	      c--, op++, (nchan > 1 ? mp++ : 0), (subchan > 1 ? sp++ : 0) )
	    for ( i = sublen, s = *sp, o = *op; i > 0; i--, s++, o++ )
		*o = (*mp)[(*s) << subshift];
#ifdef NO_NEGATIVE_SHIFT
    else
    {
	subshift = -subshift;
	for ( c = ochan, mp = map, sp = submap, op = omap;
	      c > 0;
	      c--, op++, (nchan > 1 ? mp++ : 0), (subchan > 1 ? sp++ : 0) )
	    for ( i = sublen, s = *sp, o = *op; i > 0; i--, s++, o++ )
		*o = (*mp)[(*s) >> subshift];
    }
#endif

    shiftmap( omap, ochan, sublen, bits );
}


/*****************************************************************
 * TAG( linmap )
 * 
 * Build a linear map.
 * Inputs:
 * 	factor:		Linear factor to multiply map entries by.
 *	nchan:		Number of color channels.
 *	length:		Length of each channel.
 *	range:		Range of elements of the map.
 * Outputs:
 * 	amap:		Result map.
 * Assumptions:
 *	[None]
 * Algorithm:
 *	[None]
 */
void
linmap( factor, nchan, length, range, amap )
double factor;
int nchan, length, range;
rle_map **amap;
{
    register int i;
    double l = length - 1, m;

    for ( i = 0; i < length; i++ )
    {
	m = range * ((double)i / l) * factor;
	if ( factor < 0 )
	{
	    m = range + m;
	    if ( m < 0 )
		m = 0;
	}
	else
	    if ( m > range )
		m = range;
	amap[0][i] = (rle_map)(0.5 + m);
    }

    for ( i = 1; i < nchan; i++ )
	bcopy( (char *)amap[0], (char *)amap[i],
	       length * sizeof(rle_map) );
}

/*****************************************************************
 * TAG( gammap )
 * 
 * Build a gamma compensation map.
 * Inputs:
 * 	gamma:	    Gamma exponent.
 *	nchan:		Number of color channels.
 *	length:		Length of each channel.
 *	range:		Range of elements of the map.
 * Outputs:
 * 	amap:		Result map.
 * Assumptions:
 *	[None]
 * Algorithm:
 *	[None]
 */
void
gammap( gamma, nchan, length, range, amap )
double gamma;
int nchan, length, range;
rle_map **amap;
{
    register int i;
    double l = length - 1;

    gamma = 1.0 / gamma;
    for ( i = 0; i < length; i++ )
	amap[0][i] = (rle_map)(0.5 + range * pow( (double)i / l,
							 gamma ));

    for ( i = 1; i < nchan; i++ )
	bcopy( (char *)amap[0], (char *)amap[i],
	       length * sizeof(rle_map) );
}

/*****************************************************************
 * TAG( filemap )
 * 
 * Read a color map from a file
 * Inputs:
 * 	tflag:	    	Flag for type of file: 1 means all entries for a
 *  	    	    	channel are together (-f), 2 means all entries for
 *  	    	    	a given index are together (-t).
 *  	mapfname:   	Name of file to read map from.
 *	nchan:		Number of color channels.
 *	length:		Length of each channel.
 * Outputs:
 * 	amap:		Result map.
 * Assumptions:
 *	[None]
 * Algorithm:
 *	[None]
 */
void
filemap( tflag, mapfname, nchan, length, amap )
int tflag, nchan, length;
char *mapfname;
rle_map **amap;
{
    FILE * mapfile;
    register int c, i;
    int ent;

    mapfile = rle_open_f( "rleldmap", mapfname, "r" );

    if ( tflag == 1 )		/* channel-major order */
	for ( c = 0; c < nchan; c++ )
	    for ( i = 0; i < length; i++ )
		switch ( fscanf( mapfile, "%d", &ent ) )
		{
		case EOF:	/* EOF */
		    fprintf( stderr,
		"rleldmap: Premature end of file reading map %s at channel %d, entry %d\n",
			   mapfname, c, i );
		    exit(-1);
		    /* NOTREACHED */
		case 1:		/* Got it */
		    amap[c][i] = ent;
		    break;
		case 0:		/* no conversion? */
		    fprintf( stderr,
			    "rleldmap: Bad data in map %s at channel %d, entry %d\n",
			   mapfname, c, i );
		    exit(-1);
		    /* NOTREACHED */
		default:	/* error */
		    fprintf( stderr,
			     "rleldmap: Error reading map %s at channel %d, entry %d\n",
			     mapfname, c, i );
		    exit(-1);
		    /* NOTREACHED */
		}
    else			/* Entry-major order */
	for ( i = 0; i < length; i++ )
	    for ( c = 0; c < nchan; c++ )
		switch ( fscanf( mapfile, "%d", &ent ) )
		{
		case EOF:	/* EOF */
		    fprintf( stderr, 
		"rleldmap: Premature end of file reading map %s at entry %d, channel %d\n",
			   mapfname, i, c );
		    exit(-1);
		    /* NOTREACHED */
		case 1:		/* Got it */
		    amap[c][i] = ent;
		    break;
		case 0:		/* no conversion? */
		    fprintf( stderr,
			    "rleldmap: Bad data in map %s at entry %d, channel %d\n",
			     mapfname, i, c );
		    exit(-1);
		    /* NOTREACHED */
		default:	/* error */
		    fprintf( stderr,
			    "rleldmap: Error reading map %s at entry %d, channel %d: ",
			  mapfname, i, c );
		    perror("");
		    exit(-1);
		    /* NOTREACHED */
		}
    if ( mapfile != stdin )
	fclose( mapfile );
}

/*****************************************************************
 * TAG( mfilemap )
 * 
 * Read a color map from a multiple files
 * Inputs:
 *  	mfnames:   	Name of files to read map from.  Each file contains
 *  	    	    	the entries for a single channel of the map.
 *	nchan:		Number of color channels (thus number of files).
 *	length:		Length of each channel.
 * Outputs:
 * 	amap:		Result map.
 * Assumptions:
 *	[None]
 * Algorithm:
 *	[None]
 */
void
mfilemap( mfnames, nchan, length, amap )
char **mfnames;
int nchan, length;
rle_map **amap;
{
    FILE * mapfile;
    register int c, i;
    int ent;

    for ( c = 0; c < nchan; c++, mfnames++ )
    {
	mapfile = rle_open_f( "rleldmap", *mfnames, "r" );

	for ( i = 0; i < length; i++ )
	    switch ( fscanf( mapfile, "%d", &ent ) )
	    {
	    case EOF:		/* EOF */
		fprintf( stderr,
		    "rleldmap: Premature end of file reading map %s at channel %d, entry %d\n",
		    *mfnames, c, i );
		exit(-1);
		/* NOTREACHED */
	    case 1:		/* Got it */
		amap[c][i] = ent;
		break;
	    case 0:		/* no conversion? */
		fprintf( stderr,
			"rleldmap: Bad data in map %s at channel %d, entry %d\n",
			 *mfnames, c, i );
		exit(-1);
		/* NOTREACHED */
	    default:		/* error */
		fprintf( stderr,
			 "rleldmap: Error reading map %s at channel %d, entry %d: ",
			 *mfnames, c, i );
		perror("");
		exit(-1);
		/* NOTREACHED */
	    }
	if ( mapfile != stdin )
	    fclose( mapfile );
    }
}

/*****************************************************************
 * TAG( get_rle_map )
 * 
 * Read the map from an RLE file.
 * Inputs:
 *  	fname:	    Name of file to read map from.
 * Outputs:
 * 	the_hdr:    RLE header struct to fill in.
 * Assumptions:
 *	[None]
 * Algorithm:
 *	[None]
 */
void
get_rle_map( the_hdr, fname )
rle_hdr *the_hdr;
char *fname;
{
    FILE * infile;

    infile = rle_open_f( "rleldmap", fname, "r" );

    the_hdr->rle_file = infile;
    if ( rle_get_setup( the_hdr ) < 0 )
    {
	fprintf( stderr,
		 "rleldmap: Can't read setup information from %s\n", fname );
	exit(-1);
    }

    if ( the_hdr->ncmap == 0 )
    {
	fprintf( stderr, "rleldmap: No color map in %s\n", fname );
	exit(-1);
    }

    if ( infile != stdin )
	fclose( infile );
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.