/**
 **     $Header: /import/dev-vis/image/imtools/v2.0/imtools/src/RCS/imcomp.c,v 1.3 92/11/18 17:19:08 nadeau Exp $
 **     Copyright (c) 1989-1992  San Diego Supercomputer Center (SDSC)
 **             San Diego, California, USA
 **
 **     Users and possessors of this source code are hereby granted a
 **     nonexclusive, royalty-free copyright and design patent license to
 **     use this code in individual software.  License is not granted for
 **     commercial resale, in whole or in part, without prior written
 **     permission from SDSC.  This source is provided "AS IS" without express
 **     or implied warranty of any kind.
 **
 **     For further information contact:
 **             E-Mail:         info@sds.sdsc.edu
 **
 **             Surface Mail:   Information Center
 **                             San Diego Supercomputer Center
 **                             P.O. Box 85608
 **                             San Diego, CA  92138-5608
 **                             (619) 534-5000
 **/

#define HEADER  "    $Header: /import/dev-vis/image/imtools/v2.0/imtools/src/RCS/imcomp.c,v 1.3 92/11/18 17:19:08 nadeau Exp $"

/**
 **  FILE
 **	imcomp.c	-  Composite two images together 
 **
 **  PROJECT
 **	IM		-  Image Manipulation Tools
 **
 **  DESCRIPTION
 **	imcomp combines two images based upon alpha channel values and a
 **	selected compositing operation.  The result is saved to an output file.
 **
 **  PUBLIC CONTENTS
 **			d =defined constant
 **			f =function
 **			m =defined macro
 **			t =typedef/struct/union
 **			v =variable
 **			? =other
 **
 **	main		f  main program
 **
 **  PRIVATE CONTENTS
 **	toolCommand	v  tool-specific tool info
 **	toolHelp	v  tool-specific help
 **	toolFullHelp	v  tool-specific help
 **	toolOptions	v  tool-specific options
 **	toolEquivs	v  tool-specific equivalent keywords
 **
 **	toolInFilename	v  the name of the input file (could be 'stdin')
 **	toolOutFilename	v  the name of the output file (could be 'stdout')
 **
 **	toolInFormat	v  the name of the input file's format (could be '\0')
 **	toolOutFormat	v  the name of the output file's format (could be '\0')
 **
 **	toolInTable	v  a table for the storage of data read in
 **	toolFlagsTable	v  a table for the storage of read/write flags
 **
 **	toolInit	f  initialize things for the tool
 **
 **  HISTORY
 **	$Log:	imcomp.c,v $
 **	Revision 1.3  92/11/18  17:19:08  nadeau
 **	Updated comments, updated help information, rearranged code,
 **	added more error checking, etc.
 **	
 **	Revision 1.2  92/08/31  17:01:36  vle
 **	Updated copyright notice.
 **	
 **	Revision 1.1  92/08/26  12:59:54  groening
 **	Initial revision
 **	
 **/

#include "imtools.h"


extern void toolInit( );		/* Initialize things		*/



/*
 *  GLOBALS
 *	toolCommand		-  tool-specific tool info
 *	toolHelp		-  tool-specific help
 *	toolOptions		-  tool-specific options
 *	toolEquivs		-  tool-specific equivalent keywords
 *
 *  DESCRIPTION
 *	toolCommand describes the command to the arg package.
 *
 *	toolHelp is the tool-specific help for the tool.  It will be
 *	concatenated with the generic image tools help message and
 *	added to the toolCommand structure as the help string to be
 *	printed after the option listing.
 *
 *	toolOptions is the tool-specific option list.  It will be merged
 *	with the generic image tools options, and the list of image file
 *	format names supported by the library.  This larger option list
 *	is then used as the list of options supported by the tool.
 *
 *	toolEquivs is the tool-specific option equivalent keyword list.
 *	It will be merged with the generic image tools equivs.  This large
 *	equivs list is then used as the list of equivs supported by the tool.
 */

private ArgCommand toolCommand =
{
	/* command name */		"imcomp",

	/* major version # */		IMTOOLSMAJOR,
	/* minor version # */		IMTOOLSMINOR,
	/* subminor version # */	IMTOOLSSUBMINOR,

	/* -help pre-option list information				*/
"%command combines two input images together using a digital compositing\n\
operation and alpha channels (coverage masks) on one or both input images.\n\
The resulting composite image is stored to an output file.  Input and output\n\
files may have different image file formats.\n\
",
	/* -help post-option list information				*/
	NULL,				/* filled in later on		*/

	/* -fullhelp pre-option list information			*/
	NULL,				/* Use same message as for -help*/
	/* -fullhelp post-option list information			*/
	NULL,				/* filled in later on		*/

	ARGFNONE,			/* No special flags		*/
	"[options...] infilename1 infilename2 outfilename",
	"[options...] infilename1 infilename2 outfilename",
	"SDSC Image Tools, October 1992.",
	"Copyright (c) 1989-1992  San Diego Supercomputer Center (SDSC), CA, USA",
	NULL,				/* filled in later on		*/
	NULL,				/* filled in later on		*/
};

private char *toolHelp = "\n\
Typical Invocations:\n\
     Composite two Sun rasterfiles using the OVER alogrithm:\n\
        %command inone.ras intwo.ras out.rgb\n\
     Composite just the red field and the ATOP algorithm:\n\
        %command inone.ras intwo.ras -red -atop out.ras\n\
     Composite the saturation and hue fields and the OUTSIDE alogorithm:\n\
	%command inone.ras intwo.ras -saturation -hue -outside out.ras\n\
";

private char *toolFullHelp = "\n\
Files:\n\
    -infile1 names the first input file, while -infile2 names the second.\n\
    Compositing will combine selected fields in the first file with those\n\
    in the second to produce an output image stored into the file named by\n\
    the -outfile option.  Input and output file formats may each have\n\
    different image file formats.\n\
\n\
Compositing:\n\
    -over, -atop, -inside, and -outside, select the compositing algorithm:\n\
\n\
        -over    Place opaque parts of image 1 over image 2.  Default.\n\
                     OutField = In1Field * (In1Alpha/255.0) +\n\
                                In2Field * (1.0 - (In1Alpha/255.0))\n\
\n\
        -atop    Combine opaque parts of image 2 with image 1.\n\
                     OutField = In1Field * (In2Alpha/255.0) +\n\
                                In2Field * (1.0 - (In1Alpha/255.0))\n\
\n\
        -inside  Use image 1 where image 2 is opaque.\n\
                     OutField = In1Field * (In2Alpha/255.0)\n\
\n\
        -outside Use image 1 where image 2 is transparent.\n\
                     OutField = In1Field * (1.0 - (In2Alpha/255.0))\n\
\n\
    By default all fields in the first input image are composited into the\n\
    second.  Alternatively, explicit field compositing may be done by\n\
    listing the desired fields on the command-line:  -red, -green, -blue,\n\
    -mono, -index, -hue, -saturation, -intensity, and -alpha.\n\
";

private char *toolNote = "\n\
Additional Help:\n\
    This is an abbreviated help listing.  For a full listing of options,\n\
    including a list of image file formats supported, type:\n\
        %command -fullhelp\n\
";

#define TOOLNOPTIONS	17
private ArgOption toolOptions[TOOLNOPTIONS] =
{
	{ "infile1", "image_filename", "Specify a first input image file name",
	  ARGFREQUIRED | ARGFIMPKEYWORD, 1, 1, ARGTSTRING },

	{ "infile2", "image_filename", "Specify a second input image file name",
	  ARGFREQUIRED | ARGFIMPKEYWORD, 1, 1, ARGTSTRING },

	{ "outfile", "image_filename", "Specify an output image file name",
	  ARGFREQUIRED | ARGFIMPKEYWORD, 1, 1, ARGTSTRING },

	{ "over", NULL, "Use the over algorithm",
	  ARGFNONE,0,0,ARGTNONE},

	{ "inside", NULL, "Use the inside algorithm",
	  ARGFNONE,0,0,ARGTNONE},

	{ "outside", NULL, "Use the outside algorithm",
	  ARGFNONE,0,0,ARGTNONE},

	{ "atop", NULL, "Use the atop algorithm",
	  ARGFNONE,0,0,ARGTNONE},

	{ "mono", NULL, "Composite monochrome values",
	  ARGFNONE, 0,0, ARGTNONE },

	{ "index", NULL, "Composite color indexes",
	  ARGFNONE, 0,0, ARGTNONE },

	{ "red", NULL, "Composite red channels",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "green", NULL, "Composite green channels",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "blue", NULL, "Composite blue channels",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "hue", NULL, "Composite hues",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "saturation", NULL, "Composite saturations",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "intensity", NULL, "Composite intensities",
	  ARGFNONE, 0, 0, ARGTNONE },

	{ "alpha", NULL, "Composite alphas",
	  ARGFNONE, 0, 0, ARGTNONE},

	{ "verbose", NULL, "Be verbose",
	  ARGFFULLHELP, 0, 0, ARGTNONE },
};

#define TOOLNEQUIVS	0
#if TOOLNEQUIVS == 0
private ArgEquiv *toolEquivs;
#else
private ArgEquiv toolEquivs[TOOLNEQUIVS] =
{
};
#endif





/*
 *  GLOBALS
 *	toolIn1Filename	-  the name of the first input file (could be 'stdin')
 *	toolIn2Filename	-  the name of the second input file (could be 'stdin')
 *	toolOutFilename	-  the name of the output file (could be 'stdout')
 *
 *	toolIn1Format	-  the name of the first input file's format
 *	toolIn2Format	-  the name of the second input file's format
 *	toolOutFormat	-  the name of the output file's format (could be NULL)
 *
 *	toolIn1Table	-  first table for the storage of data read in
 *	toolIn2Table	-  second table for the storage of data read in
 *	toolFlagsTable	-  a table for the storage of read/write flags
 *
 *	toolOperator	-  compositing operator to use
 *	toolField	-  compositing fields
 *
 *  DESCRIPTION
 *	Data held by these variables is used throughout the tool.
 */

private char      toolIn1Filename[1024];/* Input file name		*/
private char      toolIn1Format[1024];	/* Input file's format name	*/

private char      toolIn2Filename[1024];/* Input file name		*/
private char      toolIn2Format[1024];	/* Input file's format name	*/

private char      toolOutFilename[1024];/* Output file name		*/
private char      toolOutFormat[1024];	/* Output file's format name	*/

private TagTable *toolIn1Table;		/* Data tag table		*/
private TagTable *toolIn2Table;		/* Data tag table		*/
private TagTable *toolFlagsTable;	/* Flags tag table		*/

private int	  toolOperator;		/* where to fill in area	*/
private int	  toolField;		/* What fields to change 	*/




/*
 *  FUNCTION
 *	main	-  main program
 *
 *  DESCRIPTION
 *	Control things:
 *		-  Initialize things (parse arguments and set up tables).
 *		-  Read in the input file (put data into data table).
 *		-  Composite each image.
 *		-  Replace the data table images with the results.
 *		-  Write out the output file (get data from data table).
 *	That's about it.
 *
 *  NOTES
 *	This 'main' is pretty much the same for each of the image tools.
 *	Differences between tools include:
 *		-  the number of input files read in
 *		-  the number of output files written out
 *		-  the actions taken on the data between read and write
 */

main( argc, argv )
	int argc;			/* Argument count		*/
	char *argv[];			/* Argument vector		*/
{
	int         nIn1Vfb;		/* Number of images in file	*/
	int         nIn2Vfb;		/* Number of images in file	*/
	int         i;			/* Counter			*/

	TagEntry   *dataOneEntry;	/* Entry from data table	*/
	TagEntry   *dataTwoEntry;	/* Entry from data table	*/

	ImVfb      *source2Vfb;		/* Source image			*/
	ImVfb      *source1Vfb;		/* Source image			*/
	ImVfb      *vfb;		/* Source image			*/
        TagEntry   *newEntry;           /* Entry from data table        */
        int        nEntry;              /* index into vfb as to where image is*/
	int	   nVfb;		/* how many times to loop 	*/
	int	   fieldMask;		/* Field mask of images		*/
	int	   source1Fields;	/* Source image #1's fields	*/
	int	   source2Fields;	/* Source image #2's fields	*/
	int	   tmpFields;		/* Temporary field mask holder	*/
	int	   compField;		/* Fields to composite on	*/


	/*
	 *  Initialize things:
	 *	-  Prepare the arg parsing data, then parse the command-line.
	 *	-  Prepare the flags table based upon command-line args.
	 *	-  Determine the file formats for input and output files.
	 */
	toolInit( argc, argv );


        /*
         *  Read in the input files.
         *      -  Open the files (or stdin) and read data into the data tables.
         */
	ImToolsFileRead( toolIn1Filename, toolIn1Format, toolFlagsTable,
		toolIn1Table );
	ImToolsFileRead( toolIn2Filename, toolIn2Format, toolFlagsTable,
		toolIn2Table );


        /*
         *  Check for errors
         *      -  no input images
	 *	-  unequal number of images in two files (except when doing
	 *	   a 1->many composite or a many<-1 composite).
         */
	nIn1Vfb = TagTableQNEntry( toolIn1Table, "image vfb" );
	if ( nIn1Vfb == 0 )
	{
		fprintf( stderr, "%s: First input file contains no images!\n",
			ImToolsProgram );
		exit( 1 );
	}

	nIn2Vfb = TagTableQNEntry( toolIn2Table, "image vfb" );
	if ( nIn2Vfb == 0 )
	{
		fprintf( stderr, "%s: Second input file contains no images!\n",
			ImToolsProgram );
		exit( 1 );
	}

        if ( (nIn1Vfb != nIn2Vfb) && (nIn1Vfb > 1) && (nIn2Vfb > 1) )
        {
		fprintf (stderr,"%s: Input files must have the same number of images\n", ImToolsProgram );
		exit( 1 );
        }


	/*
	 *  Composite!
	 *	-  get the input images
	 *	-  convert color indexed images to RGB
	 *	-  composite.
	 */
	if ( nIn1Vfb > nIn2Vfb )
		nVfb = nIn1Vfb;
	else
		nVfb = nIn2Vfb; 
	for ( i = 0; i < nVfb; i++ )
	{
		/*
		 *  Get the first and second composite images.
		 *  We could be compositing in one of several manners:
		 *
		 *	n to n		There's n images in each file (usually
		 *			just one).  Composite them one at a
		 *			time.
		 *
		 *	1 to n		There's 1 image in the first file
		 *			and n in the second.  Composite the
		 *			1 first image repeatedly with each
		 *			of the n second images.
		 *
		 *	n to 1		There's n images in the first file
		 *			and 1 in the second.  Composite each
		 *			of the n first images with the same
		 *			second image.
		 *
		 *  So, on each pass through this loop, figure out which
		 *  method to use and get the next image as appropriate.
		 */
		if ( nIn1Vfb > i )
		{
			dataOneEntry = TagTableQDirect( toolIn1Table,
				"image vfb", i );
			TagEntryQValue( dataOneEntry, &source1Vfb );
		}
		if ( nIn2Vfb > i )
		{
			dataTwoEntry = TagTableQDirect( toolIn2Table,
				"image vfb", i );
			TagEntryQValue( dataTwoEntry, &source2Vfb );
		}


                if ( ImToolsVerbose )
                        fprintf (stderr, "%s: Compositing image set %d of %d\n",
                                ImToolsProgram, i+1, nVfb );



		/*
		 *  If one of the source images is color indexed, while the
		 *  other is RGB, convert the color indexed image to RGB.
		 */
		source1Fields = ImVfbQFields( source1Vfb );
		source2Fields = ImVfbQFields( source2Vfb );
		if ( (source1Fields & (IMVFBINDEX8 | IMVFBINDEX16)) &&
			(source2Fields & IMVFBRGB) )
		{
			/* First image needs to be promoted to RGB.	*/
			if (ImToolsVerbose)
			{
				fprintf (stderr, "%s: Converting first color index image to RGB.\n",
					ImToolsProgram );
			}
			tmpFields = source1Fields & ~(IMVFBINDEX8|IMVFBINDEX16);
			vfb = ImVfbAlloc( ImVfbQWidth( source1Vfb ),
				ImVfbQHeight( source1Vfb ),
				tmpFields | IMVFBRGB );
			if ( vfb == IMVFBNULL )
			{
				fprintf( stderr, "%s: Cannot allocate memory for first color index image as RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}

			/* Copy all non-color index fields to new VFB.	*/
			if ( tmpFields && ImVfbCopy( source1Vfb, 0, 0,
				ImVfbQWidth( vfb ), ImVfbQHeight( vfb ),
				tmpFields, vfb, 0, 0 ) == IMVFBNULL )
			{
				fprintf( stderr, "%s: Problem in copying first color index image to RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}
			if ( ImVfbToRgb( source1Vfb, vfb ) == IMVFBNULL )
			{
				fprintf( stderr, "%s: Cannot convert first color index image to RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}

			/*
			 *  Free the original source image.  This will mean
			 *  that the first input file's tag table entry for
			 *  it becomes bogus (points to VFB that isn't there).
			 *  This is OK since we won't be using the first
			 *  tag table's entry again.
			 */
			ImVfbFree( source1Vfb );
			source1Vfb = vfb;
			source1Fields = ImVfbQFields( source1Vfb );
		}
		else if ( (source2Fields & (IMVFBINDEX8 | IMVFBINDEX16)) &&
			(source1Fields & IMVFBRGB) )
		{
			/* Second image needs to be promoted to RGB.	*/
			if (ImToolsVerbose)
			{
				fprintf (stderr, "%s: Converting second color index image to RGB.\n",
					ImToolsProgram );
			}
			tmpFields = source2Fields & ~(IMVFBINDEX8|IMVFBINDEX16);
			vfb = ImVfbAlloc( ImVfbQWidth( source2Vfb ),
				ImVfbQHeight( source2Vfb ),
				tmpFields | IMVFBRGB );
			if ( vfb == IMVFBNULL )
			{
				fprintf( stderr, "%s: Cannot allocate memory for second color index image as RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}

			/* Copy all non-color index fields to new VFB.	*/
			if ( tmpFields && ImVfbCopy( source2Vfb, 0, 0,
				ImVfbQWidth( vfb ), ImVfbQHeight( vfb ),
				tmpFields, vfb, 0, 0 ) == IMVFBNULL )
			{
				fprintf( stderr, "%s: Problem in copying second color index image to RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}
			if ( ImVfbToRgb( source2Vfb, vfb ) == IMVFBNULL )
			{
				fprintf( stderr, "%s: Cannot convert second color index image to RGB.\n", ImToolsProgram );
				ImPError( ImToolsProgram );
				exit( 1 );
			}

			/* Replace in second tag table (will be out table)*/
			nEntry = TagEntryQNthEntry( dataTwoEntry );
			newEntry = TagEntryAlloc( "image vfb", POINTER, &vfb );
			TagTableReplace( toolIn2Table, nEntry, newEntry );
			ImVfbFree( source2Vfb );
			source2Vfb = vfb;
			source2Fields = ImVfbQFields( source2Vfb );
		}


		/*
		 *  Make sure the alpha channels we need are there.
		 */
		switch ( toolOperator )
		{
		case IMCOMPOVER:	/* Requires 1st image have alpha*/
			if ( !(source1Fields & IMVFBALPHA) )
			{
				fprintf( stderr, "%s: The -over option requires that the first image have an alpha channel,\n", ImToolsProgram );
				fprintf( stderr, "%s: yet it doesn't have one.  You may use tools like imfill and\n", ImToolsProgram );
				fprintf( stderr, "%s: imadjust to create alpha channels.\n", ImToolsProgram );
				exit( 1 );
			}
			break;

		case IMCOMPATOP:	/* Requires both have alpha	*/
			if ( !(source1Fields & IMVFBALPHA) )
			{
				fprintf( stderr, "%s: The -atop option requires that the first image have an alpha channel,\n", ImToolsProgram );
				fprintf( stderr, "%s: yet it doesn't have one.  You may use tools like imfill and\n", ImToolsProgram );
				fprintf( stderr, "%s: imadjust to create alpha channels.\n", ImToolsProgram );
				exit( 1 );
			}
			if ( !(source2Fields & IMVFBALPHA) )
			{
				fprintf( stderr, "%s: The -atop option requires that the second image have an alpha channel,\n", ImToolsProgram );
				fprintf( stderr, "%s: yet it doesn't have one.  You may use tools like imfill and\n", ImToolsProgram );
				fprintf( stderr, "%s: imadjust to create alpha channels.\n", ImToolsProgram );
				exit( 1 );
			}
			break;

		case IMCOMPINSIDE:	/* Requires 2nd image have alpha*/
			if ( !(source2Fields & IMVFBALPHA) )
			{
				fprintf( stderr, "%s: The -inside option requires that the second image have an alpha channel,\n", ImToolsProgram );
				fprintf( stderr, "%s: yet it doesn't have one.  You may use tools like imfill and\n", ImToolsProgram );
				fprintf( stderr, "%s: imadjust to create alpha channels.\n", ImToolsProgram );
				exit( 1 );
			}
			break;

		case IMCOMPOUTSIDE:	/* Requires 2nd image have alpha*/
			if ( !(source2Fields & IMVFBALPHA) )
			{
				fprintf( stderr, "%s: The -outside option requires that the second image have an alpha channel,\n", ImToolsProgram );
				fprintf( stderr, "%s: yet it doesn't have one.  You may use tools like imfill and\n", ImToolsProgram );
				fprintf( stderr, "%s: imadjust to create alpha channels.\n", ImToolsProgram );
				exit( 1 );
			}
			break;
		}


		/*
		 *  Confirm our set of fields on which to composite.
		 */
	        if ( toolField == 0 ) 
	        {
			/*
			 *  No explicit field list given.  Composite on all
			 *  fields common to the two images.
			 */
			compField = source1Fields & source2Fields;
			if ( compField == 0 )
			{
				fprintf( stderr, "%s: Input images share no common pixel fields on which to composite!\n", ImToolsProgram );
				exit( 1 );
			}

			/*
			 *  Expand IMVFBRGB to IMRED | IMGREEN | IMBLUE
			 */
			if ( compField & IMVFBRGB )
				compField = (compField & ~IMVFBRGB) | IMRED | IMGREEN | IMBLUE;
	        }
		else
		{
			/*
			 *  Make sure the fields listed actually exist in
			 *  both input images.
			 */
			if ( toolField & (IMHUE | IMSATURATION | IMINTENSITY) )
				fieldMask = IMVFBRGB |
					(toolField & ~(IMHUE | IMSATURATION | IMINTENSITY));
			else if ( toolField & (IMRED | IMGREEN | IMBLUE) )
				fieldMask = IMVFBRGB |
					(toolField & ~(IMRED | IMGREEN|IMBLUE));
			else
				fieldMask = toolField;
			if ( (source1Fields & fieldMask) != fieldMask )
			{
				fprintf( stderr, "%s: First input image doesn't have those pixel fields!\n", ImToolsProgram );
				exit( 1 );
			}
			if ( (source2Fields & fieldMask) != fieldMask )
			{
				fprintf( stderr, "%s: Second input image doesn't have those pixel fields!\n", ImToolsProgram );
				exit( 1 );
			}
			compField = toolField;
		}


		/*
		 *  Composite!
		 */
		vfb = ImVfbComp( source1Vfb, source2Vfb, toolOperator,
			compField, IMVFBNEW );
                if ( vfb == IMVFBNULL )
                {
                        ImPError( ImToolsProgram );
                        fprintf( stderr, "%s: Couldn't composite image set %d of %d.\n",
                                ImToolsProgram, i + 1, nVfb );
                        fprintf( stderr, "%s: Untouched second image copied to output file.\n",
                                ImToolsProgram );
                        continue;
                }


		/*
		 *  Replace the second image in its data table.  We'll be
		 *  using this table when we write the output file.
		 */
		nEntry = TagEntryQNthEntry( dataTwoEntry );
		newEntry = TagEntryAlloc( "image vfb", POINTER, &vfb );
		TagTableReplace( toolIn2Table, nEntry, newEntry );
	}
  

	/*
	 *  Write out the output file.
	 *	-  Open the file (or stdout) and write the data in the data
	 *	   table.  Upon failure, remove the bad output file.
	 */
	ImToolsFileWrite( toolOutFilename, toolOutFormat, toolFlagsTable,
		toolIn2Table );

	exit( 0 );
}





/*
 *  FUNCTION
 *	toolInit	-  initialize things for the tool
 *
 *  DESCRIPTION
 *	The tool's argument parsing data structures are set up to include:
 *		- the full help message (generic help + tool-specific help)
 *		- the full option list (generic options + tool-specific options)
 *		- the full equivs list (generic equivs + tool-specific equivs)
 *
 *	Command-line arguments are then parsed.  The results are used to
 *	set up the flags table (the generic -out* options).
 *
 *	Input and output file's names and formats are determined from the
 *	command-line arguments.
 *
 *  NOTES
 *	This function is included in most of the image tools and differs
 *	only in slight ways.  Typical differences include:
 *		-  the number of input and output file names found
 *		-  the number of input and output file formats found
 *		-  the number of command-line arg flags checked for
 */

private void				/* Returns nothing		*/
toolInit( argc, argv )
	int argc;			/* Argument count		*/
	char *argv[ ];			/* Argument vector		*/
{
	ImVfb	    *vfb;
	int	    i;			/* Counter			*/
	int	    noccur;		/* Number of option occurrences	*/
	int         nOpt;		/* Number of options		*/
	int         nEquiv;		/* Number of equivalences	*/
	int	    nInVfb;		/* number of images in file	*/
	int	    numOps;		/* number ofoperations on command line*/
	ArgOption  *options1;		/* Argument options		*/
	ArgOption  *options;		/* Argument options		*/
	ArgEquiv   *equivs1;		/* Argument equivalent keywords	*/
	ArgEquiv   *equivs;		/* Argument equivalent keywords	*/
        ArgValue   *value;              /* Argument value               */

	char       *tmp;		/* Temporary string holder	*/
	char       *tmpFormat;		/* Tmp format name		*/


	/*
	 *  Save the name of the program, as invoked.
	 */
	ImToolsProgram = argv[0];


	/*
	 *  Make a data table to hold the incomming data.
	 */
	if ( (toolIn1Table = TagTableAlloc( )) == TAGTABLENULL )
	{
		TagPError( ImToolsProgram );
		exit( 1 );
	}

	if ( (toolIn2Table = TagTableAlloc( )) == TAGTABLENULL )
	{
		TagPError( ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Use the standard Image Tools user registration and feedback forms.
	 */
	toolCommand.arg_register = ImToolsRegister;
	toolCommand.arg_feedback = ImToolsFeedback;


	/*
	 *  Allocate space for the total help string for the tool.  Copy the
	 *  tool-specific help in, then concatenate on the generic help text
	 *  used by most of the image tools.
	 */
	if ( (tmp = (char *)malloc( sizeof( char ) * (strlen( toolNote ) +
		strlen( toolHelp ) + 1) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}
	strcpy( tmp, toolHelp );
	strcat( tmp, toolNote );
	toolCommand.arg_help2 = tmp;

	if ( (tmp = (char *)malloc( sizeof( char ) * (strlen( ImToolsBaseHelp) +
		strlen( toolHelp ) + strlen( toolFullHelp ) + 1) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}
	strcpy( tmp, toolHelp );
	strcat( tmp, toolFullHelp );
	strcat( tmp, ImToolsBaseHelp );
	toolCommand.arg_fullhelp2 = tmp;


	/*
	 *  Build up an option list by merging the tool-specific options,
	 *  the standard (base) tool options, and those for the various
	 *  image file formats.
	 */
	nOpt = ImToolsMergeOptions( TOOLNOPTIONS, toolOptions,
		IMTOOLSNBASEOPTIONS, ImToolsBaseOptions, &options1 );
	if ( (nOpt = ImFileFormatOptions( nOpt, options1, &options )) == -1)
	{
		ImPError( ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Build up an equivalent keyword list by merging the tool-specific
	 *  equivalences, the standard (base) tool equivalences, and those
	 *  for the various image file formats.
	 */
	nEquiv = ImToolsMergeEquivs( TOOLNEQUIVS, toolEquivs,
		 IMTOOLSNBASEEQUIVS, ImToolsBaseEquivs, &equivs1 );
	if ( (nEquiv = ImFileFormatEquivs( nEquiv, equivs1, &equivs )) == -1)
	{
		ImPError( ImToolsProgram );
		exit( 1 );
	}

	/*
	 *  Parse the command line!
	 */
	nOpt = ArgParse( argc, argv, &toolCommand, nOpt, options,
		nEquiv, equivs );
	if ( ArgQNOccur( "verbose" ) != 0 )
		ImToolsVerbose = TRUE;


	/* Initialize information */
	toolOperator = IMCOMPOVER;
	numOps =0;

	/*
	 *  Get the operator information.
	 */
	if (ArgQNOccur ("over")!=0)
	{
		toolOperator = IMCOMPOVER;
		numOps++;
	}

	if (ArgQNOccur ("inside")!=0)
	{
		toolOperator = IMCOMPINSIDE;
		numOps++;
	}

	if (ArgQNOccur ("atop")!=0)
	{
		toolOperator = IMCOMPATOP;
		numOps++;
	}

	if (ArgQNOccur ("outside")!=0)
	{
		toolOperator=IMCOMPOUTSIDE;
		numOps++;
	}

	if ( numOps > 1 )
        {
                fprintf( stderr, "%s: Only one compositing operator is allowed at a time\n", ImToolsProgram );
                exit( 1 );
        }

	/*
	 *  Set up the flags table based upon command-line arguments.
	 *  This is primarily derived from the -out* directives part of the
	 *  standard image tool option set (see ImToolsBaseOptions above).
	 *  Also included are flags to direct error messages to stderr and
	 *  a flag giving the program's name for later use in error messages.
	 */
	toolFlagsTable = ImToolsBuildFlagsTable( );


	/*
	 *  Get the two input file names (-infile1 and 2), and search backwards
	 *  in the command-line option list to find the last format selection
	 *  (if any).  Stop the search on the beginning of the command-line, or
	 *  on -outfile or -infile2 (or 1).
	 */
	strcpy( toolIn1Filename, ArgQValue( "infile1", 0, 0 )->arg_s );
	tmpFormat = NULL;
	for ( i = ArgQOccurOpt( "infile1", 0 ) - 1; i >= 0; i-- )
	{
		tmp = ArgQOpt( i, &noccur );


		/*
		 *  Stop looking backward when we reach any other file name
		 *  argument.
		 */
		if ( strcmp( tmp, "outfile" ) == 0 )
			break;
		if ( strcmp( tmp, "infile2" ) == 0 )
			break;


		/*
		 *  Skip it if it isn't the name of a file format.
		 */
		if ( !ImToolsIsFormat( tmp ) )
			continue;


		if ( tmpFormat != NULL )
		{
			fprintf( stderr, "%s:  Only 1 file format selection may precede -infile1.\n",
				ImToolsProgram );
			exit( 1 );
		}
		tmpFormat = tmp;
	}
	if ( tmpFormat == NULL )
		*toolIn1Format = '\0';
	else
		strcpy( toolIn1Format, tmpFormat );

	strcpy( toolIn2Filename, ArgQValue( "infile2", 0, 0 )->arg_s );
	tmpFormat = NULL;
	for ( i = ArgQOccurOpt( "infile2", 0 ) - 1; i >= 0; i-- )
	{
		tmp = ArgQOpt( i, &noccur );


		/*
		 *  Stop looking backward when we reach any other file name
		 *  argument.
		 */
		if ( strcmp( tmp, "outfile" ) == 0 )
			break;
		if ( strcmp( tmp, "infile1" ) == 0 )
			break;


		/*
		 *  Skip it if it isn't the name of a file format.
		 */
		if ( !ImToolsIsFormat( tmp ) )
			continue;


		if ( tmpFormat != NULL )
		{
			fprintf( stderr, "%s:  Only 1 file format selection may precede -infile.\n",
				ImToolsProgram );
			exit( 1 );
		}
		tmpFormat = tmp;
	}
	if ( tmpFormat == NULL )
		*toolIn2Format = '\0';
	else
		strcpy( toolIn2Format, tmpFormat );


	/*
	 *  Get the output file's name (-outfile), and search backwards in the
	 *  command-line option list to find the last format selection (if
	 *  any).  Stop the search on the beginning of the command-line, or
	 *  on -infile1 or infile2.
	 */
	strcpy( toolOutFilename, ArgQValue( "outfile", 0, 0 )->arg_s );
	tmpFormat = NULL;
	for ( i = ArgQOccurOpt( "outfile", 0 ) - 1; i >= 0; i-- )
	{
		tmp = ArgQOpt( i, &noccur );


		/*
		 *  Stop looking backward when we reach any other file name
		 *  argument.
		 */
		if ( strcmp( tmp, "infile1" ) == 0 )
			break;
		if ( strcmp( tmp, "infile2" ) == 0 )
			break;


		/*
		 *  Skip it if it isn't the name of a file format.
		 */
		if ( !ImToolsIsFormat( tmp ) )
			continue;


		if ( tmpFormat != NULL )
		{
			fprintf( stderr, "%s:  Only 1 file format selection may precede -outfile.\n",
				ImToolsProgram );
			exit( 1 );
		}
		tmpFormat = tmp;
	}
	if ( tmpFormat == NULL )
		*toolOutFormat = '\0';
	else
		strcpy( toolOutFormat, tmpFormat );

        /*
         *  Check that the input files are not both stdin.
         */
        if ( strcmp( toolIn1Filename, "-" ) == 0 &&
                strcmp( toolIn2Filename, "-" ) == 0)
        {
                fprintf( stderr, "%s: -infile1 and -infile2 cannot both be stdin.\n",
                        ImToolsProgram );
                exit( 1 );
        }


	/*
	 *  Get the composite fields.
	 */
	toolField = 0;
	if ( ArgQNOccur( "red" ) != 0 )			toolField |= IMRED;
	if ( ArgQNOccur( "green" ) != 0 )		toolField |= IMGREEN;
	if ( ArgQNOccur( "blue" ) != 0 )		toolField |= IMBLUE;
	if ( ArgQNOccur( "alpha" ) != 0 )		toolField |= IMALPHA;
	if ( ArgQNOccur( "index" ) != 0 )		toolField |= IMINDEX8;
	if ( ArgQNOccur( "mono" ) != 0 )		toolField |= IMMONO;
	if ( ArgQNOccur( "hue" ) != 0 )			toolField |= IMHUE;
	if ( ArgQNOccur( "saturation" ) != 0 )		toolField |= IMSATURATION;
	if ( ArgQNOccur( "intensity" ) != 0 )		toolField |= IMINTENSITY;


	if ( toolField & (IMRED | IMGREEN | IMBLUE) )
	{
		if ( (toolField & ~(IMRED | IMGREEN | IMBLUE | IMALPHA)) != 0 )
		{
			fprintf( stderr, "%s: Compositing can only occur in one color space at a time\n", ImToolsProgram );
			fprintf( stderr, "%s: -red, -green, and -blue cannot be mixed with other field options\n", ImToolsProgram );
			exit( 1 );
		}
	}
	else if ( toolField & (IMHUE | IMSATURATION | IMINTENSITY) )
	{
		if ( (toolField & ~(IMHUE | IMSATURATION | IMINTENSITY | IMALPHA)) != 0 )
		{
			fprintf( stderr, "%s: Compositing can only occur in one color space at a time\n", ImToolsProgram );
			fprintf( stderr, "%s: -hue, -saturation, and -intensity cannot be mixed with other\n", ImToolsProgram );
			fprintf( stderr, "%s: field options.\n",ImToolsProgram);
			exit( 1 );
		}
	}
	else if ( toolField & IMMONO )
	{
		if ( (toolField & ~(IMMONO | IMALPHA)) != 0 )
		{
			fprintf( stderr, "%s: Compositing can only occur in one color space at a time\n", ImToolsProgram );
			fprintf( stderr, "%s: -mono cannot be mixed with other field options\n", ImToolsProgram );
			exit( 1 );
		}
	}
	else if ( toolField & IMINDEX8 )
	{
		if ( (toolField & ~(IMINDEX8 | IMALPHA)) != 0 )
		{
			fprintf( stderr, "%s: Compositing can only occur in one color space at a time\n", ImToolsProgram );
			fprintf( stderr, "%s: -index cannot be mixed with other field options\n", ImToolsProgram );
			exit( 1 );
		}
	}
}
