/*************************************************************************
	File:	tga_in.c

	FormZ image bitmap translator sample.  Imports and exports Targa images.

	COPYRIGHT (C) 2001.  All Rights Reserved.
	auto*des*sys, Inc., Columbus, Ohio, USA.
*************************************************************************/

#include	"tga_prvt.h"

#if (__PLATFORM__ == MACINTOSH)	
#ifdef true
#undef true
#undef false
#endif
#if defined(__GNUC__)
#include <Carbon/Carbon.h>
#else
#include <Resources.h>
#endif
#endif

/************************************************************************
	Initialize the Targa import working data.
*************************************************************************/
static void	tga_prvt_init(tga_in_prvt_td *tga_in)
{
	tga_in->id_length		= 0;
	tga_in->col_map_type	= 0;
	tga_in->img_type		= 0;
	
	tga_in->col_map_eindex	= 0;
	tga_in->col_map_length	= 0;
	tga_in->col_map_esize	= 0;
	tga_in->col_map_size	= 0;

	tga_in->img_width		= 0;
	tga_in->img_height		= 0;
	tga_in->img_pixel_depth	= 0;
	tga_in->img_desc		= 0;
	
	tga_in->col_map			= NULL;
	
	tga_in->version			= 0;
	tga_in->ext_offset		= 0;
	tga_in->dev_offset		= 0;
	
	tga_in->buff_end_pos	= 0;

	tga_in->gamma			= 0.0;
}

/************************************************************************
	Uninitialize the Targa import working data.
*************************************************************************/
static void	tga_prvt_finit(tga_in_prvt_td *tga_in)
{
	if ( tga_in->col_map != NULL ) _fzrt_mem_funcs.fzrt_dispose_ptr((fzrt_ptr) tga_in->col_map);
	tga_prvt_init(tga_in);
}


/************************************************************************
	Read the Targa header and store it in the import working data.
*************************************************************************/
static fzrt_error_td	tga_read_header(FILE *fp, tga_in_prvt_td *tga_in, short seek)
{
	char			data[18],*ptr;
	fzrt_error_td	err = FZRT_NOERR;
	
	/* Seek to the beginning of the file. */
	if(seek && (fseek(fp,0,SEEK_SET) != 0 ))
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 2 );
	}
	else
	{
		/* Read the image header. */
		if(fread(data,18,1,fp) < 1)
		{
			err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 3 );
		}
		else
		{
			/* Extract the image header data. */
			tga_in->id_length		= data[0];
			tga_in->col_map_type	= data[1];
			tga_in->img_type		= data[2];
			
			ptr = &data[3];	tga_in->col_map_eindex = *((short*)ptr);
			TGA_IN_SWAP_SHORT(tga_in->col_map_eindex);
			ptr = &data[5];	tga_in->col_map_length = *((short*)ptr);
			TGA_IN_SWAP_SHORT(tga_in->col_map_length);
			tga_in->col_map_esize  = data[7];
			tga_in->map_bytes_per_entry = (char)ceil((double)tga_in->col_map_esize / 8.0);

			ptr = &data[12];	tga_in->img_width  = *((short*)ptr);
			TGA_IN_SWAP_SHORT(tga_in->img_width);
			ptr = &data[14];	tga_in->img_height = *((short*)ptr);
			TGA_IN_SWAP_SHORT(tga_in->img_height);
			tga_in->img_pixel_depth	 = data[16];
			tga_in->img_desc	 	 = data[17];
			tga_in->img_bytes_per_pixel	 = (char)ceil((double)tga_in->img_pixel_depth / 8.0);
			tga_in->attr_bits	 	 =  tga_in->img_desc & 0x0F;
			tga_in->top_2_bottom	 = (tga_in->img_desc & 0x20) ? 1 : 0;
			tga_in->left_2_right	 = (tga_in->img_desc & 0x10) ? 0 : 1;
			
			if(tga_in->col_map_type == TGA_HAS_COLOR_MAP)
			{
				 tga_in->col_map_size = (fzrt_int)ceil((double)tga_in->col_map_esize / 8.0) * tga_in->col_map_length;
			}
			else tga_in->col_map_size = 0;
			
		}
	}
	return err;
}

/************************************************************************
	Read the Targa 2.0 extension area for gamma correction values
*************************************************************************/
static fzrt_error_td	tga_read_ext_area(FILE *fp, tga_in_prvt_td *tga_in)
{
	short			gamma_num=0, gamma_denom=0;
	fzrt_error_td	err = FZRT_NOERR;

	/* seek to Gamma Numerator. */
	if(fseek(fp,tga_in->ext_offset + 478,SEEK_SET) != 0)
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 4 );
	}
	else
	{
		/* read to Gamma Numerator. */
		if(fread(&gamma_num,sizeof(short),1,fp) < 1)
		{
			err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 5 );
		}
		/* read to Gamma Denomerator. */
		else if ( fread(&gamma_denom,sizeof(short),1,fp) < 1 )
		{
			err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 6 );
		}

		TGA_IN_SWAP_SHORT(gamma_num);
		TGA_IN_SWAP_SHORT(gamma_denom);
		
		if (gamma_denom > 0)
		{	
			tga_in->gamma = (float)gamma_num/(float)gamma_denom;
		}
	}
	
	
	return err;
}

/************************************************************************
	Read the Targa footer and store it in the import working data.
	This determines also the targa file version. This is the first
	function that needs to be called when reading a Targa file.
*************************************************************************/
static fzrt_error_td	tga_read_footer(FILE *fp, tga_in_prvt_td *tga_in)
{
	char			data[26],*ptr;
	fzrt_error_td	err = FZRT_NOERR;
	
	/* Seek to the end of the file (minus the size of a targa footer). */
	if (fseek(fp,-26,SEEK_END) != 0 )
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 7 );
	}
	else
	{
		/* Read the footer data. */
		if ( fread(data,26,1,fp) < 1 )
		{
			err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 8 );
		}
		/* Make sure the whole footer was read. */
		else if ((tga_in->buff_end_pos = ftell(fp)) < 0 )
		{
			err =_fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 9 );
		}
		else
		{
			/* Extract the footer data. */
			if ( !strncmp(&data[8],"TRUEVISION-XFILE",16))
			{	
				tga_in->buff_end_pos -= 26;
				
				tga_in->version		= TGA_VERSION_20;
				ptr = &data[0];	tga_in->ext_offset = *((fzrt_int*)ptr);
				TGA_IN_SWAP_LONG(tga_in->ext_offset);
				ptr = &data[4];	tga_in->dev_offset = *((fzrt_int*)ptr);
				TGA_IN_SWAP_LONG(tga_in->dev_offset);
				
				if (tga_in->ext_offset != 0)
				{
					err = tga_read_ext_area(fp, tga_in);
				}
			}
			else
			{	tga_in->version 	= TGA_VERSION_OLD;
				tga_in->ext_offset	= 0;
				tga_in->dev_offset	= 0;
			}
		}
	}
	return err;
}


/************************************************************************
	Allocate and read the Targa color map.
*************************************************************************/
static fzrt_error_td	tga_read_color_map(FILE *fp, tga_in_prvt_td *tga_in, short seek)
{
	unsigned char			temp;
	register short			i,bbp,nentry;
	register unsigned char	*map_ptr;
	fzrt_error_td			err = FZRT_NOERR;
	
	/* Advance to color map data. */
	if ( seek && (fseek(fp,(fzrt_int)(18L + tga_in->id_length),SEEK_SET) != 0 ) )
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 10 );		
	}
	else
	{	
		if ( tga_in->col_map_type == TGA_HAS_COLOR_MAP &&		/* Has color map.								*/
		   ( tga_in->img_type == TGA_IMAGE_TYPE_COLMAP ||		/* Is color map image type.						*/
			 tga_in->img_type == TGA_IMAGE_TYPE_COLMAP_RLE))	/* Is compressed color map image type.			*/
		{
			/* Allocate color map. */
			if(_fzrt_mem_funcs.fzrt_new_ptr(tga_in->col_map_size, (fzrt_ptr*)&tga_in->col_map) == FZRT_NOERR )
			{
				/* Read color map. */
				if ( fread(tga_in->col_map,tga_in->col_map_size,1,fp) < 1 )
				{
					err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 11 );
				}
				else
				{
					/* Swap bytes */
					bbp     = tga_in->map_bytes_per_entry;
					map_ptr = tga_in->col_map;
					nentry  = tga_in->col_map_length;
					switch ( bbp )
					{
						case 2 :
							for(i = 0; i < nentry; i++, map_ptr += bbp)
							{
								temp = map_ptr[0];
								map_ptr[0] = map_ptr[1];
								map_ptr[1] = temp;
							}
						break;
						
						case 3 :
							for(i = 0; i < nentry; i++, map_ptr += bbp)
							{
								temp = map_ptr[0];
								map_ptr[0] = map_ptr[2];
								map_ptr[2] = temp;
							}
						break;
						
						case 4 :
							for(i = 0; i < nentry; i++, map_ptr += bbp)
							{
								temp = map_ptr[0];
								map_ptr[0] = map_ptr[3];
								map_ptr[3] = temp;
								temp = map_ptr[1];
								map_ptr[1] = map_ptr[2];
								map_ptr[2] = temp;
							}
						break;
					}
				}
			}
			else
			{
				err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_MEM_ERR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 12 );
			}
		}
		else
		{
			if ( fseek(fp,(fzrt_int)tga_in->col_map_size,SEEK_CUR) != 0 )
			{
				err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 13 );				
			}
		}
	}
	
	return err;
}

/************************************************************************
	Expand a single Targa scanline into an rgb character size scanline
	if run length encoded and swap bytes into RGB order.
*************************************************************************/
static void	tga_fill_scanline(tga_in_prvt_td *tga_in,unsigned char *buff_in)
{
	register	unsigned char 	*in_ptr,*in_ptr2,temp;
	register	unsigned short	i,j,npixels;
	short		bbp,dir,indx1[4],indx2[4];

	npixels	= tga_in->img_width;
	in_ptr  = tga_in->left_2_right ? buff_in : &buff_in[(tga_in->img_width - 1) * tga_in->img_bytes_per_pixel];
	dir     = tga_in->left_2_right ? tga_in->img_bytes_per_pixel : -tga_in->img_bytes_per_pixel;
	bbp 	= tga_in->img_bytes_per_pixel;
	
	if ( tga_in->left_2_right )
	{
		switch (bbp)
		{
			case 2 :
				for(i = 0; i < npixels; i++, in_ptr += dir)
				{
					TGA_IN_SWAP_SHORT(*in_ptr);
				}
			break;
			
			case 3 :
				for(i = 0; i < npixels; i++, in_ptr += dir)
				{
					temp = in_ptr[0];
					in_ptr[0] = in_ptr[2];
					in_ptr[2] = temp;
				}
			break;
			
			case 4 :
				for(i = 0; i < npixels; i++, in_ptr += dir)
				{
					temp = in_ptr[0];
					in_ptr[0] = in_ptr[3];
					in_ptr[3] = temp;
					temp = in_ptr[1];
					in_ptr[1] = in_ptr[2];
					in_ptr[2] = temp;
				}
			break;
		}
	}
	else
	{
		npixels = (unsigned short)ceil((double) npixels * 0.5);
		in_ptr2 = buff_in;

		for(i = 0; i < bbp; i++)
		{	
			indx1[i] = i; 
			/* Change pixel  format to RGBx from xBGR or swap 2-byte pixels. */
			/* Targa 2-byte pixels are in little endian byte order. */
			if (bbp != 2 || FZ_NATIVE_ENDIAN != FZ_ENDIAN_SMALL)
			{
				indx2[i] = bbp - i - 1;	
			}
			else
			{
				indx2[i] = indx1[i];
			}
		}

		for(i = 0; i < npixels; i++, in_ptr += dir, in_ptr2 -= dir)
		{
			for(j = 0; j < bbp; j++)
			{
				temp				= in_ptr [indx1[j]];	
				in_ptr [indx1[j]]	= in_ptr2[indx2[j]];	
				in_ptr2[indx2[j]]	= temp;
			}
		}

	}
}


/************************************************************************
	Unpack a compressed buffer.
*************************************************************************/
static short tga_decode_rle_scanline(	char	*src_ptr,
										char	*dst_ptr,
										fzrt_int	src_size,
										fzrt_int	blk_size,
										fzrt_int	length,
										fzrt_int	*nbytes,
										fzrt_int	*scan_left,
										short	*scan_type)
{
	register	char	*src,*dst;
	register 	fzrt_int	sbyte,dbyte,cnt,i,j;
	char				byte;
	fzrt_int				tot,temp;

	short				rv = 0;

	src = src_ptr;
	sbyte = 0;
	dst = dst_ptr;
	dbyte = 0;
	
	if ( *scan_left > 0 )
	{
		sbyte = 0;
		if (*scan_type == 0)
		{
			for(i = 0; i < *scan_left; i++)
			{
				for(j = 0; j < blk_size; j++)
				{
					dst[dbyte++] = src[sbyte + j];
				}
			}
			sbyte += blk_size;
		}
		else
		{
			for(i = 0; i < *scan_left; i++)
			{
				for(j = 0; j < blk_size; j++)
				{
					dst[dbyte++] = src[sbyte++];
				}
			}
		}
		temp = *scan_left;
		*scan_left = 0;		
	}
	else
	{
		temp = 0;
	}

	do
	{
		if ( sbyte >= src_size )
		{
			rv =  TGA_END_OF_BUFFER;
			*scan_left = temp;
			goto EXIT;
		}
		
		byte = src[sbyte++];
		if(byte & 0x80)
		{
			cnt = (byte & 0x7F) + 1;
			tot = blk_size * cnt;

			if ( sbyte +  blk_size > src_size )
			{
				rv =  TGA_END_OF_BUFFER;
				*scan_left = temp;
				goto EXIT;
			}
			
			if ( dbyte + tot > length )
			{
				*scan_type  = 0;
				*scan_left = (dbyte + tot - length) / blk_size;
				cnt -= *scan_left;
			}

			for(i = 0; i < cnt; i++)
			{
				for(j = 0; j < blk_size; j++)
				{
					dst[dbyte++] = src[sbyte + j];
				}
			}
			if (*scan_left == 0) sbyte += blk_size;
		}
		else
		{	
			cnt = byte + 1;
			tot = blk_size * cnt;
			
			if ( sbyte + tot > src_size )
			{
				rv =  TGA_END_OF_BUFFER;
				*scan_left = temp;
				goto EXIT;
			}
			
			if ( dbyte + tot > length   )
			{
				*scan_type  = 1;
				*scan_left = (dbyte + tot - length) / blk_size;
				cnt -= *scan_left;
			}

			for(i = 0; i < cnt; i++)
			{
				for(j = 0; j < blk_size; j++)
				{
					dst[dbyte++] = src[sbyte++];
				}
			}
		}
		
	} while ( dbyte < length );

EXIT :

	*nbytes = sbyte;
	return rv;
}

/************************************************************************
	Smart allocation for Targa read buffer
*************************************************************************/
static fzrt_error_td tga_buffer_alloc( FILE *fp, tga_in_prvt_td *tga_in, short compressed, unsigned char** buffer, fzrt_int *buff_size)
{
	fzrt_int				scan_size,min_size;
	fzrt_error_td		err = FZRT_NOERR;

	*buffer    = NULL;
	*buff_size = 0;

	if ( !compressed )
	{
		scan_size = (fzrt_int)tga_in->img_width * (fzrt_int)tga_in->img_bytes_per_pixel;
		
		/* Try to allocate memory for all scanlines first. */
		*buff_size = tga_in->img_height;
		
		/* Check for max buffer overflow. */
		if ((scan_size * (fzrt_int)(*buff_size)) > TGA_MAX_READ_BUFFER )
			*buff_size = (TGA_MAX_READ_BUFFER) / scan_size;
		
		while ( 1 )
		{
			err = _fzrt_mem_funcs.fzrt_new_ptr(scan_size * (*buff_size ) * sizeof(unsigned char), (fzrt_ptr*)buffer);
			if( *buffer != NULL ) break;
			
			/* Divide number of lines by two and try again. */
			min_size = (fzrt_int)ceil ( ((double) *buff_size) * 0.5);
			if ( *buff_size == 1 && min_size == 1) break;
			*buff_size = min_size;
		}
	
	}
	else
	{	
		/* Try to allocate a buffer which is the size of the rest of the file. */
		*buff_size = tga_in->buff_end_pos - ftell(fp);
		/* Minimum buffer is the size of one scanline. */
		min_size   = tga_in->img_width * (fzrt_int)tga_in->img_bytes_per_pixel;

		/* Check for max buffer overflow. */
		if ( *buff_size > TGA_MAX_READ_BUFFER ) *buff_size = TGA_MAX_READ_BUFFER;
	
		while ( 1 )
		{
			err = _fzrt_mem_funcs.fzrt_new_ptr((*buff_size) * sizeof(unsigned char), (fzrt_ptr*)buffer);
			if( *buffer != NULL ) break;
			
			/* Divide buffer by two and try again. */
			*buff_size = (fzrt_int)ceil ( ((double) *buff_size) * 0.5);
			if ( *buff_size < min_size ) break;
		}
	}

	if ( *buffer == NULL )
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_MEM_ERR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 14 );
	}

	return err;
}

/************************************************************************
	Read the Targa image pixels.
*************************************************************************/
static fzrt_error_td	tga_read_image2(fzrt_pbuf_ptr *pbuf, FILE *fp, tga_in_prvt_td *tga_in, short seek, fz_ffmt_image_channel_enum which_channel)
{
	unsigned char				*buffer,*pixels;
	unsigned short				i,j,img_width,img_height;
	unsigned short				n_lines,cnt,y,n_read;
	short						channels;
	fz_ffmt_image_type_enum		img_type;
	short						compressed,scan_type;
	fzrt_int						max_buffer,scan_size,out_size,bytes_per_pixel;
	fzrt_int						buff_size,buff_needed,bytes_done,bytes_left,bytes_processed,scan_left;
	short						rv;
	fzrt_error_td				err = FZRT_NOERR;
	fz_ffmt_image_import_ptr	image_import;
	short						cmap_size=0, map_bytes_per_entry=1, col_map_esize=0;
	fz_ffmt_image_map_type_enum	map_type=FZ_FFMT_IMAGE_MAP_TYPE_GRAY_WHITE_FIRST;
	
	if ( tga_in->img_type != TGA_IMAGE_TYPE_NONE )
	{
		/* Advance to the pixel data. */
		if ( seek && (fseek(fp,(fzrt_int)(18L + tga_in->id_length),SEEK_SET) != 0 ) )
		{
			err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 15 );
		}
		else
		{	
			/* Read or skip the color map. */
			if((err = tga_read_color_map(fp,tga_in,FALSE)) == FZRT_NOERR)
			{
				img_width		= tga_in->img_width;
				img_height		= tga_in->img_height;
				
				/* Get size for read buffers. */
				bytes_per_pixel = tga_in->img_bytes_per_pixel;
				out_size 	    = (fzrt_int)tga_in->img_width * bytes_per_pixel;
				scan_size		= (fzrt_int)tga_in->img_width * bytes_per_pixel;
				
				/* Is the image compressed? */
				switch (tga_in->img_type)
				{
					case TGA_IMAGE_TYPE_COLMAP_RLE 		:
					case TGA_IMAGE_TYPE_TRUECOL_RLE 	:
					case TGA_IMAGE_TYPE_BW_RLE 			:
						compressed  = TRUE;
					break;
					
					default :
						compressed  = FALSE;
					break;
				}
				
				/* Get the number of color channels. */
				switch (tga_in->img_type)
				{
					case TGA_IMAGE_TYPE_BW_RLE 			:
					case TGA_IMAGE_TYPE_BW	 			:
						channels = 3;
						img_type = FZ_FFMT_IMAGE_TYPE_MAPPED_COLOR;
						cmap_size = 256;
						map_bytes_per_entry = 3;
						col_map_esize = 24;
						map_type = FZ_FFMT_IMAGE_MAP_TYPE_GRAY_BLACK_FIRST;
					break;
					
					case TGA_IMAGE_TYPE_COLMAP_RLE 		:
					case TGA_IMAGE_TYPE_COLMAP	 		:
						channels = 3;
						img_type = FZ_FFMT_IMAGE_TYPE_MAPPED_COLOR;
						cmap_size = tga_in->col_map_length;
						map_bytes_per_entry = tga_in->map_bytes_per_entry;
						col_map_esize = tga_in->col_map_esize;
						map_type = FZ_FFMT_IMAGE_MAP_TYPE_COLOR;
					break;
					
					default 							:
						channels = ( tga_in->img_bytes_per_pixel == 4 ) ? 4 : 3;
						img_type = FZ_FFMT_IMAGE_TYPE_TRUE_COLOR;
					break;
				}
			
				/* Initialize the image bitmap import storage. */
				err = _ffmt_2d_funcs.fz_ffmt_image_bmap_scanline_init(pbuf,
										img_width,
										tga_in->img_pixel_depth,
										tga_in->img_bytes_per_pixel,
										channels,
										which_channel,
										FZ_FFMT_IMAGE_ORDER_RGB,
										TRUE,
										img_type,
										tga_in->col_map,
										col_map_esize,
										map_bytes_per_entry,
										cmap_size,
										1,
										map_type,
										tga_in->gamma,
										&image_import);
				pixels = NULL;
				buffer = NULL;
				if( err == FZRT_NOERR && 
				  ( err = tga_buffer_alloc(fp,tga_in,compressed,&buffer,&buff_size)) == FZRT_NOERR )
				{
					if( compressed )
					{
						if (_fzrt_mem_funcs.fzrt_new_ptr(out_size, (fzrt_ptr*)&pixels) != FZRT_NOERR)
						{
							err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_MEM_ERR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 16 );
						}
						else
						{
							max_buffer = tga_in->buff_end_pos - ftell(fp);
							if ( buff_size > max_buffer ) buff_size = max_buffer;
							buff_needed = buff_size;
							bytes_left	= 0;
							cnt 		= 0;
							scan_left	= 0;

							while ( 1 )
							{
								/* Read one buffer full of compressed pixel data. */
								if ( fread(&buffer[bytes_left],buff_needed,1,fp) < 1 )
								{
									err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 17 );
									break;
								}
								else
								{
									bytes_done  = 0;
									bytes_left	= buff_size;
						
									if ( err == FZRT_NOERR )
									{
										while(1)
										{
											/* Uncompress pixel data */
											if((rv = tga_decode_rle_scanline((char*)&buffer[bytes_done],(char*)pixels,bytes_left,
																		bytes_per_pixel,out_size,&bytes_processed,
																		&scan_left,&scan_type)) != 0 )
											{
												if ( rv == TGA_END_OF_BUFFER ) rv = 0;
												break;
											}
											
											bytes_left -= bytes_processed;
											bytes_done += bytes_processed;
											
											/* Expand one scanline in buffer. */
											tga_fill_scanline(tga_in,pixels);
											
											/* formZ stores bitmap images in a top down scanline order.  In other words,
												pixel 0, 0 is in the top-left corner of the bitmap.  If the Targa image
												is stored bottom up (pixel 0, 0 in bottom-left corner), the image needs
												to be flipped. */
											if (tga_in->top_2_bottom) y = cnt;
											else					  y = img_height - cnt - 1;
											
											/* Add one scanline to the pixel buffer */
											if((err = _ffmt_2d_funcs.fz_ffmt_image_bmap_scanline_import(pbuf, image_import,pixels,y,0,img_width)) != FZRT_NOERR ) 	break;		/*** DEK 10/08/01 ***/
											cnt++;
									
											if ( cnt == img_height ) break;							/* ALL LINES HAVE BEEN READ */
										}
									}
									
								}
								if ( err != FZRT_NOERR || cnt == img_height ) break;					/* ALL LINES HAVE BEEN READ */
								
								_fzrt_mem_funcs.fzrt_block_move(&buffer[bytes_done],buffer,bytes_left);
								
								max_buffer -= buff_needed;										/* DECREASE BUFFER COUNTER */
								buff_needed = buff_size - bytes_left;							/* SET NEW BUFFER SIZE TO BE READ	  */
								if ( max_buffer <= 0 ) break;									/* FILE HAS BEEN READ				  */
								if ( buff_needed > max_buffer ) buff_needed = max_buffer;		/* REMAINING FILE IS LESS THAN BUFFER */
								
							}
						}
					}
					else		/* Uncompressed */
					{
						n_read = (unsigned short)ceil((double)img_height / (double)buff_size);
	
						for(i = 0, cnt = 0; i < n_read; i++)
						{
							/* Get number of scanlines to be read into buffer. */
							if ( cnt + buff_size > img_height ) n_lines = (unsigned short)(img_height - cnt);
							else								n_lines = (unsigned short)buff_size;
							
							/* Read buffer */
							if ( fread(buffer,scan_size,n_lines,fp) < n_lines )
							{
								err = _fzrt_base_funcs.fzrt_error_set_with_detail(ferror(fp), FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 18 );
							}
							else
							{

								if ( err == FZRT_NOERR )
								{
									for(j = 0; j < n_lines; j++)
									{	
										/* Expand one scanline in buffer. */
										tga_fill_scanline(tga_in,&buffer[scan_size * j]);
		
										/* formZ stores bitmap images in a top down scanline order.  In other words,
											pixel 0, 0 is in the top-left corner of the bitmap.  If the Targa image
											is stored bottom up (pixel 0, 0 in bottom-left corner), the image needs
											to be flipped. */
										if (tga_in->top_2_bottom) y = cnt;
										else					  y = img_height - cnt - 1;
										
										/* Add one scanline to the pixel buffer */
										if((err = _ffmt_2d_funcs.fz_ffmt_image_bmap_scanline_import(pbuf, image_import,&buffer[scan_size * j],y,0,img_width)) != FZRT_NOERR ) 	break;		/*** DEK 10/08/01 ***/
										cnt++;
									}
								}
							}
							
							if ( err != FZRT_NOERR ) break;
						}
					}
				}
				if (pixels != NULL ) _fzrt_mem_funcs.fzrt_dispose_ptr((fzrt_ptr) pixels);
				if (buffer != NULL ) _fzrt_mem_funcs.fzrt_dispose_ptr((fzrt_ptr) buffer);
			
				/* Uninitialize the image bitmap import storage. */
				_ffmt_2d_funcs.fz_ffmt_image_bmap_scanline_finit(&image_import);
			}
		}
	}
	else
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_NO_IMAGE, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 19 );
	}

	return err;
}

/************************************************************************
	Converts a C string to a pascal string.
*************************************************************************/
static void   tga_c_to_p_str(char *str)
{
	short i, len;

	len = strlen(str);
	if (len > 255) len = 255;
	for(i=len;i;i--)  str[i] = str[i-1];
	str[0] = (unsigned char)len;
	return;
}

#if (__PLATFORM__ == MACINTOSH)	
/************************************************************************
	Converts a pascal string to a C string.
*************************************************************************/
static void   tga_p_to_c_str(unsigned char *str)
{
	short	i, len;

	len = str[0];
	for(i=0;i<len;i++)  str[i] = str[i+1];
	str[len] = 0x00;
	return;
}
/*************************************************************************
	Converts a fixed point number to a double.
 *************************************************************************/
static double tga_fixed_to_double(tga_fixed_td x)
{
register	fzrt_int	mask = 0x00008000;
register	double	d,val;
register	short	i;
	
	d = 1.0;	val = 0.0;
	for(i=0;i<16;i++)
	{
		d/=2.0;
		if(mask & x)val += d;
		mask = mask>>1;	
	}
	
	return((x>>16) + val);
}
#endif	/* (__PLATFORM__ == MACINTOSH) */


/************************************************************************
	Get information about a Targa file. 
*************************************************************************/
fzrt_error_td tga_read_info(fzrt_int			windex,
							fz_ffmt_ref_td	ffmt_id,
							fzrt_floc_ptr	floc,
							fzrt_int			*lwidth,
							fzrt_int			*lheight,
							short			*pixel_depth,
							fzrt_boolean	*has_alpha,
							double			*dpi)
{
	tga_in_prvt_td		tga_in;
	FILE				*file_ptr;
	fzrt_error_td		err = FZRT_NOERR;
	char				fmat[256], err_str[256];
	char				fname[256];
	char				tmp_name[256];
	
	/* Open the file. */
	_fzrt_file_funcs.fzrt_file_floc_get_name(floc, fname, 256);
	_fzrt_file_funcs.fzrt_file_cnvt_name(fname, tmp_name, 256);
	file_ptr = fopen(tmp_name,"rb");
	
	if (file_ptr != NULL )
	{
		/* Initialize working data. */
		tga_prvt_init(&tga_in);
		
		/* Read file header and footer and set image info. */
		if ((err = tga_read_footer(file_ptr,&tga_in)) == FZRT_NOERR &&
			(err = tga_read_header(file_ptr,&tga_in,TRUE)) == FZRT_NOERR )
		{
			
			if(lwidth != NULL)
			{
				*lwidth 		= tga_in.img_width;
			}
			if(lheight != NULL)
			{
				*lheight 		= tga_in.img_height;
			}
			if(pixel_depth != NULL)
			{
				*pixel_depth	= tga_in.img_pixel_depth;
			}
			if(has_alpha != NULL)
			{
				*has_alpha		= (tga_in.attr_bits & 0x0F) ? 1 : 0;
			}
			if(dpi != NULL)		/* default to 72. dots per inch */
			{
				*dpi			= 72.0;
			}
		}
		
		/* Uninitialize working data. */
		tga_prvt_finit(&tga_in);
		
		/* Close the file */
		fclose (file_ptr);
	
#if (__PLATFORM__ == MACINTOSH)
#if 0
		/* Try to read image resolution from Photoshop resource. */
		if ( err == FZRT_NOERR && dpi != NULL )
		{
		
			typedef struct PShop_ResolutionInfo_td
			{	tga_fixed_td	hRes;
				short		hResUnit;
				short		widthUnit;
				tga_fixed_td	vRes;
				short		vResUnit;
				short		heightUnit;
			}	PShop_ResolutionInfo_td;

			short					rfile,resfile;
			double					dval;
			PShop_ResolutionInfo_td	**ptr;
			fzrt_int	dir;
			short	vol;
					
			rfile = CurResFile();
			_fzrt_file_funcs.fzrt_file_floc_get_name(floc, fname, 256);
			tga_c_to_p_str((void*)fname);
			HGetVol(NULL, &vol, &dir);
			if ((resfile = HOpenResFile(vol, dir, (unsigned char *)fname, fsRdPerm) != -1))
			{	UseResFile(resfile);	
				if((ptr = (PShop_ResolutionInfo_td**)GetResource('8BIM',1005)) != NULL )
				{
					if ((dval = tga_fixed_to_double((*ptr)->hRes)) > 0.0) *dpi = dval;
					ReleaseResource((Handle)ptr);
				}
				CloseResFile(resfile);
			}
			UseResFile(rfile);
			tga_p_to_c_str((void*)fname);
		}
#endif	
#endif	/* (__PLATFORM__ == MACINTOSH) */
	}
	else
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(errno, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 20 );
	}
	
	/* If an error occurred, display it to the user */
	if(err != FZRT_NOERR)
	{
		_fzrt_base_funcs.fzrt_fzr_get_string(_fz_rsrc_ref, TGA_STRINGS, TGA_READ_ERR_STR, fmat);
		_fzrt_file_funcs.fzrt_file_floc_get_name(floc, fname, 256);
		sprintf(err_str, fmat, fname);
		_fuim_funcs.fz_fuim_alrt_std_error(err, TGA_READ_ERR_ID, err_str);
	}

	return(err);
}

/************************************************************************
	Get the bitmap image from a Targa file. 
*************************************************************************/
fzrt_error_td tga_read_image(fzrt_int windex, fz_ffmt_ref_td ffmt_id, fzrt_floc_ptr floc, fz_ffmt_image_channel_enum which_channel, fzrt_pbuf_ptr *pbuf)	/* VAS 02/12/98 */
{
	tga_in_prvt_td		tga_in;
	FILE				*file_ptr;
	fzrt_error_td		err = FZRT_NOERR;
	char				fmat[256], err_str[256];
	char				fname[256];
	char				tmp_name[256];

	/* Get the file name from the floc and open the file. */
	_fzrt_file_funcs.fzrt_file_floc_get_name(floc, fname, 256);
	_fzrt_file_funcs.fzrt_file_cnvt_name(fname, tmp_name, 256);
	file_ptr = fopen(tmp_name,"rb");
	
	if (file_ptr != NULL )
	{
		/* Initialize working data. */
		tga_prvt_init(&tga_in);
		
		/* Read file header and footer. */
		if ((err = tga_read_footer(file_ptr,&tga_in)) == FZRT_NOERR &&
			(err = tga_read_header(file_ptr,&tga_in,TRUE)) == FZRT_NOERR )
		{
			/* Check for valid pixel depth. */
			switch ( tga_in.img_pixel_depth )
			{	case  8 :
				case 16 :
				case 24 :
				case 32 :
				break;
				default :
					err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_UNSUPPORTED_MAP_DEPTH, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 21 );
				break;
			}
			
			/* Check for volid color map image. */
			if ( tga_in.img_type == TGA_IMAGE_TYPE_COLMAP ||
				 tga_in.img_type == TGA_IMAGE_TYPE_COLMAP_RLE )
			{
				switch ( tga_in.img_pixel_depth )
				{
					case  8 :
					break;
					default :
						err = _fzrt_base_funcs.fzrt_error_set_with_detail(TGA_UNSUPPORTED_MAP_DEPTH, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, _tga_plugin_runtime_id, 22 );
					break;
				}
			}
		
			if ( err == FZRT_NOERR )
			{
				/* Read the image pixels */
				err = tga_read_image2(pbuf, file_ptr,&tga_in,TRUE, which_channel);
			}
		}
		
		/* Uninitialize working data. */
		tga_prvt_finit(&tga_in);
		
		/* Close the file */
		fclose (file_ptr);
	}
	else
	{
		err = _fzrt_base_funcs.fzrt_error_set_with_detail(errno, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_ANSI, 0, 23 );
	}

	/* If an error occurred, display it to the user */
	if(err != FZRT_NOERR)
	{
		_fzrt_base_funcs.fzrt_fzr_get_string(_fz_rsrc_ref, TGA_STRINGS, TGA_READ_ERR_STR, fmat);
		_fzrt_file_funcs.fzrt_file_floc_get_name(floc, fname, 256);
		sprintf(err_str, fmat, fname);
		_fuim_funcs.fz_fuim_alrt_std_error(err, TGA_READ_ERR_ID, err_str);
	}

	return(err);
}

