/* this program crops a given greyscale raw image */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
  /* declare variables */
  FILE *in_file, *out_file;
  int in_x, in_y, out_x, out_y;
  int top, left, bottom, right;
  long in_buf_size, out_buf_size;

  register int i, j;

  unsigned char *in_buf, *out_buf;

  struct stat filestat;

  if (argc!=7 && argc!=9) {
    fprintf(stderr, "Usage: rawcrop in_file out_file in_width in_height width height left top\n");
    fprintf(stderr, "\tleft and top parameters are optional, default to (0,0)\n");
    fprintf(stderr, "\tuse (0, 0) to specify a center cropping\n");
    exit(1);
  }
  else {
    /* open input file */
    if ( (in_file=fopen(argv[1], "rb"))==NULL ) {
      fprintf(stderr, "Can not open input file %s\n", argv[1]);
      exit(1);
    }
    /* open output file */
    if ( (out_file=fopen(argv[2], "wb"))==NULL ) {
      fprintf(stderr, "Can not open output file %s\n", argv[2]);
      exit(1);
    }
    /* read input file dimension */
    if ( (in_x=atoi(argv[3])) <=0 || (in_y=atoi(argv[4])) <=0 ) {
      fprintf(stderr, "Invalid input image dimension %d x %d\n", in_x, in_y);
      exit(1);
    }
    /* check input image dimension */
    if ( stat(argv[1], &filestat) ) {
      fprintf(stderr, "Can not get the size of image file %s\n", argv[1]);
      exit(1);
    }
    else {
      if ( filestat.st_size != (in_x * in_y) ) {
	fprintf(stderr, "Input image size %d is not specified size %d",
	    filestat.st_size, in_x * in_y);
	exit(1);
      }
    }
    /* check output file dimension */
    if ( (out_x=atoi(argv[5])) <=0 || (out_y=atoi(argv[6])) <=0 ) {
      fprintf(stderr, "Invalid output image dimension %d x %d\n", out_x, out_y);
      exit(1);
    }
    /* check clip top-left corner position */
    if (argc == 7) {
      top = left = 0;
      fprintf(stderr, "No top-left corner window position specified\n");
      fprintf(stderr, "setting to default (1, 1)\n");
    }
    else {
      /* check for validity of top-left corner position */
      if ( (left=atoi(argv[7]))<0 || (top=atoi(argv[8]))<0 ) {
	fprintf(stderr, "Invalid top-left corner positions\n");
	exit(1);
      }
    }
    /* check for centering cropping specification */
    if ( left == 0 && top == 0 ) {
      left = (in_x - out_x) / 2;
      top = (in_y - out_y) / 2;
    }
    if ( out_x > in_x || out_y > in_y ||
	 (out_x+left) > in_x || (out_y+top) > in_y ) {
      fprintf(stderr, "Invalid cropping size specification\n");
      exit(1);
    }
  }

  /* report cropping options */
  printf("input image size \t%d by %d\n", in_x, in_y);
  printf("cropping window size \t%d by %d\n", out_x, out_y);
  printf("top-left corner position \t(%d, %d)\n", left+1, top+1);
  printf("absolute cropping window \t(%d, %d) to (%d, %d)\n",
	  left+1, top+1, left+out_x, top+out_y);

  /* allocating buffers */
  in_buf_size = in_x * in_y;
  out_buf_size = out_x * out_y;
  if ( (in_buf=malloc(sizeof(unsigned char)*in_buf_size)) == NULL ||
      (out_buf=malloc(sizeof(unsigned char)*out_buf_size)) == NULL ) {
    fprintf(stderr, "Can not allocate enough memory\n");
    exit(1);
  }

  /* read the input image into memory */
  if ( fread(in_buf, sizeof(unsigned char), in_buf_size, in_file) !=
       in_buf_size ) {
    fprintf(stderr, "Read input file %s error\n", argv[1]);
    exit(1);
  }
  
  /* perform cropping operation */
  for (i=0;i<out_y;i++)
    for (j=0;j<out_x;j++)
      out_buf[i*out_x+j] = in_buf[(i+top)*in_x+(j+left)];

  /* write output file */
  if ( fwrite(out_buf, sizeof(unsigned char), out_buf_size, out_file) !=
      out_buf_size) {
    fprintf(stderr, "Write output file %s error\n", argv[2]);
    exit(1);
  }

  /* clean up */
  fclose(in_file);
  fclose(out_file);
  free(in_buf);
  free(out_buf);
  return(0);
}