/************************************************************************ 
plm2jpg.c

This code is for the ECE1766 course taught by Prof. Mann at the University of Toronto.  It takes a plm file in and spits out a jpg file.  It takes a variety of parameters, eventually most of which will be optional.

It takes in 2 or 3 command line parameters.
1) source file (must be a plm)
2) output for jpg
3) quality of image (optional with default 95)

Written by Terry Borer
Nov. 17, 1999


JPG code courtesy of the Independant JPEG Group (IJG), and some routines from the cement program written by Steve Mann, Curtis Wickman and others.  This code is greatly taken from ppm2all.c written by Terry Borer and Lukas Stras.



ftp://ftp.styx.org/users/ww/gnuav
av.h: jpeg_compress_buffer, jpeg_decompress_buffer
*************************************************************************/

/* Functions headers.  See below for full details */
void Create_Lookup_Table ( double exponent, int max_pixel_value ); 
int convert(double curr);

#define HAVE_UNSIGNED_CHAR 
#include <stdio.h>
#include <stdlib.h>
#include "writejpeg.h"
#include <math.h>
#include <unistd.h>

#define BUF_SIZE 128
#define BYTES_PIXEL 3
#define IMAGE_QUALITY 95

double LookupTable[255+1];
int max_pixel_val; /* Should always be 255 */
int main (int argc, char **argv) {

FILE *source, *dest;
int x_dim,y_dim; /* The high resolution dimensions */

double *sample; /* big input array */
/* declare four brilliantly named variables */
int i,j,k;
double exponent;
unsigned char *out_samp; /* downsized output array */
unsigned char buf[BUF_SIZE+1];
/* These variables are neccessary for the operation of the JPEG routines */
extern int image_height;  /* scaled height */
extern int image_width;   /* scaled width */
extern int image_quality; /* Between 0 and 100.  95 was used last year */
int bytes_read; 
int block_size;


if (argc!=3 && argc != 4) 
  {
    fprintf(stderr, "Usage: plm2jpg source dest [quality]\n");
    exit(1);
  }

/* Set the JPEG default quality now */
image_quality = IMAGE_QUALITY;

/* If the optional parameter is set, change the quality number */
if (argc == 4)
  image_quality = atoi(argv[3]);

/* atoi returns zero if failure, so we check if something is wrong. Wrong user input at this stage will crash the program later */
if (image_quality == 0)
  {
    fprintf(stderr, "Invalid quality\n");
    exit(1);
  }

/* Open the input file, a plm */
if ((source=fopen(argv[1],"r"))==0) 
  {
    fprintf(stderr, "Unable to open file %s.\n",argv[1]);
    exit(1);
  }

/* Open the first output file (jpg file) */
if ((dest=fopen(argv[2],"w"))==0)
  {
    fprintf(stderr, "Unable to open file %s for writing.\n",argv[2]);
    exit(1);
  }

/* Comments will start with a '#', and we wish to ignore them */
while(1)
  {
    fgets(buf,BUF_SIZE,source);
    if (buf[0]!='#') break;
  }
if (strcmp(buf,"PA\n")!=0)
  {
    fprintf(stderr, "Not a plm file.\n");
    exit(1);
  }

/* Ignore more comments if they exist */
while(1)
  {
    fgets(buf,BUF_SIZE,source);
    if (buf[0]!='#') break;
  }

/* Grab the two dimension from the file */
if (sscanf(buf,"%d%d",&x_dim,&y_dim)!=2)
  {
    fprintf(stderr, "Error reading dimensions!\n");
    exit(1);
  }

/* Ignore more comments */
while(1)
  {
    fgets(buf,BUF_SIZE,source);
    if (buf[0]!='#') break;
  }

if (sscanf(buf,"%d",&max_pixel_val)!=1)
  {
    fprintf(stderr, "Error reading in max pixel value");
    exit(1);
  }
fgets(buf,BUF_SIZE,source);
fgets(buf,BUF_SIZE,source);
if (sscanf(buf,"%lf",&exponent)!=1)
  {
    fprintf(stderr,"Error reading in exponent value\n");
    exit(1);
  }
exponent = 4.7;



/* allocate a HUGE array */
sample = (double *)malloc(BYTES_PIXEL*x_dim*sizeof(double));
if (!sample) 
  {
    fprintf(stderr, "Error allocating memory!");
    exit (1);
  }
/* Set all values to zero (this command doesn't do much) */
//memset(sample,0,BYTES_PIXEL*x_dim*sizeof(double));
/* Set the jpg parameters now */
image_height = y_dim;
image_width  = x_dim;


out_samp = (char *)malloc(BYTES_PIXEL*image_height*image_width*sizeof(unsigned char));
if (!out_samp)
  {
    fprintf(stderr, "Error allocating memory!");
    exit(1);
  }




    /* Here we go through the loop and convert the special double to an unsigned char.  We also create the lookup table*/
printf("exp is %f, max is %d\n",exponent,max_pixel_val);
printf("x_dim is %d, y_dim is %d\n",x_dim,y_dim);
fflush(stdout);
Create_Lookup_Table(exponent, max_pixel_val);
block_size = x_dim*BYTES_PIXEL;

for (i=0,k=0;i<y_dim;i+=1) /* grab a block */
  {

  /* Read in a whole bunch of memory.     Hopefull DMA is being used so it will be fast. */
     if ((bytes_read = fread(sample,sizeof(double),block_size,source)) < block_size)
       {
	 fprintf(stderr, "Expecting to read %d bytes; read %d\n", block_size*sizeof(double), bytes_read);
	    exit(1);
       }
     for (j=0;j<block_size;j++) 
      {	

	    out_samp[k++]=(unsigned char)convert(sample[j]);
//	    if (out_samp[i]>0) printf("%c ",out_samp[i]);
//	    out_samp[i]=(unsigned char)((int)sample[i]%255);
      }
  }
free(sample);
printf("About to write!\n");fflush(stdout);

printf("Dimension are: x_dim %d, y_dim %d, k is %d.\n",image_width,image_height,k);
printf("Quality is %d\n",image_quality);
/* Write out to the JPEG library */
write_JPEG_file ((JSAMPLE *)out_samp,dest);
free(out_samp);

fclose(source);
fclose(dest);

return 0;
}
/***************************************************************************
This function will allocate and build the lookup table.  It takes in the exponent as an argument, and the max_pixel_value.  It builds a table of unsigned chars of max_pixel_value elements, finding their exponent values.
***************************************************************************/
void Create_Lookup_Table ( double exponent, int max_pixel_value ) 
  {
    int l;
    for (l=0;l<max_pixel_value;l++)
      {
	LookupTable[l]=pow((double)l,exponent);
//	printf("%d %g\n",l,LookupTable[l]);
      }
  }

/*************************************************************************** 
This function takes a double from the plm file and find the reverse exponent, or more properly, the appropriate character.  It returns an int to allow range errors to be detected more easily.  It uses a lookup table and a binary search scheme (binary sear
ch first implemented by Curtis Wickman for this purpose). 
***************************************************************************/
int convert(double curr) 
  {
    int low,high,middle;
    high = max_pixel_val + 1;

    low = 0;
    middle = high / 2; /* high / 2 */
    while (high != middle && low != middle) 
      {

	if (curr > LookupTable[middle]) 
	  {
	    low = middle;
	    middle = (high-middle)/2+middle;
	  }
	else if (curr < LookupTable[middle])
	  {
	    high = middle;
	    middle = (middle-low)/2 + low;
	  }
       else return middle;
      }
    return middle;
  }



