/**************************************************************************
 *{@C
 *	Copyright:	1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *	Module:		FileFormats
 *	Filename:	FF_ImageTarga.c
 *
 *	Author:		Paul Obermeier
 *
 *	Description:	The file format dependent layer of the image file
 *			format library: Functions to read and write
 *			files in Truevision's TARGA pixel file format.
 *			The following TARGA image types are supported:
 *
 *			    Type  2: Uncompressed RGB image
 *			    Type 10: Run-length encoded RGB image
 *
 *			Both types may have a transparency channel.
 *
 *	Additional documentation:
 *			None.
 *
 *	Exported functions:
 *			FF_ImgOpenWriteTarga
 *			FF_ImgOpenReadTarga
 *
 **************************************************************************/

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

#include "UT_Compat.h"
#include "UT_Error.h"
#include "UT_Portable.h"
#include "UT_Memory.h"
#include "UT_FileIO.h"

#include "FF_Image.h"
#include "FF_ImagePrivate.h"
#include "FF_ImageTarga.h"

#define MinRun 3
#define MaxRun 127

/* Macros for reading from the pixbuf */

#define FROM_PIXBUF 			\
    {	*(blue++)  = pixbuf[0];		\
	*(green++) = pixbuf[1];		\
	*(red++)   = pixbuf[2];		\
	if (tf->nchan == 4)		\
	    *(matte++) = pixbuf[3];	\
    }

static UInt8
	pixbuf[4],	/* Buffer for one pixel */
	*rowbuf,	/* Scanline pointers for reading uncompresses pixel values */
	*red,		/* Scanline pointers for stepping through */
	*green,
	*blue,
	*matte;

static UT_Bool 		
	left_right = UT_True;	/* Are scanlines oriented from left to right 
				   or vice versa */

#if defined (DEBUG_LOCAL)
    static Int32 
	total;
#endif

/* Close a pixel file previously opened with
   FF_ImgOpenWriteTarga or FF_ImgOpenReadTarga. */

static void tga_close (FF_ImgFileDesc *px_f)
{
    tgafile *tf;
    tf = ((pixfile *)px_f->pixprivate)->fmtprivate;
    UT_FileClose (tf->file);
    UT_MemFree (tf->rowbuf);
    UT_MemFree (tf->red);
    UT_MemFree (tf->green);
    UT_MemFree (tf->blue);
    UT_MemFree (tf->matte);
    UT_MemFree (tf);
    return;
}

static UT_Bool read_error (FILE *file)
{
    if (feof (file))
	UT_ErrSetNum (UT_ErrUnexpEof, "fread");
    else
	UT_ErrSetNum (UT_ErrFromOs (), "fread");
    return UT_False;
}

static UT_Bool write_error (void)
{
    UT_ErrSetNum (UT_ErrFromOs (), "fwrite");
    return UT_False;
}

/* Read 1 byte, representing an unsigned integer number. */

static UT_Bool read_UInt8 (FILE *file, UInt8 *b)
{
    if (1 != fread (b, sizeof(*b), 1, file))
	return read_error (file);
    return UT_True;
}

/* Read 2 bytes, representing a short integer in the form <LowByte, HighByte>, 
   from a file and convert them into the current machine's format. */

static UT_Bool read_Int16 (FILE *file, Int16 *s)
{
    Int8 buf[2];
    if (1 != fread (buf, sizeof(buf), 1, file))
	return read_error (file);
    *s = (buf[0] & 0xFF) | (buf[1] << 8);
    return UT_True;
}

/* Read 4 bytes, representing a floating point number in a machine-independent
   format, from a file and convert them into the current machine's format. */

static UT_Bool read_Float32 (FILE *file, Float32 *f)
{
    Int8 buf[4];
    if (1 != fread (buf, sizeof(buf), 1, file))
	return read_error (file);
    *f = UT_PortByteToFloat32 (buf);
    return UT_True;
}

/* Write a byte, representing an unsigned integer to a file. */

static UT_Bool write_UInt8 (FILE *file, UInt8 b)
{
    UInt8 buf[1];
    buf[0] = b;
    if (1 != fwrite (buf, sizeof(buf), 1, file))
	return write_error ();
    return UT_True;
}

/* Write a byte, representing a signed integer to a file. */

static UT_Bool write_Int8 (FILE *file, Int8 b)
{
    Int8 buf[1];
    buf[0] = b;
    if (1 != fwrite (buf, sizeof(buf), 1, file))
	return write_error ();
    return UT_True;
}

/* Convert a short integer number into the format <LowByte, HighByte> (an array
   of 2 bytes) and write the array to a file. */

static UT_Bool write_Int16 (FILE *file, Int16 s)
{
    Int8 buf[2];
    buf[0] = s;
    buf[1] = s >> 8;
    if (1 != fwrite (buf, sizeof(buf), 1, file))
	return write_error ();
    return UT_True;
}

/* Convert a floating point number into a machine-independent format (an array
   of 4 bytes) and write the array to a file. */

static UT_Bool write_Float32 (FILE *file, Float32 f)
{
    Int8 buf[4];
    UT_PortFloat32ToByte (f, buf);
    if (1 != fwrite (buf, sizeof(buf), 1, file))
	return write_error ();
    return UT_True;
}

/* Read the id data, which is located between file header and pixel data.
   We use this - up to 256 byte long - space for internal storage of gamma
   factor, aspect ratio and white point.
   If the information read is not useful, we skip the id_data. */

static UT_Bool read_id_data (FILE *file, FF_ImgFileDesc *px_f)
{
    Int16 magic;

    if (!read_Int16 (file, &magic) ||
	magic != TGA_int_magic)
	return UT_False;
    if (!read_Float32 (file, &px_f->desc.gamma) ||
	!read_Float32 (file, &px_f->desc.aspect) ||
	!read_Float32 (file, &px_f->desc.red.x) ||
	!read_Float32 (file, &px_f->desc.red.y) ||
	!read_Float32 (file, &px_f->desc.green.x) ||
	!read_Float32 (file, &px_f->desc.green.y) ||
	!read_Float32 (file, &px_f->desc.blue.x) ||
	!read_Float32 (file, &px_f->desc.blue.y) ||
	!read_Float32 (file, &px_f->desc.white.x) ||
	!read_Float32 (file, &px_f->desc.white.y) ||
	px_f->desc.gamma <= 0.01 || px_f->desc.gamma > 100.0 ||
	px_f->desc.aspect <= 0.01 || px_f->desc.aspect > 100.0 ||
	px_f->desc.red.x < 0.0 || px_f->desc.red.x > 1.0 ||
	px_f->desc.red.y < 0.0 || px_f->desc.red.y > 1.0 ||
	px_f->desc.green.x < 0.0 || px_f->desc.green.x > 1.0 ||
	px_f->desc.green.y < 0.0 || px_f->desc.green.y > 1.0 ||
	px_f->desc.blue.x < 0.0 || px_f->desc.blue.x > 1.0 ||
	px_f->desc.blue.y < 0.0 || px_f->desc.blue.y > 1.0 ||
	px_f->desc.white.x < 0.0 || px_f->desc.white.x > 1.0 ||
	px_f->desc.white.y < 0.0 || px_f->desc.white.y > 1.0)
	return UT_False;
    return UT_True;
}

/* Write the id data */

static UT_Bool write_id_data (FILE *file, FF_ImgFileDesc *px_f)
{
    if (!write_Int16 (file, TGA_int_magic) ||
	!write_Float32 (file, px_f->desc.gamma) ||
	!write_Float32 (file, px_f->desc.aspect) ||
	!write_Float32 (file, px_f->desc.red.x) ||
	!write_Float32 (file, px_f->desc.red.y) ||
	!write_Float32 (file, px_f->desc.green.x) ||
	!write_Float32 (file, px_f->desc.green.y) ||
	!write_Float32 (file, px_f->desc.blue.x) ||
	!write_Float32 (file, px_f->desc.blue.y) ||
	!write_Float32 (file, px_f->desc.white.x) ||
	!write_Float32 (file, px_f->desc.white.y))
	return UT_False;
    return UT_True;
}

/* Write the TARGA pixel file header. 
   If the format version is "ext", we write additional infomation into 
   the id_data field. */

static UT_Bool write_header (FF_ImgFileDesc *px_f)
{
    tgafile *tf;
    tgaheader th;

    tf = ((pixfile *)px_f->pixprivate)->fmtprivate;
    if (!strcmp (px_f->version, str_int_version))
	th.numid = TGA_iddata_len; 
    else
	th.numid = 0;
    th.maptyp = 0;
    th.imgtyp = px_f->compression[FF_ImgChanTypeRed] == FF_ImgComprTypeNone? 
		TGA_RGB_uncomp: TGA_RGB_comp;
    th.maporig = 0;
    th.mapsize = 0;
    th.mapbits = 0;
    th.xorig = 0;
    th.yorig = 0;
    th.xsize = px_f->desc.width;
    th.ysize = px_f->desc.height;
    th.pixsize = px_f->channel[FF_ImgChanTypeMatte]? 32: 24;
    th.imgdes = 0;		/* Origin is lower left */

    if (!write_UInt8 (tf->file, th.numid) ||
	!write_UInt8 (tf->file, th.maptyp) ||
	!write_UInt8 (tf->file, th.imgtyp) ||
	!write_Int16 (tf->file, th.maporig) ||
	!write_Int16 (tf->file, th.mapsize) ||
	!write_UInt8 (tf->file, th.mapbits) ||
	!write_Int16 (tf->file, th.xorig) ||
	!write_Int16 (tf->file, th.yorig) ||
	!write_Int16 (tf->file, th.xsize) ||
	!write_Int16 (tf->file, th.ysize) ||
	!write_UInt8 (tf->file, th.pixsize) ||
	!write_UInt8 (tf->file, th.imgdes))
	return UT_False;
    if (th.numid > 0)
	if (!write_id_data (tf->file, px_f))
	    return UT_False;
    return UT_True;
}

static UT_Bool read_header (FILE *file, const char name[], FF_ImgFileDesc *px_f)
{
    Int32 chn;
    UT_Bool int_version = UT_False;
    tgaheader th;

    if (!read_UInt8 (file, &th.numid) ||
	!read_UInt8 (file, &th.maptyp) ||
	!read_UInt8 (file, &th.imgtyp) ||
	!read_Int16 (file, &th.maporig) ||
	!read_Int16 (file, &th.mapsize) ||
	!read_UInt8 (file, &th.mapbits) ||
	!read_Int16 (file, &th.xorig) ||
	!read_Int16 (file, &th.yorig) ||
	!read_Int16 (file, &th.xsize) ||
	!read_Int16 (file, &th.ysize) ||
	!read_UInt8 (file, &th.pixsize) ||
	!read_UInt8 (file, &th.imgdes))
	return UT_False;

    /* Try to find out if this file can possibly be a TARGA pixel file. */
    if (!((th.imgtyp == TGA_RGB_uncomp || th.imgtyp == TGA_RGB_comp) &&
	  (th.pixsize == 24 || th.pixsize == 32))) {
	UT_ErrSetNum (FF_ImgErrFormat, str_open_read, name);
	return UT_False;
    }

    /* If there is additional information in the id_data field, try to read it.
       If it could not be interpreted correctly, skip that data. */
    if (th.numid > 0) {
	if (th.numid != TGA_iddata_len || !read_id_data (file, px_f)) {  
	    rewind (file);
	    fseek (file, TGA_hdr_len + th.numid, 0);
	    int_version = UT_False;
	} else {
	    int_version = UT_True;
	    strcpy (px_f->version, str_int_version);
	}
    }

    /* If not the internal version, fill the image descriptor 
       with default values. */
    if (!int_version) {
	px_f->desc.red.x = 0.670f;
	px_f->desc.red.y = 0.330f;
	px_f->desc.green.x = 0.210f;
	px_f->desc.green.y = 0.710f;
	px_f->desc.blue.x = 0.140f;
	px_f->desc.blue.y = 0.080f;
	px_f->desc.white.x = 0.313f;
	px_f->desc.white.y = 0.329f;
	px_f->desc.aspect = (Float32)th.xsize / (Float32)th.ysize;	
	px_f->desc.gamma  = 2.0;
	strcpy (px_f->version, str_ext_version);
    }

    if (th.xsize < 2 || th.ysize < 2) {
	UT_ErrSetNum (FF_ImgErrSyntax, str_header);
	return UT_False;
    }

    px_f->scanorder = ((th.imgdes >> 5) & 0x1)? 
		      FF_ImgOrderTypeTopDown: 
		      FF_ImgOrderTypeBottomUp;
    left_right = ((th.imgdes >> 4) & 0x1)? UT_False: UT_True;
    px_f->desc.width = th.xsize;
    px_f->desc.height = th.ysize;
    for (chn=0; chn<FF_NumImgChanTypes; chn++) {
	switch (chn) {
	    case FF_ImgChanTypeRed:
	    case FF_ImgChanTypeGreen:
	    case FF_ImgChanTypeBlue:
		px_f->channel[chn] = FF_ImgFmtTypeUByte;
		px_f->compression[chn] = th.imgtyp == TGA_RGB_uncomp? 
					 FF_ImgComprTypeNone: FF_ImgComprTypeRle;
		break;
	    case FF_ImgChanTypeMatte:
		if (th.pixsize == 32) {
		    px_f->channel[chn] = FF_ImgFmtTypeUByte;
		    px_f->compression[chn] = th.imgtyp == TGA_RGB_uncomp? 
					     FF_ImgComprTypeNone: FF_ImgComprTypeRle;
		    break;
		}
	    default:
		px_f->channel[chn] = FF_ImgFmtTypeNone;
		px_f->compression[chn] = FF_ImgComprTypeNone;
		break;
	}
    }

    /* Skip colormap data, if present. */
    if (th.mapsize > 0) {
	Int32   mapbytes;
	UInt8 dummy;
	switch (th.mapbits) {
	    case 15:
	    case 16:
		mapbytes = 2 * th.mapsize;
		break;
	    case 24:
		mapbytes = 3 * th.mapsize;
		break;
	    case 32:
		mapbytes = 4 * th.mapsize;
		break;
	    default:
		UT_ErrSetNum (FF_ImgErrSyntax, str_colormap, th.mapbits);
		return UT_False;
	}
	while (mapbytes--) {
	    if (!read_UInt8 (file, &dummy)) {
		return UT_False;
	    }
	}
    }
    return UT_True;
}

static UT_Bool write_pixel (FILE *fp, UInt8 b, UInt8 g, 
			 UInt8 r, UInt8 m, Int32 nchan)
{
    UInt8 buf[4];
    buf[0] = b;
    buf[1] = g;
    buf[2] = r;
    buf[3] = m;
    if (nchan != fwrite (buf, sizeof (buf[0]), nchan, fp))
	return write_error ();
    return UT_True;
}

/* Write a scanline of channel "type". */

static void write_channel (const FF_ImgFileDesc *px_f, const pixfile *pf, 
			   FF_ImgChanType chantype, 
			   UInt8 *dest, UInt8 *stop)
{
    switch (px_f->channel[chantype])
    {
	case FF_ImgFmtTypeUByte:
	{
	    UInt8 *src = pf->buf[chantype].ubytes;
	    while (dest < stop)
		*(dest++) = *(src)++;
	    break;
	}
	case FF_ImgFmtTypeFloat:
	{
	    Float32 *src = pf->buf[chantype].floats;
	    while (dest < stop)
		*(dest++) = (UInt32)(*(src++) * 255.0);
	    break;
	}
	default:
	    while (dest < stop)
		*(dest++) = 0;
    }
    return;
}

/* All channels of scan line number y for pixel file "file" are stored
   in the file. Note that the TARGA file format doesn't support the
   FF_ImgOrderTypeRandom scanline order. 
   So we write TARGA files in FF_ImgOrderTypeBottomUp order. */

static UT_Bool tga_write_scan (FF_ImgFileDesc *px_f, Int32 y)
{
    pixfile *pf;
    tgafile *tf;
    UInt8 *dest, *stop, *red_end, *green_end, *blue_end, *matte_end;

    pf = px_f->pixprivate;
    tf = pf->fmtprivate;
    /* Check if the parameters supplied are reasonable. */
    if (pf->readfile)
	UT_ErrFatal ("tga_write_scan", "pixel file is read-only");
    if (tf->lastscan != y - 1)
	UT_ErrFatal ("tga_write_scan", "wrong scan line order");
    /* Collect the data for the red, green, blue and matte channels 
       and store them in the intermediate buffers. */
    dest = tf->red;
    stop = dest + px_f->desc.width;
    write_channel (px_f, pf, FF_ImgChanTypeRed, dest, stop);

    dest = tf->green;
    stop = dest + px_f->desc.width;
    write_channel (px_f, pf, FF_ImgChanTypeGreen, dest, stop);

    dest = tf->blue;
    stop = dest + px_f->desc.width;
    write_channel (px_f, pf, FF_ImgChanTypeBlue, dest, stop);

    if (tf->nchan == 4)
    {
	dest = tf->matte;
	stop = dest + px_f->desc.width;
	write_channel (px_f, pf, FF_ImgChanTypeMatte, dest, stop);
    }

    red = tf->red;
    green = tf->green;
    blue = tf->blue;
    matte = tf->matte;
    stop = red + px_f->desc.width;

    /* Write the scanline data to the file. */
    if (px_f->compression[FF_ImgChanTypeRed] == FF_ImgComprTypeNone)
    {
	while (red < stop)
	{
	    if (!write_pixel (tf->file, *blue, *green, *red, *matte, tf->nchan))
		return UT_False;
	    blue++;
	    green++;
	    red++;
	    matte++;
	}
    }
    else	/* Run-length Compression */
    {
	red_end = red + 1;
	green_end = green + 1;
	blue_end = blue + 1;
	matte_end = matte + 1;
	while (red < stop)
	{
	    while (red_end < stop &&
		   *red == *red_end &&
		   *green == *green_end &&
		   *blue == *blue_end &&
		   red_end - red - 1 < MaxRun)
	    {
		if (tf->nchan == 4)
		{
		    if (*matte != *matte_end)
			break;
		}
		red_end++;
		green_end++;
		blue_end++;
		matte_end++;
	    }
	    if (red_end - red >= MinRun)
	    {	/* Found a run of compressable data */
		if (!write_Int8 (tf->file, (Int8)(((red_end - red)-1)|0x80)) ||
		    !write_pixel (tf->file, *blue, *green, 
				  *red, *matte, tf->nchan))
		    return UT_False;
		red = red_end;
		green = green_end;
		blue = blue_end;
		matte = matte_end;
	    }
	    else
	    {	/* Found a run of uncompressable data */
		while (red_end < stop &&
		       ((red_end + 1 >= stop ||
			*red_end != *(red_end + 1)) ||
			(red_end + 2 >= stop ||
			*(red_end + 1) != *(red_end + 2))) &&
		       ((green_end + 1 >= stop ||
			*green_end != *(green_end + 1)) ||
			(green_end + 2 >= stop ||
			*(green_end + 1) != *(green_end + 2))) &&
		       ((blue_end + 1 >= stop ||
			*blue_end != *(blue_end + 1)) ||
			(blue_end + 2 >= stop ||
			*(blue_end + 1) != *(blue_end + 2))) &&
			red_end - red < MaxRun)
		{
		    if (tf->nchan == 4)
		    {
		        if (! ((matte_end + 1 >= stop ||
			       *matte_end != *(matte_end + 1)) ||
			       (matte_end + 2 >= stop ||
			       *(matte_end + 1) != *(matte_end + 2))))
			    break;
		    }
		    red_end++;
		    green_end++;
		    blue_end++;
		    matte_end++;
		}
		if (!write_Int8 (tf->file, (Int8)((red_end - red) - 1)))
		    return UT_False;
		while (red < red_end)
		{
		    if (!write_pixel (tf->file, *blue, *green, 
				      *red, *matte, tf->nchan))
			return UT_False;
		    red++;
		    green++;
		    blue++;
		    matte++;
		}
	    }
	    red_end++;
	    green_end++;
	    blue_end++;
	    matte_end++;
	}
    }
    tf->lastscan = y;
    return UT_True;
}

static void read_channel (const FF_ImgFileDesc *px_f, const pixfile *pf,
			  FF_ImgChanType chantype, 
			  const UInt8 *src, Int32 n)
{
    const UInt8 *stop = src + n;
    if (left_right)
    {
	switch (px_f->channel[chantype])
	{
	    case FF_ImgFmtTypeNone: {
		break;
	    }
	    case FF_ImgFmtTypeUByte:
	    {
		UInt8 *dest = pf->buf[chantype].ubytes;
		while (src < stop)
		    *(dest++) = *(src++);
		break;
	    }
	    case FF_ImgFmtTypeFloat:
	    {
		Float32 *dest = pf->buf[chantype].floats;
		while (src < stop)
		    *(dest++) = (Float32)(UInt32)*(src++) * (1.0 / 255.0);
		break;
	    }
	}
    }
    else
    {
	switch (px_f->channel[chantype])
	{
	    case FF_ImgFmtTypeNone: {
		break;
	    }
	    case FF_ImgFmtTypeUByte:
	    {
		UInt8 *dest = pf->buf[chantype].ubytes + n - 1;
		while (src < stop)
		    *(dest--) = *(src++);
		break;
	    }
	    case FF_ImgFmtTypeFloat:
	    {
		Float32 *dest = pf->buf[chantype].floats + n - 1;
		while (src < stop)
		    *(dest--) = (Float32)(UInt32)*(src++) * (1.0 / 255.0);
		break;
	    }
	}
    }
    return;
}


/* Read one pixel value from file "fp". 
   A pixel is represented by 3 or 4 bytes in the order Blue/Green/Red/Matte. 
   The pixel value should be repeated in the scanline "n" times.
   Note that TARGA allows pixel values to be compressed across scanline
   boundaries. */

static UT_Bool read_pixels (UInt8 *stop, Int32 n, tgafile *tf)
{
    Int32 i;

    if (tf->nchan != fread (pixbuf, sizeof (pixbuf[0]), 
			    tf->nchan, tf->file))
	return read_error (tf->file);
    for (i=0; i<n; i++)
    {
	#if defined (DEBUG_LOCAL)
	    total++;
	#endif
	FROM_PIXBUF;

	if (red == stop)
	{   /* Scanline is filled with pixel values.
	       Determine the number of pixels to keep for next scanline. */
	    tf->scanrest = n - i - 1;
	    return UT_True;
	}
    }
    return UT_True;
}

/* The channels of scan line number "y" of the pixel file "px_f" are read.
   Note that the TARGA file format only supports FF_ImgOrderTypeTopDown or
   FF_ImgOrderTypeBottomUp scanline order! */

static UT_Bool tga_read_scan (FF_ImgFileDesc *px_f, Int32 y)
{
    pixfile *pf;
    tgafile *tf;
    UInt8   *stop;

    pf = px_f->pixprivate;
    tf = pf->fmtprivate;

    /* Check if the parameters supplied are reasonable. */
    if (!pf->readfile)
	UT_ErrFatal ("tga_read_scan", "pixel file is write-only");

    /* Read and decode the scan line data and store them in an
    intermediate buffer. */
    rowbuf = tf->rowbuf;
    red    = tf->red;
    green  = tf->green;
    blue   = tf->blue;
    matte  = tf->matte;
    stop = red + px_f->desc.width;
    #if defined (DEBUG_LOCAL)
	total = 0;
    #endif

    if (px_f->compression[FF_ImgChanTypeRed] == FF_ImgComprTypeRle)
    {	
	Int8 cbuf[1];
	Int32  pix, numpix;
	/* While there are pixels left from the previous scanline,
	   either fill the current scanline with the pixel value
	   still stored in "pixbuf" (TGA_mode_same) or read in the
	   appropriate number of pixel values (TGA_mode_diff). */
	while (tf->scanrest)
	{
	    if (tf->scanmode == TGA_mode_diff)
	    {   
		if (tf->nchan != fread (pixbuf, sizeof (pixbuf[0]), 
					tf->nchan, tf->file))
		    return read_error (tf->file);
	    }
	    #if defined (DEBUG_LOCAL)
		total++;
	    #endif
	    FROM_PIXBUF;
	    tf->scanrest--;
	    /* If the image is small, the compression might go over several
	       scanlines. */
	    if (red == stop)
		goto write_scanline;
	}

	/* Read the byte telling us the compression mode and the compression
	   count. Then read the pixel values till a scanline is filled. */
	do
	{
	    if (1 != fread (cbuf, sizeof (cbuf), 1, tf->file))
		return read_error (tf->file);
	    numpix = (cbuf[0] & 0x7F) + 1;

	    if ((cbuf[0] & 0x80) != 0x80)
	    {
		tf->scanmode = TGA_mode_diff;
		for (pix=0; pix<numpix; pix++)
		{
		    if (!read_pixels (stop, 1, tf))
			return UT_False;
		    if (red == stop)
		    {
			tf->scanrest = numpix - pix - 1;
			break;
		    }
		}
	    }
	    else
	    {
		tf->scanmode = TGA_mode_same;
		if (!read_pixels (stop, numpix, tf))
		    return UT_False;
	    }
	} 
	while (red < stop);
	#if defined (DEBUG_LOCAL)
	    printf ("\tScanline %d: Pixels: %d Rest: %d\n", 
		    y, total, tf->scanrest);
	#endif
    }
    else
    {	/* Read uncompressed pixel data. */
        Int32 i, bytesPerLine;

        bytesPerLine = 	tf->nchan * px_f->desc.width;
	if (bytesPerLine != fread (rowbuf, 1, bytesPerLine, tf->file)) {
            return read_error (tf->file);
        }
        for (i=0; i<px_f->desc.width; i++) {
            *(blue++)  = *(rowbuf++);
            *(green++) = *(rowbuf++);
            *(red++)   = *(rowbuf++);
            if (tf->nchan == 4) {
                *(matte++) = *(rowbuf++);
            }
        }
    }

write_scanline:
    /* Copy the data to their final destination. */
    read_channel (px_f, pf, FF_ImgChanTypeRed,   tf->red,   px_f->desc.width);
    read_channel (px_f, pf, FF_ImgChanTypeGreen, tf->green, px_f->desc.width);
    read_channel (px_f, pf, FF_ImgChanTypeBlue,  tf->blue,  px_f->desc.width);

    if (tf->nchan == 4)
	read_channel (px_f, pf, FF_ImgChanTypeMatte, tf->matte, px_f->desc.width);

    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgOpenWriteTarga
 *
 *	Usage:		Open a TARGA pixel file for writing.
 *
 *	Synopsis:	UT_Bool FF_ImgOpenWriteTarga
 *				(const char *name, FF_ImgFileDesc *px_f)
 *
 *	Description:	TARGA pixel file "name" is opened for writing.
 *
 *			Only FF_ImgChanTypeRed, FF_ImgChanTypeGreen,
 *			FF_ImgChanTypeBlue and FF_ImgChanTypeMatte channel
 *			types are supported.
 *			Since the TARGA file format requires, that all 
 *			three/four supported channels are stored in the file,
 *			unused channels in the image are filled with zeros.
 *			The only data compression method supported is
 *			run-length encoding.
 *
 *	Return value:	UT_True if successful, else UT_False.
 *
 *	See also:	FF_ImgOpenReadTarga
 *
 ***************************************************************************/

UT_Bool FF_ImgOpenWriteTarga (const char *name, FF_ImgFileDesc *px_f)
{
    pixfile *pf;
    tgafile *tf;
    UT_MemState memstate;

    memstate = UT_MemRemember ();
    /* Allocate memory for a "tgafile". */
    if (!(tf =         UT_MemTemp (tgafile)) ||
	!(tf->rowbuf = UT_MemTempArray (px_f->desc.width*4, UInt8)) ||
	!(tf->red =    UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->green =  UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->blue =   UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->matte =  UT_MemTempArray (px_f->desc.width, UInt8)))
    {
	UT_MemRestore (memstate);
	return UT_False;
    }
    pf = px_f->pixprivate;
    pf->fmtprivate = tf;
    /* Open the file for writing. */
    if (!(tf->file = UT_FileOpen (name, "wb"))) {
	UT_MemRestore (memstate);
	return UT_False;
    }
    tf->lastscan = -1;
    tf->nchan = px_f->channel[FF_ImgChanTypeMatte]? 4: 3;

    /* Generate the file header and write it to the file. */
    if (!write_header (px_f))
	return UT_False;

    /* Initialize the FF_ImgFileDesc. */
    pf->writescan = tga_write_scan;
    pf->close = tga_close;
    px_f->scanorder = FF_ImgOrderTypeBottomUp;
    UT_MemSave (memstate);
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		FF_ImgOpenReadTarga
 *
 *	Usage:		Open a TARGA pixel file for reading.
 *
 *	Synopsis:	UT_Bool FF_ImgOpenReadTarga
 *				(const char *name,
 *				FF_ImgFileDesc *px_f)
 *
 *	Description:	TARGA pixel file "name" is opened for reading.
 *
 *			Only FF_ImgChanTypeRed, FF_ImgChanTypeGreen,
 *			FF_ImgChanTypeBlue and FF_ImgChanTypeMatte channel
 *			types are supported.
 *
 *	Return value:	UT_True if successful, else UT_False.
 *
 *	See also:	FF_ImgOpenWriteTarga
 *
 ***************************************************************************/

UT_Bool FF_ImgOpenReadTarga (const char *name, FF_ImgFileDesc *px_f)
{
    pixfile *pf;
    tgafile *tf;
    FILE *file;
    UT_MemState memstate;

    memstate = UT_MemRemember ();
    pf = px_f->pixprivate;
    /* Open the file for reading. */
    if (!(file = UT_FileOpen (name, "rb"))) {
	return UT_False;
    }

    if (!read_header (file, name, px_f))
    {
	UT_FileClose (file);
	return UT_False;
    }

    /* Generate the "tgafile". */
    if (!(tf =         UT_MemTemp (tgafile)) ||
	!(tf->rowbuf = UT_MemTempArray (px_f->desc.width*4, UInt8)) ||
	!(tf->red =    UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->green =  UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->blue =   UT_MemTempArray (px_f->desc.width, UInt8)) ||
	!(tf->matte =  UT_MemTempArray (px_f->desc.width, UInt8)))
    {
	UT_MemRestore (memstate);
	UT_FileClose (file);
	return UT_False;
    }

    /* Fill the "pixfile". */
    pf->fmtprivate = tf;
    pf->readscan = tga_read_scan;
    pf->close = tga_close;

    /* Fill the "tgafile". */
    tf->lastscan = -1;
    tf->nchan = px_f->channel[FF_ImgChanTypeMatte]? 4: 3;
    tf->scanrest = 0;
    tf->file = file;
    UT_MemSave (memstate);
    return UT_True;
}
