/********************************************
 *
 *          absird.c
 *
 *  Version 1.0.  October 6th, 1995
 *
 *
 *
 * This program creates an auto-stereogram from a depth image, either
 * by tiling an image or creating a random-dot pattern.
 *
 * It is designed to be linked with release 6 of the IJG jpeg library.
 * That library and all the source for it are available at ftp.uu.net 
 * (Internet address 192.48.96.9) in the directory graphics/jpeg.
 * (If you don't already have it, get it.  You won't be sorry.)
 *
 * If you don't want to use that library, simply write your own
 * read & write routines in place of the ones which I have.  The
 * stereogram part of the program will not mind.
 *
 * Compile with -I set to the location of the jpeg library include
 * files, and link with libjpeg and -lm.  It should compile under C++
 * just fine.  (Famous last words.)
 *
 *
 *
 * Copyright (c) 1995 Bark of Delight.  This source may be used
 * for nonprofit purposes only, and only compiled under and for
 * use with non-Microsoft operating systems, unless under license
 * from the author.  This notice must remain in the source.
 *
 * Portions Copyright (c) Garlic Software, Olive Starlight Orchestra,
 *                        Keith Goldfarb
 *
 * Thanks to Greg Turk, Rhythm & Hues Studios, and IJG.
 *
 *
 *
 *  Questions?  Comments?  Write to me anytime.
 *
 *  Keith Goldfarb              Rhythm & Hues               
 *  keith@rhythm.com            http://www.rhythm.com/~keith
 *  I got nothing.  Too bad.                                Some pink toes
 *  But I'm happy 'cause that's all I have.                 Some black toes
 *
 */


static char sccsid[] = "$Id: absird.c 1.0 1995/10/06$";


#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <strings.h>
#include <math.h>
#include <ctype.h>


/* Here are two typedefs to make the code a bit more brief.
 *  They define the types which I use to communicate with the
 *  image read & write routines.
 */
typedef struct jpeg_compress_struct     JPEG_CS;
typedef struct jpeg_decompress_struct   JPEG_DS;

/* This is the structure used by the parsing routines. */
typedef struct
    {
    char        *flagName;
    int         minItems;
    int         maxItems;
    char        **items;
    char        **itemNames;
    int         *hit;
    int         *numItems;
    } PARSETABLE;



/* Here are all the function prototypes. */

/* First the parsing/util routines. */
/* Bail, leave the program will an error message. */
void    bail(
    char    *progname,
    char    *message
    );

/* FindInterp, looks up a value in an array of values with interpolation. */
float   findInterp(
    float       *vals,
    float       location
    );

/* ParseArgs, does the actual parsing of the command line. */
int     parseArgs(
    int         argc,
    char        **argv,
    int         numParseTable,
    PARSETABLE  *parseTable
    );

/* PrintUsage, prints the usage and exits. */
void    printUsage(
    char        *progName,
    int         numParseTable,
    PARSETABLE  *parseTable, 
    char        *extraMessage
    );


/* Next, we have the JPEG file routines (which call the JPEG library. */

/* Open routines */
JPEG_DS *openJPEGRead(
    FILE    *infile,
    int     *width,
    int     *height,
    int     *numChans
    );

JPEG_CS *openJPEGWrite(
    FILE    *outfile,
    int     width,
    int     height,
    int     numChans
    );

/* Scanline read/write. */
void    readJPEGScanline(
    JPEG_DS         *dInfo,
    unsigned char   *dest
    );

void    writeJPEGScanline(
    JPEG_CS         *cInfo,
    unsigned char   *src
    );

/* And close. */
void    closeJPEGRead(
    JPEG_DS *dInfo
    );

void    closeJPEGWrite(
    JPEG_CS     *cInfo
    );


/* Finally, the routines which actually deal with depth data. */

/* DepthToOffset, converts a depth value into a pixel offset. */
float   depthToOffset(
    float   depth,
    float   screenDistance,
    float   dpi,
    float   io
    );

/* FindIndex, given a location and an array of depth data, finds the
 *  location in the range 0-1 to assign to this pixel.
 */
float   findIndex(
    float           location,
    float           *depthData,
    float           minDepth,
    float           maxDepth,
    float           zeroDepth,
    float           screenDistance,
    float           dpi,
    float           io
    );

/* End of prototypes */






/*----------------------------------------------------------------------
 |                             main
 *----------------------------------------------------------------------
 |
 |  Function:  Main entry point.
 |
 |  Usage: 
 |      progname depthFile outFile [tileFile]]
 |          [-depthVals minDepth maxDepth [zeroDepth]]
 |          [-dpi pixelsPerUnit]
 |          [-interOcular iodistance]
 |          [-screen distanceToScreen]
 |          [-verbose]
 |
 |      depthFile is the input file, contains depth data.
 |
 |      outFile will be the file created (slightly larger!)
 |
 |      tileFile is an optional tile image.  If not given,
 |          a random-dot black & white pattern will be used
 |          as the tile pattern.
 |
 |      -depthVals specifies the distances (in real units) that
 |          the depthFile specifies.  minDepth will be what the
 |          value "1.0" (or 255 for 8-bit files) means, maxDepth
 |          will be what the smallest value means, and zeroDepth
 |          will be what zero means.  (Often, the user wants a 
 |          jump between the smallest value and zero.)
 |
 |      -dpi specifies the number of pixels per unit on the viewing
 |          screen.  Yes, it matters quite a bit.  Default is 72,
 |          as it's a reasonable guess.
 |
 |      -interOcular is the distance between the viewer's eyes.
 |          I don't know why I provide this option.  Default is 2.5.
 |
 |      -screen specifies the distance from the viewer to the screen.
 |          Default is 15.
 |
 |      -verbose sets verbose mode.
 |
 |      Measurements can be in whatever units you'd like.  I use
 |          inches since I live in the USA.
 |
 |
 |  Notes:  There are three parts here:
 |           1: Parse the input parameters.
 |           2: Open the files.
 |           3: Generate the sterogram.
 */

void    main(
    int     argc,
    char    **argv
    )
{


   /* This is all parsing stuff.  We see for each option
    *  The defaults for that option, the names of the items
    *  (for printing the usage) and the two variables "count"
    *  and "hitCount".  HitCount is 1 if the option was selected,
    *  0 otherwize.  Count is the number of parameters found.
    *  The actual parameters are stored in the item array (which
    *  must be large enough to hold them) as character pointers.
    */
    static char     *regItems[3] = { NULL, NULL, NULL };
    static char     *regNames[3] = { "depthFile", "outFile", "tileFile" };
    static int      regCount = 0;
    static int      regHitCount = 0;

    static char     *depItems[3] = { "15.0", "25.0", "30.0" };
    static char     *depNames[3] = { "minDepth", "maxDepth", "zeroDepth" };
    static int      depCount = 0;
    static int      depHitCount = 0;

    static char     *dpiItems[1] = { "72.0" };
    static char     *dpiNames[1] = { "pixelsPerUnit" };
    static int      dpiCount = 0;
    static int      dpiHitCount = 0;

    static char     *ioItems[1] = { "2.5" };
    static char     *ioNames[1] = { "iodistance" };
    static int      ioCount = 0;
    static int      ioHitCount = 0;

    static char     *dstItems[1] = { "15.0" };
    static char     *dstNames[1] = { "distanceToScreen" };
    static int      dstCount = 0;
    static int      dstHitCount = 0;

    static int      verCount = 0;
    static int      verHitCount = 0;


    static PARSETABLE   pTable[] = {
        { "",            2, 3, regItems, regNames, &regHitCount, &regCount },
        { "depthVals",   2, 3, depItems, depNames, &depHitCount, &depCount },
        { "dpi",         1, 1, dpiItems, dpiNames, &dpiHitCount, &dpiCount },
        { "interOcular", 1, 1, ioItems,  ioNames,  &ioHitCount,  &ioCount  },
        { "screen",      1, 1, dstItems, dstNames, &dstHitCount, &dstCount },
        { "verbose",     0, 0, NULL,     NULL,     &verHitCount, &verCount },
                                };


   /* Here are our local variables. */

   /* This stuff for reading/writing files. */
    JPEG_DS         *iFile, *tFile;
    JPEG_CS         *oFile;
    FILE            *fIn, *fOut, *fTile;

   /* Stuff related to the file data & size. */
    unsigned char   *inData, *outData, *tileFullData;
    float           *tileData[4];
    char            *inFilename, *outFilename, *tileFilename;
    int             across, down, numInChans, numTileChans;
    int             tileWidth, tileHeight;

   /* And stuff related to the actual SIRD algorithm. */
    float           minDepth, maxDepth, zeroDepth;
    float           screenDistance, dpi, io;
    float           *infloatData, *outfloatData, result;
    int             zeroSize;

   /* Misc */
    char            *progname;
    int             i, j, scan, numFlags, tileLine;


   /*
    * 1: Parse the arguments. 
    */

    numFlags = sizeof(pTable)/sizeof(PARSETABLE);
    progname = argv[0];

    if(parseArgs(argc, argv, numFlags, pTable))
        printUsage(progname, numFlags, pTable, "(C) Copyright 1995 Bark of Delight");


   /* Parsing ok, set our parameters from it. */
    inFilename   = regItems[0];
    outFilename  = regItems[1];
    tileFilename = regItems[2];

    minDepth = atof(depItems[0]);
    maxDepth = atof(depItems[1]);

   /* Default zeroDepth to a reasonable value, namely the maxDepth. */
    if(depCount == 2)
        zeroDepth = maxDepth;
    else
        zeroDepth = atof(depItems[2]);

   /* dpi, io, screen */
    dpi            = atof(dpiItems[0]);
    io             = atof(ioItems[0]);
    screenDistance = atof(dstItems[0]);

    if(verHitCount)
        {
        fprintf(stderr, "Input %s, output %s", inFilename, outFilename);
        if(tileFilename != NULL)
            fprintf(stderr, " tile %s", tileFilename);

        fprintf(stderr, "\nDepths are %g, %g, and %g\n",
                                        minDepth, maxDepth, zeroDepth);
        fprintf(stderr, "To screen is %g, interocular is %g, dpi is %g\n",
                                                    screenDistance, io, dpi);
        }  /* if verbose */


   /* 
    * 2: Open the files
    */

   /* We compute the zeroSize here.  It's the number of pixels which
    *  correspond to the zeroDepth.  We need it because we're going to
    *  output a file which is larger than the input depth, by this amount.
    */
    zeroSize = (int)(depthToOffset(zeroDepth, screenDistance, dpi, io) + 1.0);


   /* If we have a tile file, we read it entirely first. */
    if(tileFilename != NULL)
        {
       /* Open the raw file. */
        if((fTile = fopen(tileFilename, "r")) == NULL)
            bail(progname, "Cannot open tile file.");

       /* Call the jpeg read routines. */
        tFile = openJPEGRead(fTile, &tileWidth, &tileHeight, &numTileChans);


       /* Get some space to put the whole image. */
        tileFullData = (unsigned char *)malloc(tileHeight * tileWidth 
                                                            * numTileChans);

       /* Read the whole image into memory. */
        for(scan=0; scan<tileHeight; ++scan)
            readJPEGScanline(tFile,
                        tileFullData + (scan * tileWidth * numTileChans));

       /* Close */
        closeJPEGRead(tFile);
        fclose(fTile);

        }  /* if we have a tile file specified by the user. */

   /* If we are generating our own data randomly.
    *  Note we can choose any size for the tile pattern.  It does not
    *  matter.  But we know it won't get any smaller than the offset
    *  caused by the minimum depth.  So we'll use that as our base.
    */
    else
        {
        tileWidth = (int)(depthToOffset(minDepth, screenDistance, dpi, io)+1.0);

       /* And it's black & white. */
        numTileChans = 1;

        }  /* If no tile file specified. */

    
   /* Set up the jpeg read first. */
    if((fIn = fopen(inFilename, "r")) == NULL)
        bail(progname, "Cannot open input file.");

    iFile = openJPEGRead(fIn, &across, &down, &numInChans);

   /* Allocate room for our input data.
    *  inData is the raw scans.
    *  infloatData is converted to the range 0.0-1.0.
    */
    inData      = (unsigned char *)malloc(across * numInChans * sizeof(char));
    infloatData = (float *)malloc(across * sizeof(float));



   /* Now set up the jpeg write. */
    if((fOut = fopen(outFilename, "w")) == NULL)
        bail(progname, "Cannot open output file.");

   /* Note that this image is larger. */
    oFile = openJPEGWrite(fOut, across+zeroSize, down, numTileChans);

   /* Allocate room for our output data, same idea as above. */
    outData = (unsigned char *)malloc((across+zeroSize) *
                                            numTileChans * sizeof(char));
    outfloatData = (float *)malloc((across+zeroSize) * sizeof(float));


   /* We need room for the tile data -- a scanline per channel. */
    for(i=0; i<numTileChans; ++i)
        tileData[i] = (float *)malloc((tileWidth+1) * sizeof(float));


   /*
    * 3: Generate the stereogram.
    *
    *  This can be broken down into a few parts:
    *   a: get the tile data.
    *   b: get the depth data.
    *   c: for each point in the output, calculate a tile position.
    *   d: fill the output.
    *   e: write the output.
    */
    for(scan = 0; scan < down; ++scan)
        {
       /* a: get the tile data.  If we have a file, we move
        *  a scanline from the right place into our buffers.
        *  We convert to the range 0.0-1.0, as always.
        */
        if(tileFilename != NULL)
            {
           /* Choose a line which lies in the tile area. */
            tileLine = scan % tileHeight;

           /* Move over as many scanlines as we have. */
            for(i=0; i<tileWidth; ++i)
                for(j=0; j<numTileChans; ++j)
                    tileData[j][i] = 
                        tileFullData[(tileLine * tileWidth * numTileChans)
                                            + (i * numTileChans) + j] / 255.0;
            }  /* if there's a tile file */

       /* Make a random tile row.
        *  You can use any scheme you want.  I use this one because
        *  it give me the brightness I like.  Just a personal choice.
        */
        else
            for(i=0; i<tileWidth; ++i)
                for(j=0; j<numTileChans; ++j)
                    tileData[j][i] = pow(drand48(), 3.0);


       /* Copy the first entry into the last -- this simplfies
        *  a bunch of code.  Classic trick.
        */
        for(j=0; j<numTileChans; ++j)
            tileData[j][tileWidth] = tileData[j][0];

       /* b: Read the input file data! */
        readJPEGScanline(iFile, inData);

       /* Convert to depth data, range 0.0 to 1.0.
        *  We'll just add up what we have and divide by the number
        *  of channels.
        */
        for(i=0; i<across; ++i)
            {
            result = 0;
            for(j=0; j<numInChans; ++j)
                result += inData[i*numInChans+j];

            infloatData[i] = result / (255.0 * numInChans);
            }


       /* c: For each point in the output array, calculate a tile position.
        *  For the first "zeroSize" pixels, we can't use the standard
        *  formula, because we don't have depth data for these pixels.
        *  So we fill them in with evenly spaced tile data.
        */
        for(i=0; i<zeroSize; ++i)
            outfloatData[i] = (float)tileWidth * (float)i / (float)zeroSize;

       /* Now loop through the rest of the output scanline and 
        *  create a list of indices.  findIndex does this quite nicely.
        */
        for(i=0; i<across; ++i)
            outfloatData[i+zeroSize] = tileWidth *
                            findIndex((float)i, infloatData, minDepth,
                            maxDepth, zeroDepth, screenDistance, dpi, io);
                        
       /* d: Fill the output with the pixel data.
        *  Note the call to findInterp.  Remember the result of findIndex
        *  is a floating point number.  This is important.  You need to
        *  use floating point and interpolate.  (Thanks, Greg!)
        */
        for(i=0; i<across+zeroSize; ++i)
            for(j=0; j<numTileChans; ++j)
                {
                result = findInterp(tileData[j], outfloatData[i]);
                outData[(i*numTileChans)+j] = (unsigned char)(result * 255.0);
                }

       /* e: Write the output. */
        writeJPEGScanline(oFile, outData);
        }

   /* Close everything and we are done! */
    closeJPEGRead(iFile);
    closeJPEGWrite(oFile);

    fclose(fIn);
    fclose(fOut);

    exit(0);

}  /* main */



/*----------------------------------------------------------------------
 |                           findIndex 
 *----------------------------------------------------------------------
 |
 |  Name:   findIndex
 |
 |  Function:  Given an input line of depth data and a location in it,
 |              determine the index into the tile array to use for
 |              the autostereogram.
 |
 |  usage:
 |          float   findIndex(
 |              float           location,       Location to find
 |              float           *depthData,     Depth data
 |              float           minDepth,       Minimum depth past screen
 |              float           maxDepth,       Maximum depth past screen
 |              float           zeroDepth,      Depth that a zero means
 |              float           screenDistance, Distance from viewer to screen
 |              float           dpi,            Pixels per unit
 |              float           io              Interocular distance
 |              ) 
 |
 |  Return:  The index into the tile array, in the range 0.0 - 1.0.
 |
 |  Notes:  The idea here is this:  For each depth, we need to choose
 |          a pattern width.  The larger the width of the pattern, the
 |          furter away the image appears.  The smaller the width, the
 |          closer it appears.  You can use an arbitrary formula to
 |          go from on to the other, ignoring such "real world" parameters
 |          as screendistance and io, but the right formula is done in 
 |          the routine depthToOffset().
 |          After determining the pattern width, we subtract it from
 |          the current location and return the same index that that
 |          location has.  You see, it has to match.  And yes, it's
 |          a recursive algorithm.  That's the beauty.
 |          If the target match location is off the image, we stop,
 |          and return a value from 0 to 1 based upon how far off it
 |          is.  The maximum is what the zeroDepth value would have
 |          given us, so we use that as a normalization factor.
 */

float   findIndex(
    float           location,
    float           *depthData,
    float           minDepth,
    float           maxDepth,
    float           zeroDepth,
    float           screenDistance,
    float           dpi,
    float           io
    )
{

    float   depth, offset, depthVal;

   /* Find the depth at this location.  Depths range 0-1. */
    depthVal = findInterp(depthData, location);

   /* Special case 0 because often people want to do that. */
    if(depthVal <= 0)
        depth = zeroDepth;

   /* Otherwise, linearly interpolate. */
    else
        depth = maxDepth + ((minDepth-maxDepth) * depthVal);

   /* Get the offset which corresponds to this depth. */
    offset = depthToOffset(depth, screenDistance, dpi, io);

   /* Now subtract the offset from our location.  If that puts us out of
    *  bounds, we need to return it, put into the proper range
    *  (which is 0.0 to 1.0)
    */
    if((location-offset) < 0.0)
        return(1.0 + ((location-offset) /
                        depthToOffset(zeroDepth, screenDistance, dpi, io)));

   /* Ok, we're not out of bounds, we call ourselves recursively on
    *  the next location.
    */
    else
        return(findIndex((location-offset), depthData, minDepth, maxDepth,
                                        zeroDepth, screenDistance, dpi, io));

}  /* findIndex */



/*----------------------------------------------------------------------
 |                         depthToOffset 
 *----------------------------------------------------------------------
 |
 |  Name:   depthToOffset
 |
 |  Function:  Convert a depth value into a pixel offset.
 |
 |  usage:
 |          float   depthToOffset(
 |                  float   depth,              Depth to convert
 |                  float   screenDistance,     Distance from viewer to screen
 |                  float   dpi,                Pixels per unit
 |                  float   io                  Interocular distance
 |                  ) 
 |
 |  Return:  The offset value, in pixels.
 |
 |  Notes:  Note that the return value is a floating point number.
 |          That's important.   The formula is this:
 |
 |              offset = (depth * io * dpi) / (depth + screenDistance)
 |
 |          Where does this come from?  Well, the dpi part is just to
 |          convert the units into pixels.  Without that, it's a simple
 |          geometry problem.  Your eyes are io units apart, and they
 |          both see a point which is (depth+screenDistance) away.  The
 |          paths from your eyes to the point intersect the screen at
 |          two points.  Similar triangles say that the ratio of the total
 |          distance to your interocular distance is the same as the
 |          ratio of the depth to the distance between these two points.
 |
 |              (depth + screenDistance) / io = depth / offset
 |
 |          Solving for offset leads directly to the result.
 */

float   depthToOffset(
        float   depth,
        float   screenDistance,
        float   dpi,
        float   io
        )
{

   /* just return the magic value. */
    return((depth * io * dpi) / (depth + screenDistance));

}  /* depthToOffset */


/*----------------------------------------------------------------------
 |                          findInterp 
 *----------------------------------------------------------------------
 |
 |  Name:   findInterp
 |
 |  Function:  Interpolate a value in an array of values.
 |
 |  usage:
 |          float   findInterp(
 |              float   *vals,              Value array
 |              float   location            Floating point index to array
 |              ) 
 |
 |  Return:  An interpolated value at the given location.
 */

float   findInterp(
    float   *vals,
    float   location
    )
{
    
    float   fract;

    fract = location - (int)location;

    return((vals[(int)location] * (1.0 - fract))
                        + (vals[(int)location + 1] * fract));

}  /* findInterp */




/*----------------------------------------------------------------------
 |                           parseArgs 
 *----------------------------------------------------------------------
 |
 |  Name:   parseArgs
 |
 |  Function:  Parse a set of input args.
 |
 |  usage:
 |          int     parseArgs(
 |              int         argc,               Number of args
 |              char        **argv,             The arg list
 |              int         numParseTable,      Size of parse table
 |              PARSETABLE  *parseTable         Pointer to the table
 |
 |  Return: 0 if ok, -1 if not.  Many things which the parseTable
 |          points to indirectly are modified.
 |
 |  Notes:  I just hate the UNIX standard of using single lettered
 |          flags.  Isn't it nice to have full English words to
 |          remember?
 */

int     parseArgs(
    int         argc,
    char        **argv,
    int         numParseTable,
    PARSETABLE  *parseTable
    )
{
    int         i, j;
    char        *curString;
    PARSETABLE  *flagEntry, *curFlagEntry;

    curFlagEntry = parseTable;

   /* Init the items we want init'ed. */
    for(j=0; j<numParseTable; ++j)
        *(parseTable[j].hit) = *(parseTable[j].numItems) = 0;

    for(i=1; i<argc; ++i)
        {
        curString = argv[i];

       /* Is this a regular item, or a flag?
        *  If it starts with "-" it is a flag unless
        *  (a) that's all
        *  (b) the next character is a number or a period.
        */
        if((curString[0] == '-') &&
                    ((curString[1] != 0) && (curString[1] != '.') &&
                                                (!isdigit(curString[1]))))
            {
            flagEntry = NULL;

           /* A flag, look and see what one. */
            for(j=0; j<numParseTable; ++j)
                {
                if(!strcasecmp(curString+1, parseTable[j].flagName))
                    {
                    flagEntry = parseTable+j;
                    break;
                    }

                }  /* for j */


           /* If still no match, look for just as much as the
            *  user gave us.
            */
            if(flagEntry == NULL)
                for(j=0; j<numParseTable; ++j)
                    {
                    if(!strncasecmp(curString+1, parseTable[j].flagName,
                                                        strlen(curString+1)))
                        {
                        flagEntry = parseTable+j;
                        break;
                        }

                    }  /* for j */

           /* Did we find it?   If so, it becomes our current flag. */
            if(flagEntry != NULL)
                {
                curFlagEntry = flagEntry;
                *(flagEntry->hit) = 1;
                }

            else
                {
                fprintf(stderr, "Parsing error, unrecognized flag '%s'.\n",
                                                                    curString);
                return(-1);
                }
                
            } /* if a flag. */

       /* A regular thing.  Add it to our current entry, if it fits. */
        else
            {
           /* If we can add this item, do so */
            if(*(curFlagEntry->numItems) < curFlagEntry->maxItems)
                curFlagEntry->items[(*(curFlagEntry->numItems))++] = curString;
                
           /* If not, try to add it to the default (first item). */
            else if(*(parseTable[0].numItems) < parseTable[0].maxItems)
                parseTable[0].items[(*(parseTable[0].numItems))++] = curString;

            else
                {
                fprintf(stderr, "Parsing error, extra parameter, '%s'.\n",
                                                                curString);
                return(-1);
                }

            } /* if a regular string. */

        }  /* for each item given */

   /* Now loop through and make sure that we got our minimums. */
    for(j=0; j<numParseTable; ++j)
        if(((j == 0) || *(parseTable[j].hit)) &&
                        (*(parseTable[j].numItems) < parseTable[j].minItems))
            return(-1);

    return(0);

}  /* parseArgs */



/*----------------------------------------------------------------------
 |                        printUsage 
 *----------------------------------------------------------------------
 |
 |  Name:   printUsage
 |
 |  Function:  Print the usage.
 |
 |  usage:
 |          void        printUsage(
 |              char        *progName,          Program name
 |              int         numParseTable,      Size of parse table
 |              PARSETABLE  *parseTable,        Pointer to parse table
 |              char        *extraMessage       Additional message to print
 |
 |  Notes:  This routine exits the program.
 */

void        printUsage(
    char        *progName,
    int         numParseTable,
    PARSETABLE  *parseTable, 
    char        *extraMessage
    )
{
    
    int         i, j;
    PARSETABLE  *curFlag;

    fprintf(stderr, "Usage: %s", progName);

    for(i=0, curFlag = parseTable; i<numParseTable; ++i, ++curFlag)
        {
       /* First item is the "normal" parameters, everything
        *  else is optional.
        */
        if(i > 0)
            fprintf(stderr, "        [-%s", curFlag->flagName);

       /* Print the names which are required. */
        for(j=0; j<curFlag->minItems; ++j)
            fprintf(stderr, " %s", curFlag->itemNames[j]);

       /* If any more, print them inside brackets. */
        if(curFlag->maxItems > curFlag->minItems)
            {
            fprintf(stderr, " [%s", curFlag->itemNames[curFlag->minItems]);

            for(j=curFlag->minItems+1; j<curFlag->maxItems; ++j)
                fprintf(stderr, " %s", curFlag->itemNames[j]);
            
            fprintf(stderr, "]]\n");
            }
        
        else
            fprintf(stderr, "]\n");

        } /* for i */

    fprintf(stderr, "\n");

    if(extraMessage != NULL)
        fprintf(stderr, "%s\n\n", extraMessage);

    exit(-1);

}  /* printUsage */




/*----------------------------------------------------------------------
 |                             bail 
 *----------------------------------------------------------------------
 |
 |  Name:   bail
 |
 |  Function:
 |
 |  usage:
 |          void    bail(
 |              char    *progname,          Program name
 |              char    *message            Message to print
 |
 |  Notes:  This routine exits the program.  It also prints out
 |          any error encountered if errno is not zero.
 */

void    bail(
    char    *progname,
    char    *message
    )
{
    extern int  errno;

    fprintf(stderr, "%s : %s\n", progname, message);
    if(errno)
        perror("Error encountered");
            
    exit(-1);

}  /* bail */



/*
 *  From here down are image read & write routines.
 *  These routines are cleanup of the examples provided
 *  with the IJG JPEG package.
 *
 *
 *  These routines stand alone -- they only "talk" to the
 *  main program via returned pointers, which are allocated
 *  here.  So you can replace all of the routines below this
 *  point with any other image read/write routines if you'd like.
 */

#ifdef __cplusplus
extern "C" {
#endif
#include "jpeglib.h"
#ifdef __cplusplus
}
#endif


/* These routines are required by the jpeg error handlers. */
static struct jpeg_error_mgr ReadJerr, WriteJerr;




/*----------------------------------------------------------------------
 |                        openJPEGWrite 
 *----------------------------------------------------------------------
 |
 |  Name:   openJPEGWrite
 |
 |  Function:  Open a jpeg file for write.
 |
 |  usage:
 |          JPEG_CS *openJPEGWrite(
 |                  FILE    *outfile,           File pointer
 |                  int     width,              Width (x resolution)
 |                  int     height,             Height (y resolution)
 |                  int     numChans            Number of color channels
 |
 |  Return:  A pointer to the JPEG_CS structure for later use when
 |           writing scanlines.
 |
 |  Notes:  numChans can only be 1 or 3, really.  1 for a grayscale
 |          image, and 3 for a color image.  The jpeg libraries don't
 |          return error codes; you can define your own error handlers
 |          but I'm using the default ones here which will just print
 |          out error messages and exit.
 */

JPEG_CS *openJPEGWrite(
    FILE    *outfile,
    int     width,
    int     height,
    int     numChans
    )
{

    JPEG_CS *cInfo;

   /* Create a structure to be returned. */
    cInfo = (JPEG_CS *)malloc(sizeof(JPEG_CS));

   /* Use the standard error routines. */
    cInfo->err = jpeg_std_error(&WriteJerr);
    jpeg_create_compress(cInfo);
    jpeg_stdio_dest(cInfo, outfile);

   /* Set some parameters. */
    cInfo->image_width = width; 
    cInfo->image_height = height;
    cInfo->input_components = numChans;

   /* Choose our color space appropriately */
    if(numChans == 1)
        cInfo->in_color_space = JCS_GRAYSCALE;
    else
        cInfo->in_color_space = JCS_RGB;

   /* Call the library routines. */
    jpeg_set_defaults(cInfo);
    jpeg_start_compress(cInfo, TRUE);

   /* And return the structure. */
    return(cInfo);

}  /* openJPEGWrite */



/*----------------------------------------------------------------------
 |                       writeJPEGScanline 
 *----------------------------------------------------------------------
 |
 |  Name:   writeJPEGScanline
 |
 |  Function:  Write a single scanline.
 |
 |  usage:
 |          void    writeJPEGScanline(
 |                  JPEG_CS         *cInfo,         The open JPEG file pointer
 |                  unsigned char   *src            Pointer to the data.
 */

void    writeJPEGScanline(
    JPEG_CS         *cInfo,
    unsigned char   *src
    )

{

   /* Just call the library routine. */
    jpeg_write_scanlines(cInfo, &src, 1);

}  /* writeJPEGScanline */



/*----------------------------------------------------------------------
 |                        closeJPEGWrite 
 *----------------------------------------------------------------------
 |
 |  Name:   closeJPEGWrite
 |
 |  Function:  Close a jpeg file previously opened for writing.
 |
 |  usage:
 |          void    closeJPEGWrite(
 |                  JPEG_CS     *cInfo              The open JPEG file pointer
 */

void    closeJPEGWrite(
    JPEG_CS     *cInfo
    )
{

   /* Just call the library routines. */
    jpeg_finish_compress(cInfo);
    jpeg_destroy_compress(cInfo);

}  /* closeJPEGWrite */




/*----------------------------------------------------------------------
 |                         openJPEGRead 
 *----------------------------------------------------------------------
 |
 |  Name:   openJPEGRead
 |
 |  Function:  Open a jpeg file for write.
 |
 |  usage:
 |          JPEG_DS *openJPEGRead(
 |                  FILE    *infile             File pointer
 |                  int     *width,             Width of image (returned)
 |                  int     *height,            Height of image (returned)
 |                  int     *numChans           Number of channels (returned)
 |
 |  Return:  A pointer to the JPEG_DS structure for later use when
 |           reading scanlines, and the width, height, and numChans filled.
 |
 |  Notes:  The jpeg libraries don't return error codes; you can 
 |          define your own error handlers but I'm using the default 
 |          ones here which will just print out error messages and exit.
 */

JPEG_DS *openJPEGRead(
    FILE    *infile,
    int     *width,
    int     *height,
    int     *numChans
    )
{

    JPEG_DS *dInfo;

   /* Create a structure to be returned. */
    dInfo = (JPEG_DS *)malloc(sizeof(JPEG_DS));

   /* Use the standard error routines. */
    dInfo->err = jpeg_std_error(&ReadJerr);

   /* Call the library routines. */
    jpeg_create_decompress(dInfo);
    jpeg_stdio_src(dInfo, infile);
    jpeg_read_header(dInfo, TRUE);
    jpeg_start_decompress(dInfo);

   /* Fill the width, height, & numChans. */
    *width = dInfo->image_width;
    *height = dInfo->image_height;
    *numChans = dInfo->output_components;

   /* And return the structure. */
    return(dInfo);

}  /* openJPEGRead */


/*----------------------------------------------------------------------
 |                       readJPEGScanline 
 *----------------------------------------------------------------------
 |
 |  Name:   readJPEGScanline
 |
 |  Function:  Read a single scanline.
 |
 |  usage:
 |          void    readJPEGScanline(
 |                  JPEG_DS         *dInfo,         The open JPEG file pointer
 |                  unsigned char   *dest           Pointer to destination
 */

void    readJPEGScanline(
    JPEG_DS         *dInfo,
    unsigned char   *dest
    )
{

   /* Just call the library routine. */
    jpeg_read_scanlines(dInfo, &dest, 1);

}  /* readJPEGScanline */



/*----------------------------------------------------------------------
 |                        closeJPEGRead 
 *----------------------------------------------------------------------
 |
 |  Name:   closeJPEGRead
 |
 |  Function:  Close a jpeg file previously opened for reading.
 |
 |  usage:
 |          void    closeJPEGRead(
 |                  JPEG_DS     *dInfo              The open JPEG file pointer
 */

void    closeJPEGRead(
    JPEG_DS *dInfo
    )
{

   /* Just call the library routines. */
    jpeg_finish_decompress(dInfo);
    jpeg_destroy_decompress(dInfo);

}  /* closeJPEGRead */



