/************************************************************************ 
ppm2all.c

This code is for the ECE1766 course taught by Prof. Mann at the University of Toronto.  It purpose is to be called after the digital camera has taken an 18 meg .ppm file.  It then grabs the file and spits out three different files.

It takes in 4 or 5 command line parameters.
1) source file (must be a ppm)
2) output for low resolution jpg file
3) output for low resolution ppm file
4) output for high resolution jpg file
5) N, the downsampling factor (if omitted N=5)

Written by Terry Borer and Lukas Stras
Nov. 16, 1999

JPG code courtesy of the Independant JPEG Group (IJG), and some routines from the cement program written by Steve Mann and others.



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




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


#define N_DEF 5
#define BUF_SIZE 128
#define BYTES_PIXEL 3
#define IMAGE_QUALITY 85

int main (int argc, char **argv) {


int x_dim,y_dim; /* The high resolution dimensions */
int max_pixel_val; /* Should always be 255 */
unsigned char *sample; /* big input array */
/* declare four brilliantly named variables */
int i,j,k,l;
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 N = N_DEF;
char *high_res_jpg_path;
char *low_res_ppm_path;
char *low_res_jpg_path;
char *source_path;
FILE *source, *high_res_jpg,*low_res_ppm,*low_res_jpg;



if (argc!=5 && argc != 6) 
  {
    fprintf(stderr, "Use: ppm2all source_image.ppm lowres_dest.ppm.jpg lowres_dest.ppm highres_dest.jpg [downsample]\n");
    exit(1);
  }
/* Set jpg parameters */
high_res_jpg_path  = argv[4];
low_res_ppm_path  = argv[3];
low_res_jpg_path  = argv[2];
source_path       = argv[1];

image_quality = IMAGE_QUALITY;
/* If the optional parameter is set, change the downsampling number */
if (argc == 6)
  image_quality = atoi(argv[5]);

/* 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 downsampling rate\n");
    exit(1);
  }

/* Open the input file, a ppm, probably taken from the digital camera */
if ((source=fopen(source_path,"r"))==0) 
  {
    fprintf(stderr, "Unable to open file %s.\n",source_path);
    exit(1);
  }


/* Open the first output file (low res jpg file) */
if ((low_res_jpg=fopen(low_res_jpg_path,"w"))==0)
  {
    fprintf(stderr, "Unable to open file %s for writing.\n",low_res_jpg_path);
    exit(1);
  }

/* Open the secpmd output file (high res jpg file) */
if ((high_res_jpg=fopen(high_res_jpg_path,"w"))==0)
  {
    fprintf(stderr, "Unable to open file %s for writing.\n",high_res_jpg_path);
    exit(1);
  }

/* Open the third output file (low res ppm file) */
if ((low_res_ppm=fopen(low_res_ppm_path,"w"))==0)
  {
    fprintf(stderr, "Unable to open file %s for writing.\n",low_res_ppm_path);
    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,"P6\n")!=0)
  {
    fprintf(stderr, "Not a ppm 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);
  }

/* allocate a HUGE array */
sample = (char *)malloc(BYTES_PIXEL*y_dim*x_dim*sizeof(unsigned char));
if (!sample) 
  {
    fprintf(stderr, "Error allocating memory!");
    exit (1);
  }


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

/* set the current jpeg paramets */
image_height = y_dim;
image_width = x_dim;
/* Write out to the JPEG library */
write_JPEG_file ((JSAMPLE *)sample,high_res_jpg);
printf("Finished writing high res jpg :%s\n",high_res_jpg_path);



/* set the jpeg parameters, and use them in malloc.  Because of truncation in integer division, we must add N-1 to ensure the correct amount of memory is allocated. */
image_height = (y_dim+N-1) / N;
image_width = (x_dim+N-1) / N;


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 sample every N pixels in each direction */
/* k goes up by one each loop, so it squashes the big array into the little one.  The code is a bit confusing, but trust us, it works.  */
for (k=0,i=0;i<y_dim;i+=N) /* x offset */
  {
    for (j=0;j<x_dim;j+=N,k+=BYTES_PIXEL) /* y offset */
      {
 
	for (l=0;l<BYTES_PIXEL;l++)
	  {
	    out_samp[k+l]=(unsigned char)sample[BYTES_PIXEL*(j+i*x_dim)+l];
	    /*	    printf("%d %d\n",k+l,BYTES_PIXEL*(j+i*x_dim)+l); */
	  }
	    
      }
  }
free(sample);
/* Now we write out the downsampled image to disk because we want a downsampled ppm as well. */
/* first the header */
fprintf(low_res_ppm,"P6\n%d %d\n%d\n",image_width,image_height,max_pixel_val);
fwrite(out_samp,1,image_height*image_width*BYTES_PIXEL,low_res_ppm);
printf("Finished writing low res ppm :%s.\n",low_res_ppm_path);


/* Write out to the JPEG library */
write_JPEG_file ((JSAMPLE *)out_samp,low_res_jpg);
printf("Finished writing low res jpg :%s.\n",low_res_jpg_path);
free(out_samp);

fclose(source);
fclose(low_res_jpg);
fclose(high_res_jpg);
fclose(low_res_ppm);

return 0;
}

