/*	PCXFILE.c -  Routines to generate PCX files
*/


#include <stdio.h>
#include "lib.h"
#include "pcx.h"


/*	PCX_GETC

	Read the next "token" in the picture file. If the high order
	two bits are not 11, then it's a single byte. If they are,
	it's a repeat count, and the next byte is the value to repeat.
*/

int pcx_getc( c, n, fp, maxn )
FILE *fp;
uint *c, *n, maxn;
{
	static int csave=-1, nsave=-1;
	unsigned int i;

	if ( !fp )
	   return nsave = csave = -1;

	if ( nsave == -1 )
	{  *n = 1;

	   if ( (i=getc(fp)) == EOF )
	      return ERROR;

	   if ( (i & 0xc0) == 0xc0 )
	   {  *n = i & 0x3f;
	      if ( (i=getc(fp)) == EOF )
	         return ERROR;
	   };

	   *c = i;
	} else
	{  *n = nsave;
	   *c = csave;
	   nsave = csave = -1;
	};

	if ( *n > maxn )
	{  nsave = *n - maxn;
	   csave = *c;
	   *n = maxn;
	};
	return OK;
}


/*	PCX_PUTC

	Write a "token" to the picture file. If the repeat count is
	not 1, then write the repeat count | 0xc0 as the first byte,
	then the character as the next. If the character has its two
	high order bits set, then first write out a repeat count of
	1, then the character.

	pcx_xputc() counts the number of times that it receives a
	character, then passes that information on to pcx_putc().
*/

int pcx_xputc( c, fp )
FILE *fp;
int c;
{
	int i;
	static int csave = -1, n = -1;

	if ( c == -1 )
	{  if ( csave != -1 )
	      if ( pcx_putc( csave, n, fp ) )
	         return ERROR;      
	   csave = n = -1;
	   return OK;
	};

	if ( c == csave )
	{  n++;
	   return OK;
	};

	if ( csave != -1 )
	{  if ( i = pcx_putc( csave, n, fp ) )
	      return i;
	   csave = n = -1;
	};

	csave = c;
	n = 1;
	return OK;
}


int pcx_putc( c, n, fp )
FILE *fp;
unsigned int c, n;
{
	if ( (n > 1) || ( (c & 0xc0) == 0xc0 ) )
	{  while ( n > 0x3f )
	      if ( pcx_putc( c, 0x3f, fp ) )
	         return ERROR;
	      else n -= 0x3f;

	   if ( !n )
	      return OK;

	   if ( fputc( 0xc0 | n, fp ) == EOF )
	      return ERROR;
	};

	if ( fputc( c, fp ) == EOF )
	   return ERROR;

	return OK;
}


int pcx_read_pic( pic, fp )
FILE *fp;
PCXPIC *pic;
{
	/* A picture consists of a number of lines, each line having
	   a number of planes. Each plane is a bit map.
	*/
	uchar *calloc(), *allocz(), *p;
	int i, j, c, n, row, plane, bytes, nrows, nplan;
	PCXHDR *ph;
	
	ph = & pic->hdr;

	fseek( fp, 0l, 0 );
	if ( fread( (char *)ph, sizeof(*ph), 1, fp ) != sizeof(*ph) )
	   return ERROR;

	pcx_getc( (uint *)0, (uint *)0, (FILE *)0, 0 );

	bytes = ph->bpl;
	nrows = ph->y2 - ph->y1 +1;
	nplan = ph->nplanes;

	for ( plane = 0; plane < nplan; plane++ )
	   if ( ! pic->rows[plane] )
	      if ( ! (pic->rows[plane] = 
				(uchar **)calloc(1,sizeof(char *) * nrows)) )
	         return 1;

	for ( row = 0; row < nrows; row++ )
	   for ( plane = 0; plane < nplan; plane++ )
	   {  if ( !(p = pic->rows[plane][row]) )
	         if ( !(p = pic->rows[plane][row] = calloc(1,bytes +1)) )
	            return 1;

	      for ( n=i=0; i<bytes; i += n )
	      {  if ( pcx_getc( (uint *)&c, (uint *)&n, (FILE *)fp, bytes - i ) )
	            return ERROR;
	         for ( j=0; j < n; j++ )
	            *p++ = c;
	      };
	   };

	return OK;
}


int pcx_write_pic( pic, fp )
PCXPIC *pic;
FILE *fp;
{
	unsigned char *p;
	int i, row, plane, bytes, nrows, nplan;
	
	fseek( fp, 0l, 0 );
	if ( fwrite( (char *)&(pic->hdr), sizeof(pic->hdr), 1, fp ) != 1 )
	   return ERROR;

	bytes = pic->hdr.bpl;
	nrows = pic->hdr.y2 - pic->hdr.y1 +1;
	nplan = pic->hdr.nplanes;

	for ( row = 0; row < nrows; row++ )
	   for ( plane = 0; plane < nplan; plane++, pcx_xputc(-1,fp) )
	      for ( p = pic->rows[plane][row], i=bytes; i--; )
	         if ( pcx_xputc( *p++, fp ) )
	            return ERROR;

	pcx_xputc( -1, fp );
	return OK;
}


