/**************************************************************************
 *{@C
 *	Copyright:	1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *	Module:		FileFormats
 *	Filename:	FF_Image.c
 *
 *	Author:		Paul Obermeier
 *
 *	Description: 	Functions for reading and writing image files.
 *			This file combines all available format parser.
 *
 *			Currently available formats are:
 *
 * 			Format			Shortcut
 * 			--------------------------------
 *			poSoft format		po
 *			poSoft Raw format	raw
 *			Silicon Graphics format	sgi
 *			Truevision Targa format	targa
 *
 *	Additional documentation:
 *			None.
 *
 *	Exported functions:
 *			FF_ImgNew
 *			FF_ImgDelete
 *			FF_ImgUBytePixel
 *			FF_ImgUByteRow
 *			FF_ImgFloatPixel
 *			FF_ImgFloatRow
 *			FF_ImgOpenWrite
 *			FF_ImgOpenRead
 *			FF_ImgClose
 *			FF_ImgSetSrcImg
 *			FF_ImgSetDestImg
 *			FF_ImgWriteRow
 *			FF_ImgReadRow
 *			FF_ImgGetFmtTypeName
 *			FF_ImgGetChanTypeName
 *			FF_ImgGetComprTypeName
 *
 **************************************************************************/

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "UT_Compat.h"
#include "UT_Macros.h"
#include "UT_Error.h"
#include "UT_Memory.h"

#include "FF_Image.h"
#include "FF_ImagePrivate.h"
#include "FF_ImagePoSoft.h"
#include "FF_ImageSgi.h"
#include "FF_ImageTarga.h"
#include "FF_ImageRaw.h"

#define str_InvalidFmtName    "Invalid channel format name: %s.\n"
#define str_InvalidTypeName   "Invalid channel type name: %s.\n"
#define str_InvalidComprName  "Invalid channel compression name: %s.\n"

/* The error message texts for the 3D-model file format library. */

static const char * const errMsgs[] = {
    /*  0 */  "File does not contain pixel data",
    /*  1 */  "Syntax error in pixel file",
    /*  2 */  "Unknown pixel file format",
    /*  3 */  "Unknown pixel file format version",
    /*  4 */  "Pixel file format version cannot be used",
};

/* The error message list for the image file format library. */

UT_ErrBase FF_ImgErrList;

/* Possible formats of the pixel data.
 * Note: This must be synchronized with the enumeration FF_ImgFmtType in
 * header file FF_Image.h.
 */
static const char *fmtTypeNames[] = {
    "NONE", "UBYTE", "FLOAT"
};

/* Possible channel types of an image. 
 * Note: This must be synchronized with the enumeration FF_ImgChanType in
 * header file FF_Image.h.
 */
static const char *chanTypeNames[] = {
    "BW", "RED", "GREEN", "BLUE",
    "MATTE", "REDMATTE", "GREENMATTE", "BLUEMATTE",
    "HORINORMAL", "VERTNORMAL", "DEPTH",
    "TEMPERATURE", "RADIANCE"
};

/* Possible compression methods of an image.
 * Note: This must be synchronized with the enumeration FF_ImgComprType in
 * header file FF_Image.h.
 */
static const char *comprTypeNames[] = {
    "NONE", "RLE"
};

/* A file-format specific set of functions to read and write a pixel file */

typedef struct {
    char	*format;
    UT_Bool	(* open_write) (const char*, FF_ImgFileDesc *);
    UT_Bool	(* open_read)  (const char*, FF_ImgFileDesc *);
} imgFunctType;

imgFunctType imgFunctTable[] = {
    /* poSoft image file */
    {
	"po",
	FF_ImgOpenWritePoSoft,
	FF_ImgOpenReadPoSoft
    },

    /* Silicon Graphics image file */
    {
	"sgi",
	FF_ImgOpenWriteSgi,
	FF_ImgOpenReadSgi
    },

    /* Truevision Targa image file */
    {
	"targa",
	FF_ImgOpenWriteTarga,
	FF_ImgOpenReadTarga
    },

    /* poSoft Raw image file */
    {
	"raw",
	FF_ImgOpenWriteRaw,
	FF_ImgOpenReadRaw
    }
};

static Int32 numFormats = sizeof (imgFunctTable) / sizeof (imgFunctType);

static void initialize (void)
{
    static UT_Bool initialized = UT_False;

    if (!initialized) {
	UT_ErrSupply (errMsgs, sizeof(errMsgs)/sizeof(char *), &FF_ImgErrList);
	initialized = UT_True;
    }
    return;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgNew
 *
 *	Usage:		Allocate memory to store the pixels of an image.
 *
 *	Synopsis:	UT_Bool FF_ImgNew (FF_ImgDesc *img)
 *
 *	Description:	FF_ImgNew allocates memory to store pixel
 *			data in scan lines "firstscan" to "lastscan"
 *			of image "img" and stores pointers to the allocated
 *			memory in the appropriate entries of the "pixelptr"
 *			array of image "img", so that the FF_ImgUByteRow,
 *			FF_ImgUBytePixel, FF_ImgFloatRow and FF_ImgFloatPixel
 *			functions work for all scan lines in the range from
 *			"firstscan" to "lastscan".
 *
 *			Notes:
 *
 *			- The "desc.width", "desc.height", "channel",
 *			  "firstscan" and "lastscan" fields of "img" must
 *			  be set before FF_ImgNew is invoked.
 *			  FF_ImgNew reads these fields in order to
 *			  determine how much memory must be allocated.
 *
 *			- Memory allocated with FF_ImgNew must be
 *			  deallocated with FF_ImgDelete.
 *
 *			- The "firstscan" and "lastscan" fields of an image
 *			  must not be altered after pixel data memory has
 *			  been invoked for that image.
 *
 *	Return value:	UT_True if successful, else UT_False.
 *
 *	See also:	FF_ImgDelete
 *
 ***************************************************************************/

UT_Bool FF_ImgNew (FF_ImgDesc *img)
{
    UT_MemState memstate;
    Int32 i, j, nscan, npix;
    UInt8 *uptr;
    Float32 *fptr;

    memstate = UT_MemRemember ();
    if (img->lastscan < img->firstscan) {
	UT_ErrFatal ("FF_ImgNew", "Invalid number of scan lines");
    }

    nscan = img->lastscan - img->firstscan + 1;
    npix = nscan * img->desc.width;

    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	switch (img->channel[i]) {
	    case FF_ImgFmtTypeNone:
		break;
	    case FF_ImgFmtTypeUByte:
		if (!(img->pixelptr[i].ubytes = (UInt8 **)
		      UT_MemTempArray (img->desc.height, UInt8 *))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		if (!(uptr = UT_MemTempArray (npix, UInt8))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		for (j = img->firstscan; j <= img->lastscan; ++j) {
		    img->pixelptr[i].ubytes[j] = uptr;
		    uptr += img->desc.width;
		}
		break;
	    case FF_ImgFmtTypeFloat:
		if (!(img->pixelptr[i].floats = (Float32 **)
		      UT_MemTempArray (img->desc.height, Float32 *))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		if (!(fptr = UT_MemTempArray (npix, Float32))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		for (j = img->firstscan; j <= img->lastscan; ++j) {
		    img->pixelptr[i].floats[j] = fptr;
		    fptr += img->desc.width;
		}
		break;
	    default:
		UT_ErrFatal ("FF_ImgNew", "Invalid pixel format");
	}
    }
    UT_MemSave (memstate);
    return UT_True;
}


/***************************************************************************
 *[@e
 *	Name:  		FF_ImgDelete
 *
 *	Usage:		Free the memory used for the pixels of an image.
 *
 *	Synopsis:	void FF_ImgDelete (FF_ImgDesc *img)
 *
 *	Description:	Free the memory used by image "img".
 *
 *	Return value:	None.
 *
 *	See also:	FF_ImgNew
 *
 ***************************************************************************/

void FF_ImgDelete (FF_ImgDesc *img)
{
    Int32 i;

    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	switch (img->channel[i]) {
	    case FF_ImgFmtTypeNone:
	        break;
	    case FF_ImgFmtTypeUByte:
		UT_MemFree (img->pixelptr[i].ubytes[img->firstscan]);
		UT_MemFree (img->pixelptr[i].ubytes);
		break;
	    case FF_ImgFmtTypeFloat:
		UT_MemFree (img->pixelptr[i].floats[img->firstscan]);
		UT_MemFree (img->pixelptr[i].floats);
		break;
	    default:
		UT_ErrFatal ("FF_ImgDelete", "Invalid pixel format");
	}
    }
    return;
}


/***************************************************************************
 *[@e
 *	Name:  		FF_ImgUByteRow
 *
 *	Usage:		Get the address of an image row containing UByte values.
 *
 *	Synopsis:	UInt8 *FF_ImgUByteRow
 *				(FF_ImgDesc *img, FF_ImgChanType chan, Int32 y)
 *
 *	Description:	Get the address of the beginning of scan line number "y"
 *			of channel "chan" of image "img". 
 *			"Chan" must be stored as UInt8's (i.e. 
 *			img->pixelptr.format must be equal to 
 *			"FF_ImgFmtTypeUByte"), and memory must have been
 *			allocated for scan line number "y" of the image.
 *
 *			Note: You can switch to a macro version of this
 *			function by defining the preprocessor symbol
 *			USE_IMG_MACROS.
 *
 *	Return value:	The address of the beginning of scan line number "y"
 *			of channel "chan" of image "img".
 *
 *	See also:	FF_ImgFloatRow
 *			FF_ImgUBytePixel
 *
 ***************************************************************************/

UInt8 *FF_ImgUByteRow (FF_ImgDesc *img, FF_ImgChanType chan, Int32 y)
{
    if (img->channel[(Int32) chan] != FF_ImgFmtTypeUByte ||
	y < img->firstscan || y > img->lastscan) {
	UT_ErrFatal ("FF_ImgUByteRow", "Invalid parameters");
    }
    return img->pixelptr[(Int32) chan].ubytes[y];
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgUBytePixel
 *
 *	Usage:		Get the address of a pixel containing a UByte value.
 *
 *	Synopsis:	UInt8 *FF_ImgUBytePixel
 *				(FF_ImgDesc *img, FF_ImgChanType chan,
 *				 Int32 x, Int32 y)
 *
 *	Description:	Get the address of the pixel (x, y) of channel "chan" of
 *			image "img". Chan must be stored as UInt8's (i.e.
 *			img->pixelptr.format must be equal to 
 *			"FF_ImgFmtTypeUByte"), and memory must have been
 *			allocated for scan line number "y" of the image.
 *
 *			Note: You can switch to a macro version of this
 *			function by defining the preprocessor symbol
 *			USE_IMG_MACROS.
 *
 *	Return value:	The address of the pixel (x, y) of channel "chan" of
 *			image "img". 
 *
 *	See also:	FF_ImgFloatPixel
 *			FF_ImgUByteRow
 *
 ***************************************************************************/

UInt8 *FF_ImgUBytePixel (FF_ImgDesc *img, FF_ImgChanType chan, Int32 x, Int32 y)
{
    if (img->channel[(Int32) chan] != FF_ImgFmtTypeUByte ||
	y < img->firstscan || y > img->lastscan ||
	x < 0 || x > img->desc.width) {
	UT_ErrFatal ("FF_ImgUBytePixel", "Invalid parameters");
    }
    return img->pixelptr[(Int32) chan].ubytes[y] + x;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgFloatRow
 *
 *	Usage:		Get the address of an image row containing Float values.
 *
 *	Synopsis:	Float32 *FF_ImgFloatRow
 *				(FF_ImgDesc *img, FF_ImgChanType chan, Int32 y)
 *
 *	Description:	Get the address of the beginning of scan line number "y"
 *			of channel "chan" of image "img". "Chan" must be stored
 *			as Float32s (i.e. img->pixelptr.format must be equal to
 *			"FF_ImgFmtTypeFloat"), and memory must have been
 *			allocated for scan line number "y" of the image.
 *
 *			Note: You can switch to a macro version of this
 *			function by defining the preprocessor symbol
 *			USE_IMG_MACROS.
 *
 *	Return value:	The address of the beginning of scan line number "y"
 *			of channel "chan" of image "img".
 *
 *	See also:	FF_ImgUByteRow
 *			FF_ImgFloatPixel
 *
 ***************************************************************************/

Float32 *FF_ImgFloatRow (FF_ImgDesc *img, FF_ImgChanType chan, Int32 y)
{
    if (img->channel[(Int32) chan] != FF_ImgFmtTypeFloat ||
	y < img->firstscan || y > img->lastscan)
	UT_ErrFatal ("FF_ImgFloatRow", "Invalid parameters");
    return img->pixelptr[(Int32) chan].floats[y];
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgFloatPixel
 *
 *	Usage:		Get the address of a pixel containing a Float value.
 *
 *	Synopsis:	Float32 *FF_ImgFloatPixel
 *				(FF_ImgDesc *img, FF_ImgChanType chan,
 *				Int32 x, Int32 y)
 *
 *	Description:	Get the address of the pixel (x, y) of channel "chan" of
 *			image "img". "Chan" must be stored as Float32s (i.e.
 *			img->pixelptr.format must be equal to
 *			"FF_ImgFmtTypeFloat"), and memory must have been
 *			allocated for scan line number "y" of the image.
 *
 *			Note: You can switch to a macro version of this
 *			function by defining the preprocessor symbol
 *			USE_IMG_MACROS.
 *
 *	Return value:	The address of the pixel (x, y) of channel "chan" of
 *			image "img".
 *
 *	See also:	FF_ImgUBytePixel
 *			FF_ImgFloatRow
 *
 ***************************************************************************/

Float32 *FF_ImgFloatPixel (FF_ImgDesc *img, FF_ImgChanType chan,
                           Int32 x, Int32 y)
{
    if (img->channel[(Int32) chan] != FF_ImgFmtTypeFloat ||
	y < img->firstscan || y > img->lastscan ||
	x < 0 || x > img->desc.width) {
	UT_ErrFatal ("FF_ImgFloatPixel", "Invalid parameters");
    }
    return img->pixelptr[(Int32) chan].floats[y] + x;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgOpenWrite
 *
 *	Usage:		Open an image file for writing.
 *
 *	Synopsis:	UT_Bool FF_ImgOpenWrite
 *				(const char *name, 
 *				 FF_ImgFileDesc *px_f)
 *
 *	Description:	Image file "name" is opened for writing; arrangements
 *			are made so that the image file will be written
 *			using the file format given be the values of
 *			"px_f->format" and "px_f->version".
 *			The "channel" and "compression" fields of "px_f"
 *			specify which channels have to be included in the
 *			file and which data compression methods have to
 *			be used.
 *			The "scanorder" and "pixprivate" fields of "px_f" are
 *			set by FF_ImgOpenWrite.
 *
 *			Note: Some file formats do not permit the creation
 *			of image files with arbitrary resolution. If the
 *			image resolution specified for the file is not
 *			one of the resolutions allowed for the current file
 *			format and version, the pixel file library selects
 *			a resolution that is as close as possible to the one
 *			desired, and changes the "desc.width", "desc.height"
 *			and "desc.aspect" fields of "px_f" accordingly.
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *	See also:	FF_ImgOpenRead
 *
 ***************************************************************************/

UT_Bool FF_ImgOpenWrite (const char *name, FF_ImgFileDesc *px_f)
{
    UT_MemState memstate;
    pixfile *pf;
    Int32 i;

    initialize ();
    memstate = UT_MemRemember ();

    /* Allocate and initialize the private part of the pixel file data. */

    if (!(pf = UT_MemTemp (pixfile))) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    px_f->pixprivate = pf;
    pf->fmtprivate = NULL;
    pf->readfile = UT_False;
    pf->readscan = NULL;
    pf->writescan = NULL;
    pf->close = NULL;
    pf->image = NULL;
    pf->stretch = UT_False;
    pf->gamma = UT_False;
    pf->color = UT_False;
    pf->xtable = NULL;
    pf->xtabsize = 0;
    pf->my = 0.0;
    pf->gammatable = NULL;
    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	pf->sbuf[i] = NULL;
	if (px_f->channel[i]) {
	    pf->rbuf[i].ubytes = NULL;
	    pf->rbuf[i].floats = NULL;
	}
    }

    /* Now try to open the file. */

    for (i = 0; i < numFormats; ++i) {
	if (!strcmp (imgFunctTable[i].format, px_f->format)) {
	    break;
	}
    }
    if (i >= numFormats) {
	UT_MemRestore (memstate);
	UT_ErrSetNum (FF_ImgErrFormat, str_open_write, name);
	return UT_False;
    }
    if (!(*imgFunctTable[i].open_write) (name, px_f)) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	if (!px_f->channel[i]) {
	    px_f->compression[i] = FF_ImgComprTypeNone;
	}
    }
    UT_MemSave (memstate);
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgOpenRead
 *
 *	Usage:		Open an image file for reading.
 *
 *	Synopsis:	UT_Bool FF_ImgOpenRead
 *				(const char *name,
 *				 FF_ImgFileDesc *px_f)
 *
 *	Description:	Image file "name" is opened for reading, and a
 *			description of the image stored in the file is
 *			copied to "px_f".
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *	See also:	FF_ImgOpenWrite
 *
 ***************************************************************************/

UT_Bool FF_ImgOpenRead (const char *name, FF_ImgFileDesc *px_f)
{
    UT_MemState memstate;
    pixfile *pf;
    Int32 i;

    initialize ();
    memstate = UT_MemRemember ();

    /* Allocate and initialize the private part of the pixel file data. */

    if (!(pf = UT_MemTemp (pixfile))) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    memset (pf, 0, sizeof (pixfile));
    px_f->pixprivate = pf;
    pf->fmtprivate = NULL;
    pf->readfile = UT_True;
    pf->readscan = NULL;
    pf->writescan = NULL;
    pf->close = NULL;
    pf->image = NULL;
    pf->stretch = UT_False;
    pf->gamma = UT_False;
    pf->color = UT_False;
    pf->xtable = NULL;
    pf->xtabsize = 0;
    pf->my = 0.0;
    pf->gammatable = NULL;
    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	pf->sbuf[i] = NULL;
	if (px_f->channel[i]) {
	    pf->rbuf[i].ubytes = NULL;
	    pf->rbuf[i].floats = NULL;
	} else {
	    px_f->compression[i] = FF_ImgComprTypeNone;
	}
    }
    pf->ybuf = -1;

    /* Now try to open the file. */

    i = 0;
    while (UT_True) {
	if ((*imgFunctTable[i].open_read) (name, px_f)) {
	    strcpy (px_f->format, imgFunctTable[i].format);
	    break;
	}
	if ((UT_ErrNum == UT_ErrPermission) || (++i == numFormats)) {
	    UT_MemRestore (memstate);
	    return UT_False;
	}
    }
    UT_MemSave (memstate);
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgClose
 *
 *	Usage:		Close an image file.
 *
 *	Synopsis:	void FF_ImgClose (FF_ImgFileDesc *px_f)
 *
 *	Description:	"Px_f" must be the descriptor of an image file
 *			opened with FF_ImgOpenWrite or FF_ImgOpenRead.
 *			Image files should be closed when they are no
 *			longer needed.
 *
 *	Return value:	None.
 *
 *	See also:	FF_ImgNew
 *			FF_ImgDelete
 *
 ***************************************************************************/

void FF_ImgClose (FF_ImgFileDesc *px_f)
{
    pixfile *pf;
    Int32 i;

    pf = px_f->pixprivate;
    (*pf->close) (px_f);
    if (pf->image) {
	if (pf->xtable) {
	    UT_MemFree (pf->xtable);
	}
	if (pf->gammatable) {
	    UT_MemFree (pf->gammatable);
	}
	for (i = 0; i < FF_NumImgChanTypes; ++i) {
	    switch (px_f->channel[i]) {
		case FF_ImgFmtTypeNone:
		    break;
		case FF_ImgFmtTypeUByte:
		    if (pf->rbuf[i].ubytes) {
			UT_MemFree (pf->rbuf[i].ubytes);
		    }
		    break;
		case FF_ImgFmtTypeFloat:
		    if (pf->rbuf[i].floats) {
			UT_MemFree (pf->rbuf[i].floats);
		    }
		    break;
		default:
		    UT_ErrFatal ("FF_ImgClose", "invalid pixel format");
	    }
	    if (pf->sbuf[i]) {
		UT_MemFree (pf->sbuf[i]);
	    }
	}
    }
    UT_MemFree (pf);
    return;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgSetSrcImg
 *
 *	Usage:		Define the image that has to be copied into
 *			an image file.
 *
 *	Synopsis:	UT_Bool FF_ImgSetSrcImg
 *				(FF_ImgFileDesc *px_f,
 *				 FF_ImgDesc *img,
 *				 UT_Bool gamma,
 *				 UT_Bool color)
 *
 *	Description:	The image file library is informed that image "img"
 *			will be written to file "px_f". Since the "desc"
 *			fields in "px_f" and "img" can be different, the
 *			image data may have to be transformed while it is
 *			written to the file. "Gamma" and "color" determine
 *			how these transformations will be done:
 *			
 *			If "gamma" is UT_True, gamma correction is performed
 *			on the FF_ImgChanTypeGray, FF_ImgChanTypeRed,
 *			FF_ImgChanTypeGreen and	FF_ImgChanTypeBlue channels
 *			of the image.
 *
 *			If "color" is UT_True, and the hues of the primary
 *			colors in "img" and "px_f" differ, color correction
 *			is performed on the FF_ImgChanTypeRed, 
 *			FF_ImgChanTypeGreen and	FF_ImgChanTypeBlue channels
 *			of the image.
 *
 *			If the resolution of the source image and the
 *			resolution of the pixel file are different, the image
 *			will be clipped or padded with zeros.
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *	See also:	FF_ImgSetDestImg
 *
 ***************************************************************************/

UT_Bool FF_ImgSetSrcImg (FF_ImgFileDesc *px_f, FF_ImgDesc *img, 
                         UT_Bool gamma, UT_Bool color)
{
    UT_MemState memstate;
    pixfile *pf;
    Int32 i;

    memstate = UT_MemRemember ();
    pf = px_f->pixprivate;

    /* First check the parameters for obvious errors. */

    if (pf->readfile || img->desc.width < 1 || img->desc.height < 1) {
	UT_ErrFatal ("FF_ImgSetSrcImg", "called with invalid parameters");
    }
    if (pf->image) {
	UT_ErrFatal ("FF_ImgSetSrcImg", "image has already been set up");
    }

    /* Allocate buffers to store one scan line of pixel data. */

    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	switch (px_f->channel[i]) {
	    case FF_ImgFmtTypeNone:
	        break;
	    case FF_ImgFmtTypeUByte:
		if (!(pf->rbuf[i].ubytes =
		      UT_MemTempArray (px_f->desc.width, UInt8))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		break;
	    case FF_ImgFmtTypeFloat:
		if (!(pf->rbuf[i].floats =
		      UT_MemTempArray (px_f->desc.width, Float32))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		break;
	    default:
		UT_ErrFatal ("FF_ImgSetSrcImg", "Invalid pixel format");
	}
    }
    if ((gamma && !FF_ImgGenGammaTable (px_f, img)) ||
	(color && !FF_ImgGenColorMatrix (px_f, img))) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    pf->image = img;
    pf->gamma = gamma;
    pf->color = color;

    /* We can eliminate copying of the image data into an intermediate
    buffer during FF_ImgWriteRow if no gamma or color correction is
    requested and if the width of the source image is not less than
    the width of the pixel file. */

    if (!gamma && !color && img->desc.width >= px_f->desc.width) {
	pf->nocopy = UT_True;
    } else {
	pf->nocopy = UT_False;
    }

    UT_MemSave (memstate);
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgSetDestImg
 *
 *	Usage:		Define the image that will receive the data from
 *			an image file.
 *
 *	Synopsis:	UT_Bool FF_ImgSetDestImg
 *				(FF_ImgFileDesc *px_f,
 *				 FF_ImgDesc *img,
 *				 UT_Bool stretch,
 *				 UT_Bool gamma,
 *				 UT_Bool color)
 *
 *	Description:	The image file library is informed that the data
 *			from pixel file "px_f" have to be copied into
 *			image "img".
 *
 *			If "stretch" is UT_True, the picture stored in "px_f"
 *			is scaled to the dimensions of image "img";
 *			if "stretch" is UT_False, the picture is clipped or
 *			padded with zeros if it does not precisely fit
 *			into "img".
 *			Note that the image scaling algorithm has been
 *			designed to transform the pixel data quickly on the
 *			fly, while the data are read from the pixel file.
 *			The quality of the scaled picture is not optimal; 
 *			aliasing may occur at high-contrast edges, especially
 *			when the picture is shrunk or blown up by some large
 *			amount.
 *
 *			The meaning of "gamma" and "color" is as in
 *			FF_ImgSetSrcImg.
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *	See also:	FF_ImgSetSrcImg
 *
 ***************************************************************************/

UT_Bool FF_ImgSetDestImg
	(FF_ImgFileDesc *px_f, FF_ImgDesc *img,
	 UT_Bool stretch, UT_Bool gamma, UT_Bool color)
{
    UT_MemState memstate;
    pixfile *pf;
    Int32 i;

    memstate = UT_MemRemember ();
    pf = px_f->pixprivate;

    /* First check the parameters for obvious errors. */

    if (!pf->readfile || img->desc.width < 1 || img->desc.height < 1) {
	UT_ErrFatal ("FF_ImgSetDestImg", "called with invalid parameters");
    }
    if (pf->image) {
	UT_ErrFatal ("FF_ImgSetDestImg", "image has already been set up");
    }

    /* Switch stretching off, if destination image and pixel file
       are equal in size. */
    
    if (stretch && 
	img->desc.width  == px_f->desc.width && 
	img->desc.height == px_f->desc.height) {
	stretch = UT_False;
    }

    /* Allocate buffers to store one scan line of pixel data. */

    for (i = 0; i < FF_NumImgChanTypes; ++i) {
	switch (px_f->channel[i]) {
	    case FF_ImgFmtTypeNone:
	        break;
	    case FF_ImgFmtTypeUByte:
		if (!(pf->rbuf[i].ubytes =
		      UT_MemTempArray (px_f->desc.width, UInt8))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		break;
	    case FF_ImgFmtTypeFloat:
		if (!(pf->rbuf[i].floats =
		      UT_MemTempArray (px_f->desc.width, Float32))) {
		    UT_MemRestore (memstate);
		    return UT_False;
		}
		break;
	    default:
		UT_ErrFatal ("FF_ImgSetDestImg", "Invalid pixel format");
	}
	if (stretch && (img->channel[i] == FF_ImgFmtTypeUByte)) {
	    if (!(pf->sbuf[i] = UT_MemTempArray (img->desc.width, Float32))) {
		UT_MemRestore (memstate);
		return UT_False;
	    }
	}
    }
    if ((stretch && !FF_ImgGenScaleTable (px_f, img)) ||
	(gamma && !FF_ImgGenGammaTable (px_f, img)) ||
	(color && !FF_ImgGenColorMatrix (px_f, img))) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    pf->image = img;
    pf->stretch = stretch;
    pf->gamma = gamma;
    pf->color = color;

    /* We can eliminate copying of the image data into an intermediate
    buffer during FF_ImgReadRow if the image resolution doesn't have
    to be changed and if the width of the destination image is not smaller
    than the width of the scan lines stored in the pixel file. */

    if (!stretch && img->desc.width >= px_f->desc.width) {
	pf->nocopy = UT_True;
    } else {
	pf->nocopy = UT_False;
    }
    UT_MemSave (memstate);
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgWriteRow
 *
 *	Usage:		Write one scan line of an image to an image file.
 *
 *	Synopsis:	UT_Bool FF_ImgWriteRow 
 *				(FF_ImgFileDesc *px_f, Int32 y, Int32 offset)
 *
 *	Description:	Scan line number (y + offset) of the image associated
 *			with image file "px_f" is written to "px_f" so that
 *			it becomes scan line number "y" in the file.
 *
 *			Note: Do not try to write data to a pixel file
 *			after an unsuccessful FF_ImgWriteRow!
 *
 *	Return value:	UT_True if successful, else UT_False.
 *
 *	See also: 	FF_ImgReadRow
 *
 ***************************************************************************/

UT_Bool FF_ImgWriteRow (FF_ImgFileDesc *px_f, Int32 y, Int32 offset)
{
    pixfile *pf;
    Int32 i, ncopy, nclear, yoff;
    Float32 *fsrc, *fdest, *fstop;
    UInt8 *ubsrc, *ubdest, *ubstop;
    UT_Bool scan_in_image;

    pf = px_f->pixprivate;
    yoff = y + offset;
    if (yoff >= pf->image->firstscan && yoff <= pf->image->lastscan) {
	scan_in_image = UT_True;
    } else {
	scan_in_image = UT_False;
    }

    /* Gather the data from the source scan line and copy them into
    the intermediate buffer, converting the pixel formats and clipping
    the scan line or padding it with zeros as necessary. */

    pf->ybuf = yoff;
    if (!scan_in_image) {
	nclear = px_f->desc.width;
	for (i = 0; i < FF_NumImgChanTypes; ++i) {
	    switch (px_f->channel[i]) {
		case FF_ImgFmtTypeNone:
		    break;
		case FF_ImgFmtTypeUByte:
		    ubdest = pf->rbuf[i].ubytes;
		    ubstop = ubdest + nclear;
		    pf->buf[i].ubytes = ubdest;
		    while (ubdest < ubstop) {
			*(ubdest++) = 0;
		    }
		    break;
		case FF_ImgFmtTypeFloat:
		    fdest = pf->rbuf[i].floats;
		    fstop = fdest + nclear;
		    pf->buf[i].floats = fdest;
		    while (fdest < fstop)
			*(fdest++) = 0.0;
		    break;
		default:
		    UT_ErrFatal ("FF_ImgWriteRow", "Invalid pixel format");
	    }
	}
    } else {
	ncopy = UT_MIN (pf->image->desc.width, px_f->desc.width);
	nclear = px_f->desc.width - ncopy;
	for (i = 0; i < FF_NumImgChanTypes; ++i) {
	    switch (px_f->channel[i]) {
		case FF_ImgFmtTypeNone:
		    break;
		case FF_ImgFmtTypeUByte: {
		    ubdest = pf->rbuf[i].ubytes;
		    ubstop = ubdest + ncopy;
		    switch (pf->image->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    pf->buf[i].ubytes = ubdest;
			    while (ubdest < ubstop)
				*(ubdest++) = 0;
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    ubsrc =
				pf->image->pixelptr[i].ubytes[yoff];
			    if (pf->nocopy) {
				pf->buf[i].ubytes = ubsrc;
				ubdest = ubstop;
			    } else {
				pf->buf[i].ubytes = ubdest;
				while (ubdest < ubstop)
				    *(ubdest++) = *(ubsrc++);
			    }
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    fsrc =
				pf->image->pixelptr[i].floats[yoff];
			    pf->buf[i].ubytes = ubdest;
			    while (ubdest < ubstop)
				*(ubdest++) = (Int32) (*(fsrc++) * 255.0);
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgWriteRow", "Invalid pixel format");
			}
		    }
		    ubstop += nclear;
		    while (ubdest < ubstop)
			*(ubdest++) = 0;
		    break;
		}
		case FF_ImgFmtTypeFloat: {
		    fdest = pf->rbuf[i].floats;
		    fstop = fdest + ncopy;
		    switch (pf->image->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    pf->buf[i].floats = fdest;
			    while (fdest < fstop)
				*(fdest++) = 0.0;
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    ubsrc =
				pf->image->pixelptr[i].ubytes[yoff];
			    pf->buf[i].floats = fdest;
			    while (fdest < fstop)
				*(fdest++) = *(ubsrc++) * (1.0 / 255.0);
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    fsrc =
				pf->image->pixelptr[i].floats[yoff];
			    if (pf->nocopy) {
				pf->buf[i].floats = fsrc;
				fdest = fstop;
			    } else {
				pf->buf[i].floats = fdest;
				while (fdest < fstop)
				    *(fdest++) = *(fsrc++);
			    }
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgWriteRow", "Invalid pixel format");
			}
		    }
		    fstop += nclear;
		    while (fdest < fstop)
			*(fdest++) = 0.0;
		    break;
		}
		default: {
		    UT_ErrFatal ("FF_ImgWriteRow", "Invalid pixel format");
		}
	    }
	} /* end for */
    } /* end else */

    /* Gamma-correct the scan line. */
    if (pf->gamma) {
	for (i = FF_ImgChanTypeGray; i <= FF_ImgChanTypeBlue; ++i) {
	    switch (px_f->channel[i]) {
		case FF_ImgFmtTypeNone:
		    break;
		case FF_ImgFmtTypeUByte:
		    FF_ImgApplyGammaUByte (px_f, pf->buf[i].ubytes);
		    break;
		case FF_ImgFmtTypeFloat:
		    FF_ImgApplyGammaFloat (px_f, pf->buf[i].floats);
		    break;
		default:
		    UT_ErrFatal ("FF_ImgWriteRow", "Invalid pixel format");
	    }
	}
    }

    /* Color-correct the scan line. */
    if (pf->color) {
	FF_ImgApplyColorCorrect (px_f);
    }

    /* Write the scan line to the pixel file. */
    return (*pf->writescan) (px_f, y);
}

/***************************************************************************
 *[@e
 *	Name:  		FF_ImgReadRow
 *
 *	Usage:		Read one scan line of an image from a pixel file.
 *
 *	Synopsis:	UT_Bool FF_ImgReadRow 
 *				(FF_ImgFileDesc *px_f, Int32 y, Int32 offset)
 *
 *	Description:	The data of scan line number "y" of the image stored in
 *			the with pixel file "px_f" are read, transformed
 *			as defined with FF_ImgSetDestImg, and then written to
 *			scan line number (y + offset) of the image.
 *
 *			Note: Except for pixel files with FF_ImgOrderTypeRandom scan
 *			line order, do not try to read data from a pixel file
 *			after an unsuccessful FF_ImgReadRow!
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *	See also:	FF_ImgWriteRow
 *
 ***************************************************************************/

UT_Bool FF_ImgReadRow (FF_ImgFileDesc *px_f, Int32 y, Int32 offset)
{
    pixfile *pf;
    Int32 v, vclip, vstart, vend, vinc, i, ncopy, nclear, yoff;
    Float32 vmin, vmax, scanweight, *fsrc, *fdest, *fstop;
    UInt8 *ubsrc, *ubdest, *ubstop;
    scaletab *st, *ststop;
    UT_Bool scan_in_image;

    pf = px_f->pixprivate;
    yoff = y + offset;
    if (yoff >= pf->image->firstscan && yoff <= pf->image->lastscan) {
	scan_in_image = UT_True;
    } else {
	scan_in_image = UT_False;
    }

    if (pf->stretch) {
	/* The image in "px_f" must be stretched to a different size
	while it is read from the file: First clear the destination
	scan line to all zeros; then read all scan lines of the source
	image contributing to the destination scan line and compute
	the values of the destination pixels. */

	if (scan_in_image) {
	    nclear = pf->image->desc.width;
	    for (i = 0; i < FF_NumImgChanTypes; ++i) {
		switch (pf->image->channel[i]) {
		    case FF_ImgFmtTypeNone: {
			break;
		    }
		    case FF_ImgFmtTypeUByte: {
			fdest = pf->sbuf[i];
			fstop = fdest + nclear;
			while (fdest < fstop)
			    *(fdest++) = 0.0;
			break;
		    }
		    case FF_ImgFmtTypeFloat: {
			fdest = pf->image->pixelptr[i].floats[yoff];
			fstop = fdest + nclear;
			while (fdest < fstop)
			    *(fdest++) = 0.0;
			break;
		    }
		    default: {
			UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
		    }
		}
	    }
	}
	vmin = y * pf->my;
	vmax = (y + 1) * pf->my;
	if ((px_f->scanorder == FF_ImgOrderTypeBottomUp) ||
	    (px_f->scanorder == FF_ImgOrderTypeRandom && pf->ybuf == (Int32) vmin)) {
	    vstart = (Int32) vmin;
	    vend = (Int32) vmax;
	    vinc = 1;
	} else {
	    vstart = (Int32) vmax;
	    vend = (Int32) vmin;
	    vinc = -1;
	}
	for (v = vstart; v != vend + vinc; v += vinc) {
	    /* If scan line number v is not already in the intermediate
	    buffer, read it from the file. */

	    if (v < 0)
		vclip = 0;
	    else if (v >= px_f->desc.height)
		vclip = px_f->desc.height - 1;
	    else
		vclip = v;
	    if (vclip != pf->ybuf) {
		for (i = 0; i < FF_NumImgChanTypes; ++i) {
		    switch (px_f->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    pf->buf[i].ubytes = pf->rbuf[i].ubytes;
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    pf->buf[i].floats = pf->rbuf[i].floats;
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    }
		}
		if (!(*pf->readscan) (px_f, vclip))
		    return UT_False;
		pf->ybuf = vclip;

		/* Gamma-correct the scan line. */
		if (pf->gamma) {
		    for (i = FF_ImgChanTypeGray; i <= FF_ImgChanTypeBlue; ++i) {
			switch (px_f->channel[i]) {
			    case FF_ImgFmtTypeNone: {
				break;
			    }
			    case FF_ImgFmtTypeUByte: {
				FF_ImgApplyGammaUByte (px_f, pf->buf[i].ubytes);
				break;
			    }
			    case FF_ImgFmtTypeFloat: {
				FF_ImgApplyGammaFloat (px_f, pf->buf[i].floats);
				break;
			    }
			    default: {
				UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			    }
			}
		    }
		}

		/* Color-correct the scan line. */
		if (pf->color) {
		    FF_ImgApplyColorCorrect (px_f);
		}
	    }

	    /* Add the pixel values in the intermediate buffer to
	    the values of the pixels in the destination scan line. */
	    if (scan_in_image) {
		scanweight =
		    (UT_MIN (vmax, (Float32) (v + 1)) - UT_MAX (vmin, (Float32) v));
		for (i = 0; i < FF_NumImgChanTypes; ++i) {
		    st = pf->xtable;
		    ststop = st + pf->xtabsize;
		    switch (pf->image->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    fdest = pf->sbuf[i];
			    switch (px_f->channel[i]) {
				case FF_ImgFmtTypeNone: {
				    break;
				}
				case FF_ImgFmtTypeUByte: {
				    ubsrc = pf->buf[i].ubytes;
				    while (st < ststop) {
					fdest[st->dest] += (UInt32) ubsrc[st->src] *
							   st->weight * scanweight;
					++st;
				    }
				    break;
				}
				case FF_ImgFmtTypeFloat: {
				    fsrc = pf->buf[i].floats;
				    while (st < ststop) {
					fdest[st->dest] += fsrc[st->src] * 255.0 *
							   st->weight * scanweight;
					++st;
				    }
				    break;
				}
				default: {
				    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
				}
			    }
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    fdest = pf->image->pixelptr[i].floats[yoff];
			    switch (px_f->channel[i]) {
				case FF_ImgFmtTypeNone: {
				    break;
				}
				case FF_ImgFmtTypeUByte: {
				    ubsrc = pf->buf[i].ubytes;
				    while (st < ststop) {
					fdest[st->dest] += (UInt32) ubsrc[st->src] *
							(1.0 / 255.0) * st->weight *
							scanweight;
					++st;
				    }
				    break;
				}
				case FF_ImgFmtTypeFloat: {
				    fsrc = pf->buf[i].floats;
				    while (st < ststop) {
					fdest[st->dest] += fsrc[st->src] * st->weight *
							scanweight;
					++st;
				    }
				    break;
				}
				default: {
				    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
				}
			    }
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    } /* switch (pf->image->channel[i]) */
		} /* for (i = ... */
	    } /* if (scan_in_image) */
	} /* for (v = ... */

	if (scan_in_image) {
	    ncopy = pf->image->desc.width;
	    for (i = 0; i < FF_NumImgChanTypes; ++i) {
		if (pf->image->channel[i] == FF_ImgFmtTypeUByte) {
		    fsrc = pf->sbuf[i];
		    ubdest = pf->image->pixelptr[i].ubytes[yoff];
		    ubstop = ubdest + ncopy;
		    while (ubdest < ubstop)
			*(ubdest++) = (UInt8) (Int32) (*(fsrc++) * pf->pxarea);
		}
	    }
	} 
    } else {
	/* Read the scan line from the file "as is"; do not stretch it. */
	for (i = 0; i < FF_NumImgChanTypes; ++i) {
	    switch (pf->image->channel[i]) {
		case FF_ImgFmtTypeNone: {
		    switch (px_f->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    pf->buf[i].ubytes = pf->rbuf[i].ubytes;
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    pf->buf[i].floats = pf->rbuf[i].floats;
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    }
		    break;
		}
		case FF_ImgFmtTypeUByte: {
		    switch (px_f->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    if (pf->nocopy && pf->image->channel[i] && scan_in_image) {
				pf->buf[i].ubytes =
				    pf->image->pixelptr[i].ubytes[yoff];
			    } else {
				pf->buf[i].ubytes = pf->rbuf[i].ubytes;
			    }
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    pf->buf[i].floats = pf->rbuf[i].floats;
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    }
		    break;
		}
		case FF_ImgFmtTypeFloat: {
		    switch (px_f->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    pf->buf[i].ubytes = pf->rbuf[i].ubytes;
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    if (pf->nocopy && pf->image->channel[i] && scan_in_image) {
				pf->buf[i].floats =
				    pf->image->pixelptr[i].floats[yoff];
			    } else {
				pf->buf[i].floats = pf->rbuf[i].floats;
			    }
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    }
		    break;
		}
		default: {
		    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
		}
	    }
	}
	if (y < 0 || y >= px_f->desc.height) {
	    if (scan_in_image) {
		nclear = px_f->desc.width;
		for (i = 0; i < FF_NumImgChanTypes; ++i) {
		    switch (px_f->channel[i]) {
			case FF_ImgFmtTypeNone: {
			    break;
			}
			case FF_ImgFmtTypeUByte: {
			    ubdest = pf->buf[i].ubytes;
			    ubstop = ubdest + nclear;
			    while (ubdest < ubstop)
				*(ubdest++) = 0;
			    break;
			}
			case FF_ImgFmtTypeFloat: {
			    fdest = pf->buf[i].floats;
			    fstop = fdest + nclear;
			    while (fdest < fstop)
				*(fdest++) = 0.0;
			    break;
			}
			default: {
			    UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			}
		    }
		}
	    }
	} else if (!(*pf->readscan) (px_f, y)) {
	    return UT_False;
	}
	pf->ybuf = y;

	/* Gamma-correct the scan line. */
	if (pf->gamma) {
	    for (i = FF_ImgChanTypeGray; i <= FF_ImgChanTypeBlue; ++i) {
		switch (px_f->channel[i]) {
		    case FF_ImgFmtTypeNone: {
			break;
		    }
		    case FF_ImgFmtTypeUByte: {
			FF_ImgApplyGammaUByte (px_f, pf->buf[i].ubytes);
			break;
		    }
		    case FF_ImgFmtTypeFloat: {
			FF_ImgApplyGammaFloat (px_f, pf->buf[i].floats);
			break;
		    }
		    default: {
			UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
		    }
		}
	    }
	}

	/* Color-correct the scan line. */
	if (pf->color) {
	    FF_ImgApplyColorCorrect (px_f);
	}

	/* Copy the data into the image structure, converting the pixel
	formats and clipping the scan line or padding it with zeros as
	necessary. */
	if (scan_in_image) {
	    ncopy = UT_MIN (pf->image->desc.width, px_f->desc.width);
	    nclear = pf->image->desc.width - ncopy;
	    for (i = 0; i < FF_NumImgChanTypes; ++i) {
		switch (pf->image->channel[i]) {
		    case FF_ImgFmtTypeNone: {
			break;
		    }
		    case FF_ImgFmtTypeUByte: {
			ubdest = pf->image->pixelptr[i].ubytes[yoff];
			ubstop = ubdest + ncopy;
			switch (px_f->channel[i]) {
			    case FF_ImgFmtTypeNone: {
				while (ubdest < ubstop)
				    *(ubdest++) = 0;
				break;
			    }
			    case FF_ImgFmtTypeUByte: {
				if (pf->nocopy) {
				    ubdest = ubstop;
				    break;
				}
				ubsrc = pf->buf[i].ubytes;
				while (ubdest < ubstop)
				    *(ubdest++) = *(ubsrc++);
				break;
			    }
			    case FF_ImgFmtTypeFloat: {
				fsrc = pf->buf[i].floats;
				while (ubdest < ubstop)
				    *(ubdest++) = (Int32) (*(fsrc++) * 255.0);
				break;
			    }
			    default: {
				UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			    }
			}
			ubstop += nclear;
			while (ubdest < ubstop)
			    *(ubdest++) = 0;
			break;
		    }
		    case FF_ImgFmtTypeFloat: {
			fdest = pf->image->pixelptr[i].floats[yoff];
			fstop = fdest + ncopy;
			switch (px_f->channel[i]) {
			    case FF_ImgFmtTypeNone: {
				while (fdest < fstop)
				    *(fdest++) = 0.0;
				break;
			    }
			    case FF_ImgFmtTypeUByte: {
				ubsrc = pf->buf[i].ubytes;
				while (fdest < fstop)
				    *(fdest++) = *(ubsrc++) * (1.0 / 255.0);
				break;
			    }
			    case FF_ImgFmtTypeFloat: {
				if (pf->nocopy) {
				    fdest = fstop;
				    break;
				}
				fsrc = pf->buf[i].floats;
				while (fdest < fstop)
				    *(fdest++) = *(fsrc++);
				break;
			    }
			    default: {
				UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
			    }
			}
			fstop += nclear;
			while (fdest < fstop)
			    *(fdest++) = 0.0;
			break;
		    }
		    default: {
			UT_ErrFatal ("FF_ImgReadRow", "Invalid pixel format");
		    }
		}
	    }
	}
    }
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetFmtType
 *
 *	Usage:		Get the index number of a channel format name.
 *
 *	Synopsis:	UT_Bool FF_ImgGetFmtType
 *                               (const char *fmtName, FF_ImgFmtType *fmtType)
 *
 *	Description:	Get the index number of a channel format name.
 *
 *		        Possible format names:
 *			    "NONE", "UBYTE", "FLOAT"
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	FF_ImgGetFmtTypeName 
 *			FF_ImgGetComprTypeName
 *                      FF_ImgGetChanTypeName
 *
 ***************************************************************************/

UT_Bool FF_ImgGetFmtType (const char *fmtName, FF_ImgFmtType *fmtType)
{
    UInt16 i;

    assert (FF_NumImgFmtTypes == sizeof(fmtTypeNames)/sizeof(fmtTypeNames[0]));

    for (i=0; i<sizeof(fmtTypeNames)/sizeof(fmtTypeNames[0]); i++) {
	if (strcmp (fmtName, fmtTypeNames[i]) == 0) {
            *fmtType = (FF_ImgFmtType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidFmtName, fmtName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetFmtTypeName
 *
 *	Usage:		Get the name of a channel format type.
 *
 *	Synopsis:	const char *FF_ImgGetFmtTypeName
 *                               (FF_ImgFmtType fmtType)
 *
 *	Description:	Possible format type names:
 *			    "NONE", "UBYTE", "FLOAT"
 *			    "UNKNOWN" for invalid format type.
 *
 *	Return value:	A pointer to the name of the format type.
 *
 *      See also:	FF_ImgGetChanTypeName 
 *			FF_ImgGetComprTypeName
 *
 ***************************************************************************/

const char *FF_ImgGetFmtTypeName (FF_ImgFmtType fmtType)
{
    if (fmtType < FF_ImgFmtTypeNone || fmtType >= FF_NumImgFmtTypes) {
	return "UNKNOWN";
    }
    return fmtTypeNames[(UInt16)fmtType];
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetChanType
 *
 *	Usage:		Get the index number of a channel type name.
 *
 *	Synopsis:	UT_Bool FF_ImgGetChanType
 *                              (const char *chanName, 
 *                              FF_ImgChanType *chanType)
 *
 *	Description:	Get the index number of a channel type name.
 *
 *	                Possible channel type names:
 *			    "NONE", "BW", "RED", "GREEN", "BLUE",
 *			    "MATTE", "REDMATTE", "GREENMATTE", "BLUEMATTE",
 *			    "HORINORMAL", "VERTNORMAL", "DEPTH",
 *			    "TEMPERATURE", "RADIANCE".
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	FF_ImgGetFmtTypeName
 *			FF_ImgGetComprTypeName
 *                      FF_ImgGetChanTypeName
 *
 ***************************************************************************/

UT_Bool FF_ImgGetChanType (const char *chanName, FF_ImgChanType *chanType)
{
    UInt16 i;

    assert (FF_NumImgChanTypes == sizeof(chanTypeNames)/sizeof(chanTypeNames[0]));

    for (i=0; i<sizeof(chanTypeNames)/sizeof(chanTypeNames[0]); i++) {
	if (strcmp (chanName, chanTypeNames[i]) == 0) {
            *chanType = (FF_ImgChanType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidTypeName, chanName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetChanTypeName
 *
 *	Usage:		Get the name of a channel type.
 *
 *	Synopsis:	const char *FF_ImgGetChanTypeName
 *                               (FF_ImgChanType chanType)
 *
 *	Description:	Possible return values for channel type names:
 *			    "NONE", "BW", "RED", "GREEN", "BLUE",
 *			    "MATTE", "REDMATTE", "GREENMATTE", "BLUEMATTE",
 *			    "HORINORMAL", "VERTNORMAL", "DEPTH",
 *			    "TEMPERATURE", "RADIANCE"
 *			    "UNKNOWN" for invalid channel type.
 *
 *	Return value:	A pointer to the name of the channel type.
 *
 *      See also:	FF_ImgGetFmtTypeName 
 *			FF_ImgGetComprTypeName
 *
 ***************************************************************************/

const char *FF_ImgGetChanTypeName (FF_ImgChanType chanType)
{
    if (chanType < FF_ImgChanTypeNone || chanType >= FF_NumImgChanTypes) {
	return "UNKNOWN";
    }
    /* We have to take care of FF_ImgChanTypeNone, as this is set to -1. */
    if (chanType == FF_ImgChanTypeNone) {
	return "NONE";
    }
    return chanTypeNames[(UInt16)chanType];
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetComprType
 *
 *	Usage:		Get the index number of a channel compression name.
 *
 *	Synopsis:	UT_Bool FF_ImgGetComprType
 *                              (const char *comprName, 
 *                              FF_ImgComprType *comprType)
 *
 *	Description:	Get the index number of a channel compression name.
 *
 *		        Possible compression names:
 *			    "NONE", "RLE"
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	FF_ImgGetFmtTypeName 
 *			FF_ImgGetComprTypeName
 *                      FF_ImgGetChanTypeName
 *
 ***************************************************************************/

UT_Bool FF_ImgGetComprType (const char *comprName, FF_ImgComprType *comprType)
{
    UInt16 i;

    assert (FF_NumImgComprTypes == sizeof(comprTypeNames)/sizeof(comprTypeNames[0]));

    for (i=0; i<sizeof(comprTypeNames)/sizeof(comprTypeNames[0]); i++) {
	if (strcmp (comprName, comprTypeNames[i]) == 0) {
            *comprType = (FF_ImgComprType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidComprName, comprName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgGetComprTypeName
 *
 *	Usage:		Get the name of a channel compression type.
 *
 *	Synopsis:	const char *FF_ImgGetComprTypeName
 *                               (FF_ImgComprType comprType)
 *
 *	Description:	Possible compression type names:
 *			    "NONE", "RLE"
 *			    "UNKNOWN" for invalid compression type.
 *
 *	Return value:	A pointer to the name of the compression type.
 *
 *      See also:	FF_ImgGetFmtTypeName 
 *			FF_ImgGetComprTypeName
 *
 ***************************************************************************/

const char *FF_ImgGetComprTypeName (FF_ImgComprType comprType)
{
    if (comprType < FF_ImgComprTypeNone || comprType >= FF_NumImgComprTypes) {
	return "UNKNOWN";
    }
    return comprTypeNames[(UInt16)comprType];
}
