 /*
  * Khoros: $Id: pbm.c,v 1.3 1992/03/20 23:35:54 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: pbm.c,v 1.3 1992/03/20 23:35:54 dkhoros Exp $";
#endif

 /*
  * $Log: pbm.c,v $
 * Revision 1.3  1992/03/20  23:35:54  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */


/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 >>>>
 >>>>         File Name: pbm.c
 >>>>
 >>>>      Program Name: pbm conversion programs
 >>>>
 >>>> Date Last Updated: Fri Oct 26 23:33:41 1990 
 >>>>
 >>>>          Routines: read_pbm
 >>>>			 write_pbm
 >>>>
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/


#include "unmcopyright.h"        /* Copyright 1990 by UNM */
#include "vinclude.h"
#include "file_formats/pbm.h"

static   void read_whitespace();
static   int  read_pbmfile(), read_pgmfile(), read_ppmfile();


/****************************************************************
*
* Routine Name: read_pbm 
*
* Purpose:  Load a portable bitmap/pixmap (pbm) structure from the
*	    pbm file.
*
* Input:    filename - the pbm file
*    
*    
* Output:   pbm - the returns the newly loaded pbm structure or NULL
*		  upon failure.
*    
*    
* Written By: Mark Young
*
*    
*    
****************************************************************/


struct pbm *read_pbm(filename)

char   *filename;
{
	FILE	 *file;
        char     line[LENGTH];
	struct   pbm *pbm;


	/*
	 *  Open the input pbm file
	 */
	if (!vread_ascii(filename, &file))
	   return(FALSE);

	/*
	 *  Malloc space for the pbm structure.
	 */
	if (!(pbm = (struct pbm *) calloc(1, sizeof(struct pbm))))
	{
           (void) fprintf(stderr,"read_pbm:  Unable to allocate memory for \
pbm image structure.\n");
	   return(NULL);
	}

        if (!fgets(line, LENGTH, file))
        {
           (void) fprintf(stderr,"read_pbm:  Error!  Unexpected end of \n\
file encountered.  Portable bitmap/pixmap is not of the proper format.\n");
	   free(pbm);
           return(NULL);
        }
        if (strncmp(line, "P1", 2) == 0)
	{
           pbm->type = PBM;
	   pbm->raw  = FALSE;
	}
        else if (strncmp(line, "P4", 2) == 0)
	{
           pbm->type = PBM;
	   pbm->raw  = TRUE;
	}
        else if (strncmp(line, "P2", 2) == 0)
	{
           pbm->type = PGM;
	   pbm->raw  = FALSE;
	}
        else if (strncmp(line, "P5", 2) == 0)
	{
           pbm->type = PGM;
	   pbm->raw  = TRUE;
	}
        else if (strncmp(line, "P3", 2) == 0)
	{
           pbm->type = PPM;
	   pbm->raw  = FALSE;
	}
        else if (strncmp(line, "P6", 2) == 0)
	{
           pbm->type = PPM;
	   pbm->raw  = TRUE;
	}
        else
        {
           (void) fprintf(stderr,"read_pbm:  Error!  Invalid magic number. \n\
Valid Portable bitmap/pixmap begin with either P1 thru P6. .\n");
	   free(pbm);
           return(NULL);
        }

        /*
         *  Get the image's width and height
         */
	read_whitespace(file);
	if (fscanf(file, " %d", &pbm->width) != 1)
	{
           (void) fprintf(stderr,"read_pbm:  Error!  Invalid width. \n\
Unable to read width of the pbm image.\n");
	   free(pbm);
           return(NULL);
	}

	read_whitespace(file);
	if (fscanf(file, " %d", &pbm->height) != 1)
	{
           (void) fprintf(stderr,"read_pbm:  Error!  Invalid width. \n\
Unable to read height of the pbm image.\n");
	   free(pbm);
           return(NULL);
	}

        /*
         *  Call the appropriate routine to create the viff image
         */
        switch (pbm->type)
        {
           case PBM:
		if (!read_pbmfile(file, pbm))
		{
		   free(pbm);
		   return(NULL);
		}
                break;

           case PGM:
		if (!read_pgmfile(file, pbm))
		{
		   free(pbm);
		   return(NULL);
		}
                break;

           case PPM:
		if (!read_ppmfile(file, pbm))
		{
		   free(pbm);
		   return(NULL);
		}
                break;
        }
	return(pbm);
}




static void read_whitespace(file)

FILE *file;
{
	char temp[LENGTH];
	int  character, done = FALSE;


/*
 *  This whole routine use to look like the following:
 *
 *  do
 *  {
 *    status = fscanf(file, "%[ \t\n#]", &character);
 *    if (character == '#' && status != 0)
 *       (void) fgets(temp, 512, file);
 *  } while (status != 0);
 *
 *  This was changed due to a bug report by Tait Cyrus @ Convex who said that
 *  there seems to be a bug in the convex's fscanf() routine since it keeps
 *  core dumping.   The solution is to change it to a getc()/ungetc().  If
 *  this should prove to be a problem you may want to substitute it with the
 *  above code.
 *			Mark Young (8/30/91)
 */
	do
	{
	  character = fgetc(file);

	  if (character == EOF)
	     done = TRUE;
	  else if (character == '#')
	     (void) fgets(temp, LENGTH, file);
	  else if (isspace(character) == FALSE)
	  {
	     ungetc(character, file);
	     done = TRUE;
	  }
	} while (done == FALSE);
}



static int read_pbmfile(file, pbm)

FILE *file;
struct pbm *pbm;
{
	int    i, num_bytes, num;


	num_bytes = (pbm->width+7)/8*pbm->height;
	if (!(pbm->data = (char *) calloc(1, num_bytes)))
	{
           (void) fprintf(stderr,"read_pbm:  Unable to allocate memory for \
pbm image data.\n");
	   return(FALSE);
	}
	read_whitespace(file);

	if (pbm->raw)
	{
	   if ((num = fread(pbm->data, 1, num_bytes, file)) != num_bytes)
	   {
	      (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
bytes from the \nraw pbm file, but was only able to read %d bytes\n.",
			num_bytes, num);
	      free(pbm->data);
	      return(FALSE);
	   }
	}
	else
	{
	   for (i = 0; i < pbm->width * pbm->height; i++)
	   {
	      if (fscanf(file," %d", &num) != 1)
	      {
		 (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
values from the ascii pbm file, but was only able to read %d values\n",
			pbm->width * pbm->height, i);
		 free(pbm->data);
		 return(FALSE);
	      }
	      else
	      {
		 if (num == 1)
		    pbm->data[i/8] |= (1 << (i % 8));
		 else
		    pbm->data[i/8] &= ~(1 << (i % 8));
	      }
	   }
	}
	return(TRUE);
}



static int read_pgmfile(file, pbm)

FILE *file;
struct pbm *pbm;
{
	int    i, num_bytes, num;
	int    *data;


	if (pbm->raw)
	   num_bytes = pbm->width*pbm->height;
	else
	   num_bytes = pbm->width*pbm->height*sizeof(int);

	if (!(pbm->data = (char *) calloc(1, num_bytes)))
	{
           (void) fprintf(stderr,"read_pbm:  Unable to allocate memory for \
pbm image data.\n");
	   return(FALSE);
	}

        /*
         *  Get the maximum value
         */
	read_whitespace(file);
	if (fscanf(file, " %d", &pbm->maxval) != 1)
	{
           (void) fprintf(stderr,"read_pbm:  Error!  Invalid value. \n\
Unable to read maximum value of the pgm image.\n");
	   free(pbm->data);
           return(FALSE);
	}
	read_whitespace(file);

	if (pbm->raw)
	{
	   if ((num = fread(pbm->data, 1, num_bytes, file)) != num_bytes)
	   {
	      (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
bytes from the \nraw pbm file, but was only able to read %d bytes\n.",
			num_bytes, num);
	      free(pbm->data);
	      return(FALSE);
	   }
	}
	else
	{
	   data = (int *) pbm->data;
	   for (i = 0; i < pbm->width * pbm->height; i++)
	   {
	      if (fscanf(file," %d", &num) != 1)
	      {
		 (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
values from the ascii pbm file, but was only able to read %d values\n",
			pbm->width * pbm->height, i);
		 free(pbm->data);
		 return(FALSE);
	      }
	      else
		 data[i] = num;
	   }
	}
	return(TRUE);
}



static int read_ppmfile(file, pbm)

FILE *file;
struct pbm *pbm;
{
	int      i, num_bytes, num, *data;


	if (pbm->raw)
	   num_bytes = pbm->width*pbm->height*3;
	else
	   num_bytes = pbm->width*pbm->height*sizeof(int)*3;

	if (!(pbm->data = (char *) calloc(1, num_bytes)))
	{
           (void) fprintf(stderr,"read_pbm:  Unable to allocate memory for \
pbm image data.\n");
	   return(FALSE);
	}

        /*
         *  Get the maximum value
         */
	read_whitespace(file);
	if (fscanf(file, " %d", &pbm->maxval) != 1)
	{
           (void) fprintf(stderr,"read_pbm:  Error!  Invalid value. \n\
Unable to read maximum value of the pgm image.\n");
	   free(pbm->data);
           return(FALSE);
	}
	read_whitespace(file);

	if (pbm->raw)
	{
	   if ((num = fread(pbm->data, 1, num_bytes, file)) != num_bytes)
	   {
	      (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
bytes from the \nraw pbm file, but was only able to read %d bytes\n.",
			num_bytes, num);
	      free(pbm->data);
	      return(FALSE);
	   }
	}
	else
	{
	   data = (int *) pbm->data;
	   for (i = 0; i < pbm->width * pbm->height * 3; i++)
	   {
	      if (fscanf(file," %d", &num) != 1)
	      {
		 (void) fprintf(stderr,"read_pbm: Error!  Attempted to read %d \
values from the ascii pbm file, but was only able to read %d values\n",
			pbm->width * pbm->height * 3, i);
		 free(pbm->data);
		 return(FALSE);
	      }
	      else
		 data[i] = num;
	   }
	}
	return(TRUE);
}



/****************************************************************
*
* Routine Name: write_pbm 
*
* Purpose:  Writes a portable bitmap/pixmap (pbm) file from the
*	    pbm structure.
*
* Input:    filename - the pbm file
*	    pbm	     - the pbm structure to be written
*    
*    
* Output:   creates the pbm file and returns true if succesful
*	    otherwise we return false.
*    
*    
* Written By: Mark Young
*
*    
*    
****************************************************************/


int write_pbm(filename, pbm)

char   *filename;
struct pbm *pbm;
{
	FILE	 *file;
        char     line[LENGTH];
	int	 i, j, num_bytes, size, num;


	/*
	 *  Open the output pbm file
	 */
	if (!vwrite_ascii(filename, &file))
	   return(FALSE);

	if (pbm->type != PBM && pbm->type != PGM && pbm->type != PPM)
        {
           (void) fprintf(stderr,"write_pbm:  Error!  Invalid magic number. \n\
Valid Portable bitmap/pixmap begin with either P1 thru P6.\n");
           return(FALSE);
        }

	/*
	 *  Write the header
	 */
	if (pbm->raw)
	{
	   if (pbm->type == PBM)
	      (void) fprintf(file,"P4\n");
	   else if (pbm->type == PGM)
	      (void) fprintf(file,"P5\n");
	   else if (pbm->type == PPM)
	      (void) fprintf(file,"P6\n");
	}
	else
	{
	   if (pbm->type == PBM)
	      (void) fprintf(file,"P1\n");
	   else if (pbm->type == PGM)
	      (void) fprintf(file,"P2\n");
	   else if (pbm->type == PPM)
	      (void) fprintf(file,"P3\n");
	}

        /*
         *  Write the image's width and height
         */
	(void) fprintf(file,"%d %d\n", pbm->width, pbm->height);

	/*
	 *  Write the image's max value if the image is not a bitmap
	 */
	if (pbm->type == PGM || pbm->type == PPM)
	   (void) fprintf(file,"%d\n", pbm->maxval);

	/*
	 *  Check to see what format the image is to be written (raw or ascii)
	 */
	if (pbm->raw)
	{
	   if (pbm->type == PBM)
	      num_bytes = ((pbm->width+7)/8) * pbm->height;
	   else if (pbm->type == PGM)
	      num_bytes = pbm->width * pbm->height;
	   else if (pbm->type == PPM)
	      num_bytes = pbm->width * pbm->height * 3;

	   if ((num = fwrite(pbm->data, 1, num_bytes, file)) != num_bytes)
	   {
	      (void) fprintf(stderr,"write_pbm: Error!  Attempted to write %d \
bytes to the \nraw pbm file, but was only able to write %d bytes\n.",
			num_bytes, num);
	      return(FALSE);
	   }
	}
	else if (pbm->type == PBM)    /* writing ASCII PBM file */
	{
	   unsigned char *data;

	   data = (unsigned char *) pbm->data;
	   for (i = 0; i < pbm->width * pbm->height; i++)
	   {
	      if (data[i/8] & (1 << (i % 8)))
		 (void) fprintf(file, " 1");
	      else
		 (void) fprintf(file, " 0");

	      if (i % 35 == 0)
		 (void) fprintf(file,"\n");
	   }
	}
	else if (pbm->type == PGM)      /* writing ASCII PGM file */
	{
	   int *data;

	   data = (int *) pbm->data;
	   for (i = size = 0; i < pbm->width * pbm->height; i++)
	   {
	      (void) sprintf(line," %3d", data[i]);
 
	      size += VStrlen(line);
	      if (size > 70)
	      {
		 (void) fprintf(file,"\n");
		 size = VStrlen(line);
	      }
	      (void) fprintf(file,"%s", line);
	   }
	   (void) fprintf(file,"\n");
	}
	else                           /* writing ASCII PPM file */
	{
	   int *data;

	   data = (int *) pbm->data;
	   for (i = j = size = 0; i < pbm->width * pbm->height; i++)
	   {
	      (void) sprintf(line," %3d %3d %3d ",data[j],data[j+1],data[j+2]);
	      j += 3;

	      size += VStrlen(line);
	      if (size > 70)
	      {
		 (void) fprintf(file,"\n");
		 size = VStrlen(line);
	      }
	      (void) fprintf(file,"%s", line);
	   }
	   (void) fprintf(file,"\n");
	}
	return(TRUE);
}
