/*=========================================================================
 *
 * PCX.CPP - 1 December 1998, Dave Humphrey
 *
 * General routines for handlign PCX files.
 *
 *=======================================================================*/


	/* Required Includes */
#include "pcx.h"


/*=========================================================================
 *
 * Class PCXHeader Method - void destroy (void);
 *
 *=======================================================================*/
void PCXHeader::destroy (void) {

  manufacturer = 10;
  version = 5;
  encoding = 1;
  bits_per_pixel = 8;
  x = y = 0;
  width = height = 0;
  horz_res = vert_res = 150;
  reserved = 0;
  num_color_planes = 1;
  bytes_per_line = 0;
  palette_type = 1;
  memset (ega_palette, 0, 48);
  memset (padding, 0, 58);
  padding[0] = 0;
  padding[1] = 4;
  padding[2] = 0;
  padding[3] = 3;
 }
/*=========================================================================
 *		End of Class Method PCXHeader::destroy()
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXHeader Method - boolean read (f);
 *
 * Attempts to read in the header from the given file handle.  Returns
 * FALSE on any error.
 *
 *=======================================================================*/
boolean PCXHeader::read (FILE *f) {

	/* Load the header all at once */
  if (fread (this, sizeof(char), 128, f) != 128) {
    err_code = ERR_READ;
    return (FALSE);
   }

	/* Adjust to make life easier */
  width++;
  height++;

  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method PCXHeader::read()
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXHeader Method - boolean write (f);
 *
 * Attempts to write the header to the given file handle.  Returns
 * FALSE on any error.
 *
 *=======================================================================*/
boolean PCXHeader::write (FILE *f) {

	/* Adjust to make life easier */
  width--;
  height--;

	/* Write the header all at once */
  if (fwrite (this, sizeof(char), 128, f) != 128) {
    err_code = ERR_WRITE;
    return (FALSE);
   }

	/* Readjust */
  width++;
  height++;

  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method PCXHeader::write()
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Destructor
 *
 *=======================================================================*/
void PCXImage::destroy (void) {

	/* Delete the allocate image, if any */
  DESTROY(data);

	/* Clear the header info */
  header.destroy();
 }
/*=========================================================================
 *		End of Class PCXImage Destructor
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Method - boolean export (file, width, height, image, pal);
 *
 * Use this function for exporting a LBM image format to a PCX file.
 * The image is a Linear-BitMap, the pixels lie one after another in a
 * linear fashion (total length is width*height).  The palette is a RGB
 * color entries at least 256 colors in size (768 bytes).  The current
 * PCX image is destroyed, if any.  Returns FALSE on any error.
 *
 *=======================================================================*/
boolean PCXImage::export (const char *filename, const short width, const short height, unsigned char *image, const unsigned char *pal) {

	/* Make sure all the pointers are valid */
  if (!filename || !image || !pal || !width || !height) {
    err_code = ERR_NULL;
    return (FALSE);
   }

	/* Destroy the current image */
  destroy();

	/* Set the appropiate values */
  header.width = width;
  header.height = height;
  header.bytes_per_line = width;
  data = image;
  memcpy (palette, pal, 768);

	/* Attempt to save the image */
  if (!save (filename)) {
    data = NULL;
    destroy();
    return (FALSE);
   }

  data = NULL;
  destroy();
  return (TRUE);
 }
/*=========================================================================
 *
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Method - boolean load (filename);
 *
 * Attempts to read in the specified PCX file.  Returns FALSE on any error.
 *
 *=======================================================================*/
boolean PCXImage::load (const char *filename) {
  FILE *f;	/* File pointer */
  unsigned long image_size;
  size_t count = 0;
  int i = 0, row_diff = 0;
  int row = 0, bytes = 0;
  int num_bytes, ch;

	/* Attempt to open the file for input */
  if (!(f = openfile (filename, "rb"))) return (FALSE);

	/* Clear the current image contents, if any */
  destroy();

	/* Read in the header */
  if (!header.read(f)) {
    fclose (f);
    return (FALSE);
   }

	/* Make sure the image is small enough to fit */
  image_size = ((unsigned long)header.width) * ((unsigned long)header.height) + 1;

  if (image_size > UINT_MAX) {
    err_code = ERR_64KB;
    fclose (f);
    return (FALSE);
   }

	/* Attempt to allocate the image data */
  data = (unsigned char *) create_ptr ((size_t)image_size);

	/* Read in the image data */
  row_diff = header.bytes_per_line - header.width;

  while (count <= image_size) {

	/* Remove any row padding */
    if (row_diff > 0 && i >= header.width) {

      if (i == header.bytes_per_line) {
	i = 0;
	row++;
	count--;
       }
      else if (i >= header.width) {
	i = 0;
	row++;

	while (i != row_diff) {
	  ch = fgetc(f);
	  bytes++;

	  if (ch >= 192) {
	    ch = fgetc(f);
	    bytes++;
	   }

	  i++;
	 }

	i = 0;
       }

     }
    else {
		/* Get some data */
      ch = fgetc(f);
      bytes++;

		/* Is this byte a RLE code? */
      if (ch >= 192) {

		/* How many bytes in run? */
	num_bytes = ch - 192;

		/* Get the actual data for the run */
	ch = fgetc(f);
	bytes++;

		/* Replicate data in image buffer */
	while (num_bytes-- > 0) {
	  data[count] = (unsigned char) ch;
	  count++;
	  i++;
	 }

       }    	/* Copy data into image buffer */
      else {
	data[count] = (unsigned char) ch;
	count++;
	i++;
       }
     }
   } /* End while loop */

	/* Read in the image palette */
  fseek (f, -768l, SEEK_END);

  if (fread(palette, sizeof(char), 768, f) != 768) {
    err_code = ERR_READ;
    return (FALSE);
   }

	/* Close the file and return success */
  fclose (f);
  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method PCXImage::load()
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Method - boolean save (filename);
 *
 * Attempts to save the PCX image to the specified file.  Returns FALSE
 * on any error.
 *
 *=======================================================================*/
boolean PCXImage::save (const char *filename) {
  FILE *f;	/* File pointer */
  unsigned char *ptr, pre;
  size_t image_size, i;
  int y = 1, count;

	/* Attempt to open the file for output */
  if (!(f = openfile (filename, "wb"))) return (FALSE);

	/* Write the header information */
  if (!header.write(f)) {
    fclose (f);
    return (FALSE);
   }

	/* Compute the image size */
  image_size = header.width * header.height;
  ptr = data;
  pre = *ptr;
  ptr++;

	/* Write the main image */
  for (i = 0; i < image_size; i++) {
    count = 1;

    while (*ptr == pre && count < 63 && y != header.width) {
      ptr++;
      count++;
      i++;
      y++;
     }

    if (pre < 192 && count == 1)
      fputc(pre, f);
    else if (count == 1) {
      fputc(193, f);
      fputc(pre, f);
     }
    else { /* RLE encoded bytes */
      fputc ((unsigned char)(192+count), f);
      fputc (pre, f);
     }

    if (y == header.width) y = 0;
    pre = *ptr;
    ptr++;
    y++;
   }

	/* Need this for some reason? */
  fputc(0x0C, f);

	/* Write palette data */
  if (fwrite (palette, sizeof(char), 768, f) != 768) {
    fclose (f);
    err_code = ERR_WRITE;
    return (FALSE);
   }

	/* Close the file and return success */
  fclose (f);
  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method PCXImage::save()
 *=======================================================================*/