/*******************************************************************************
* Program: imageio.c
* Purpose: This souce code file contains functions for dynamically allocating
* and freeing 8-bit (unsigned char) images. It also contains functions for
* reading and writing images to files in raw PGM format. This code was written
* to be used as a teaching resource.
* Name: Michael Heath, University of South Florida
* Date: 1/7/2000
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "imageio.h"

/*******************************************************************************
* Function: allocate_image
* Purpose: This function allocates an image. The image is an array of pointers
* to arrays. The array of pointers will have a length of the number of rows,
* and each of these pointers will point to a separate one dimensional array
* whose length is the number of columns in the image. This scheme was used
* because it allows the image to be accessed using the syntax image[r][c]
* yet still allow the image to be any size.
* Name: Michael Heath, University of South Florida
* Date: 1/7/2000
*******************************************************************************/
unsigned char **allocate_image(int rows, int cols)
{
   unsigned char **image=NULL;
   int r, br;

   /****************************************************************************
   * Allocate an array of pointers of type (unsigned char *). The array is
   * allocated to have a length of the number of rows.
   ****************************************************************************/
   if((image = (unsigned char **) calloc(rows, sizeof(unsigned char *)))==NULL){
      fprintf(stderr, "Error allocating the array of pointers in allocate_image().\n");
      return((unsigned char **)NULL);
   }

   /****************************************************************************
   * For each row, allocate an array of type (unigned char).
   ****************************************************************************/
   for(r=0;r<rows;r++){
      if((image[r] = (unsigned char *) calloc(cols, sizeof(unsigned char)))==NULL){
         fprintf(stderr, "Error allocating an array in allocate_image().\n");
         for(br=0;br<r;br++) free(image[br]);
         free(image);
         return((unsigned char **)NULL);
      }
   }

   return(image);
}

/*******************************************************************************
* Function: free_image
* Purpose: This function frees the memort that was previously allocated to
* store an image.
* Name: Michael Heath, University of South Florida
* Date: 1/7/2000
*******************************************************************************/
void free_image(unsigned char **image, int rows)
{
   int r;

   /****************************************************************************
   * Free each row of the image.
   ****************************************************************************/
   for(r=0;r<rows;r++) free(image[r]);

   /****************************************************************************
   * Free the array of pointers.
   ****************************************************************************/
   free(image);
}

/******************************************************************************
* Function: read_pgm_image
* Purpose: This function reads in an image in raw PGM format. Because the PGM
* format includes the number of columns and the number of rows in the image,
* these are read from the file. Memory to store the image is allocated in this
* function. All comments in the header are discarded in the process of reading
* the image. Upon failure, this function returns 0, upon sucess it returns 1.
* Name: Michael Heath, University of South Florida
* Date: 1/7/2000
******************************************************************************/
int read_pgm_image(char *infilename, unsigned char ***image, int *rows,
    int *cols)
{
   FILE *fp;
   int r;
   char buf[71];

   /***************************************************************************
   * Open the input image file for reading. If the file can not be opened for
   * reading return an error code of 0.
   ***************************************************************************/
   if((fp = fopen(infilename, "r")) == NULL){
      fprintf(stderr, "Error reading the file %s in read_pgm_image().\n",
         infilename);
      return(0);
   }

   /***************************************************************************
   * Verify that the image is in PGM format, read in the number of columns
   * and rows in the image and scan past all of the header information.
   ***************************************************************************/
   fgets(buf, 70, fp);
   if(strncmp(buf,"P5",2) != 0){
      fprintf(stderr, "The file %s is not in PGM format in ", infilename);
      fprintf(stderr, "read_pgm_image().\n");
      fclose(fp);
      return(0);
   }
   do{ fgets(buf, 70, fp); }while(buf[0] == '#');  /* skip all comment lines */
   sscanf(buf, "%d %d", cols, rows);
   do{ fgets(buf, 70, fp); }while(buf[0] == '#');  /* skip all comment lines */

   /***************************************************************************
   * Allocate memory to store the image.
   ***************************************************************************/
   if(((*image) = allocate_image(*rows, *cols)) == NULL) return(0);

   /***************************************************************************
   * Read in the image from the file, one row at a time.
   ***************************************************************************/
   for(r=0;r<(*rows);r++){
      if((*cols) != fread((*image)[r], 1, (*cols), fp)){
         fprintf(stderr, "Error reading the image data in read_pgm_image().\n");
         fclose(fp);
         free_image((*image), *rows);
         return(0);
      }
   }

   fclose(fp);
   return(1);
}

/******************************************************************************
* Function: write_pgm_image
* Purpose: This function writes an image in raw PGM format. A comment can be
* written to the header if coment != NULL. If there is a comment, it can
* be up to 70 characters long.
* Name: Michael Heath, University of South Florida
* Date: 1/7/2000
******************************************************************************/
int write_pgm_image(char *outfilename, unsigned char **image, int rows,
    int cols, char *comment, int maxval)
{
   FILE *fp;
   int r;

   /***************************************************************************
   * Open the output image file for writing.
   ***************************************************************************/
   if((fp = fopen(outfilename, "w")) == NULL){
      fprintf(stderr, "Error writing the file %s in write_pgm_image().\n",
         outfilename);
      return(0);
   }

   /***************************************************************************
   * Write the header information to the PGM file.
   ***************************************************************************/
   fprintf(fp, "P5\n");
   if(comment != NULL)
      if(strlen(comment) <= 70) fprintf(fp, "# %s\n", comment);
   fprintf(fp, "%d %d\n", cols, rows);
   fprintf(fp, "%d\n", maxval);

   /***************************************************************************
   * Write the image data to the file.
   ***************************************************************************/
   for(r=0;r<rows;r++){
      if(cols != fwrite(image[r], 1, cols, fp)){
         fprintf(stderr, "Error writing the image data in write_pgm_image().\n");
         fclose(fp);
         return(0);
      }
   }

   fclose(fp);
   return(1);
}