
/*
 * jslice.c
 *
 * This program will take a slice of a given picture and save it to
 * a second file name
 *
 */

/*
 * CHANGES
 * 
 * 21/05/2000 - Added image-dimentions
 * 25/05/2000 - Fixed up hyperlink detection from slice file
 *
 */ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/*
 * Include file for users of JPEG library.
 * You will need to have included system headers that define at least
 * the typedefs FILE and size_t before you can include jpeglib.h.
 * (stdio.h is sufficient on ANSI-conforming systems.)
 * You may also wish to include "jerror.h".
 */

#include "jpeglib.h"
#include <setjmp.h>

JSAMPROW * arow;	/* points to a row of JSAMPLES*/
JSAMPLE * buffer;	/* Points to large array of R,G,B-order data */
JSAMPLE * image;
JSAMPROW wimage[2048]; /* array of rows for the "whole" image */
JSAMPROW pimage[2048]; /* array of rows for the "partial" image */

#define _VERSION "0.1.2"

#define MAX_HEIGHT 2048
#define MAX_WIDTH 2048

#define _CUT 1
#define _UNCUT 0

#define _ERR_CANTOPENJPEG 101
#define _ERR_CANTWRITEJPEG 102
#define _ERR_CANTREADSCRIPT 103
#define _ERR_INSUFFICENTMEM 200

int image_height;	/* Number of rows in image */
int image_width;		/* Number of columns in image */
int p_height; 	/* width of image after cut */
int p_width;  	/* height of image after cut */


/* define a structure that will keep the details of each box slice.
*/

struct _aslice {

	int ax, bx, ay, by;
	char link[128];
	};

typedef struct _aslice _tslice;
int slicesx[2048];
int slicesy[2048];


/* And of course, create a global variable out of it.
*/

_tslice slices[100];


GLOBAL(void)
write_JPEG_file (char * filename, int quality)
{
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * outfile;		/* target file */
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(_ERR_CANTWRITEJPEG);
  }
  jpeg_stdio_dest(&cinfo, outfile);

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = p_width; 	/* image width and height, in pixels */
  cinfo.image_height = p_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */

  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
		 row_pointer[0] = pimage[cinfo.next_scanline];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
}


/*
 * Here's the extended error handler struct:
 */

struct my_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}


/*
 * Sample routine for JPEG decompression.  We assume that the source file name
 * is passed in.  We want to return 1 on success, 0 on error.
 */


GLOBAL(int)
read_JPEG_file (char * filename)
{

	
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * infile;		/* source file */
	JSAMPROW row_pointer[1];
	
  int row_stride;		/* physical row width in output buffer */
	

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(_ERR_CANTOPENJPEG);
  }

  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;

  if (setjmp(jerr.setjmp_buffer)) {
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    exit(_ERR_INSUFFICENTMEM);
  }

  jpeg_create_decompress(&cinfo);

  jpeg_stdio_src(&cinfo, infile);

  (void) jpeg_read_header(&cinfo, TRUE);
  (void) jpeg_start_decompress(&cinfo);


	image_width = cinfo.output_width;
	image_height = cinfo.output_height;
	
  row_stride = cinfo.output_width * cinfo.output_components;	

		
  while (cinfo.output_scanline < cinfo.output_height) {

		wimage[cinfo.output_scanline] = malloc(sizeof(JSAMPLE) *(row_stride+1));
  	row_pointer[0] = wimage[cinfo.output_scanline];
		
    (void) jpeg_read_scanlines(&cinfo, row_pointer, 1);

    /* Assume put_scanline_someplace wants a pointer and sample count. */

	  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
   */

  /* And we're done! */
  return 1;
}





int get_cut_count_x( int maxx ) {

	int count = 0;
	int x;

	for (x = 0; x < maxx; x++) {
		if (slicesx[x] != _UNCUT) count++;
		}

	return count;
	}


int get_cut_count_y( int maxy ) {

	int count = 0;
	int y;

	for (y = 0; y <= maxy; y++) {
		if (slicesy[y] != _UNCUT) count++;
		}

	return count;
	}





int count_cuts( int sx, int fx ) {

	int cuts = 0;
	int cx;

	for (cx = sx; cx < fx; cx++){
		if (slicesx[cx] == _CUT) cuts++;
		}

	return cuts;
}





/* cut
 *
 * this procesdure "cuts" out the required portion of the picture.
 *
 */
 
int cut( int ax, int ay, int bx, int by ) {

	int cx, cy;
	
	p_width = bx - ax +1;
	p_height = by - ay +1;
	
	for (cy = 0; cy < p_height+1; cy++) {
	
		pimage[cy] = malloc(sizeof(JSAMPLE) *(p_width*3));
		
		for (cx = 0; cx < (p_width*3); cx++) {
		
			pimage[cy][cx] = wimage[cy+ay][cx+(ax*3)];
			
			} /* for cx */
		} /* for cy */
		
	return 0;
	
	}
			
			
int dump_slices( void ) {

	int i;
	fprintf(stdout,"\n<!-- ");
	for (i = 0; i < 2047; i++){
		if (slicesx[i] == _CUT) fprintf(stdout,"%d, ",i);
	}
	fprintf(stdout," -->\n");

	return 0;
}



int script_cut( char *script_name, char *foutprefix, int quality ){

#define MAX 1023

	FILE *script;
	int count;
	int ax;
	int i, numx, numy;
	int slicewidth;
	int tally;
	int cols;
	int lastx;
	int ffr;
	int slicew, sliceh;
	int real_url = 0;
	int urltest;
	int linecount = 0;
	char line[1024];
	char foutname[1024];
	char *link;
	
	script = fopen(script_name,"r");
	
	/* 
	 * Make sure we don't try reading dud files 
	 */
	if (script == NULL) {
		fprintf(stderr,"Error when trying to open the slice-coordinate file %s\nPlease check to see that the file name is correct, and that it is a text only file\n",script_name);
		exit(_ERR_CANTREADSCRIPT);
		}
		
	count = 0;
	
	/* clean out the slices rulers */
	for (ax = 0; ax < 2047; ax++) {
		slicesx[ax] = _UNCUT;
		slicesy[ax] = _UNCUT;
		}

	/* get all our lines... */
	while (fgets(line, MAX, script) != NULL) {
	
		linecount++;
		
		/*
		 * Digest our line if it's not a comment 
		 */
		if ((line[0] != '#')&&(strlen(line) > 7)){
		slices[count].ax = atoi(strtok(line," "));
			if (slices[count].ax > image_width) {
				fprintf(stderr,"Line %d: X1 value of %d too large, reduced to %d\n",linecount,slices[count].ax,image_width);
				slices[count].ax = image_width;
				}
			if (slices[count].ax < 0) {
				fprintf(stderr,"Line %d: X1 value of %d too small, value now 0.\n",linecount,slices[count].ax);
				slices[count].ax = 0;
				}
		slices[count].ay = atoi(strtok(NULL," "));
			if (slices[count].ay >= image_height) {
				fprintf(stderr,"Line %d: Y1 value of %d too large, reduced to %d\n",linecount,slices[count].ay,image_height-2);
				slices[count].ay = image_height-2;
				}
			if (slices[count].ay < 0) {
				fprintf(stderr,"Line %d: Y1 value of %d too small, value now 0.\n",linecount,slices[count].ay);
				slices[count].ay = 0;
				}
		slices[count].bx = atoi(strtok(NULL," ")); 
			if (slices[count].bx > image_width) {
				fprintf(stderr,"Line %d: X2 value of %d too large, reduced to %d\n",linecount,slices[count].bx,image_width);
				slices[count].bx = image_width;
				}
			if (slices[count].bx < 0) {
				fprintf(stderr,"Line %d: X2 value of %d too small, value now 0.\n",linecount,slices[count].bx);
				slices[count].bx = 0;
				}
		slices[count].by = atoi(strtok(NULL," "));
			if (slices[count].by >= image_height) {
				fprintf(stderr,"Line %d: Y2 value of %d too large, reduced to %d\n",linecount,slices[count].by,image_height-2);
				slices[count].by = image_height-2;
				}
			if (slices[count].by < 0) {
				fprintf(stderr,"Line %d: Y2 value of %d too small, value now 0.\n",linecount,slices[count].by);
				slices[count].by = 0;
				}
		
		link = strtok(NULL,"\0");
		
		/*
		 * Test to see that the URL string is potentially valid
		 *
		 */
		if (link != NULL) {
			for (urltest = 0; urltest < strlen(link); urltest++){
				real_url += isalnum(link[urltest]);
				}
			if (urltest > 0) strcpy(slices[count].link, link);
			}
		else slices[count].link[0] = '\0';
		

		/*
		 * Stamp in the slice arrays our marks where we have to slice
		 *
		 */
		slicesx[slices[count].ax] = _CUT;
		slicesx[slices[count].bx] = _CUT;
		slicesy[slices[count].ay] = _CUT;
		slicesy[slices[count].by] = _CUT;

		count++;
		}
	}

	/*
	 * Find out how many slices/cuts we have to make on each axis
	 *
	 */
	numx = get_cut_count_x(2047);
	numy = get_cut_count_y(2047);

	/*
	 * Let the HTML Table start flowing
	 *
	 */
	fprintf(stdout,"<TABLE border=0 cellpadding=0 cellspacing=0><TR>\n");
		
	/* 
	 * Print out our SLICING SPACERS! 
	 *
	 */
	slicewidth = tally = 1;
	for ( ax = 1; ax < 2047; ax++ ){
		if (slicesx[ax] == _CUT) {
			fprintf(stdout,"<TD><img src=\"spacer.gif\" width=\"%d\" height=\"1\"></td>",slicewidth);
			tally += slicewidth;
			slicewidth=0;
			}
		slicewidth++;
	}

	fprintf(stdout,"</tr>\n<TR>\n");

	dump_slices();

	lastx = -1;
	ffr = 1;
	for ( i = 0; i < count; i++ ){

		sprintf(foutname,"%s%d.jpg",foutprefix,i+1);
		cols = count_cuts(slices[i].ax, slices[i].bx);

		/* Catch the scenario where the blocks wrap back around
		*/
		if (lastx >= slices[i].ax){
			ffr = 1;
			fprintf(stdout,"</tr>\n<TR>\n");
		}

		lastx = slices[i].ax;


		slicew = slices[i].bx -slices[i].ax;
		sliceh = slices[i].by -slices[i].ay;
		
		if (slices[i].link[0] != '\0') {
			fprintf(stdout,"<TD colspan=\"%d\"><a href=\"%s\"><img src=\"%s\" border=\"0\" width=\"%d\" height=\"%d\"></a></td>\n",cols,slices[i].link,foutname,slicew,sliceh);
		} else {
		fprintf(stdout,"<TD colspan=\"%d\"><img src=\"%s\" border=\"0\" width=\"%d\" height=\"%d\"></td>\n",cols,foutname,slicew, sliceh);
		}

		cut(slices[i].ax, slices[i].ay, slices[i].bx-1, slices[i].by);

		write_JPEG_file(foutname,quality);

		ffr = 0;
	}

	fprintf(stdout,"</tr></table>");
			


	return 0;
	}
	
	
/*
 * main
 *
 * The basic procedure is as follows...
 *
 * read in the input jpeg,
 * x1,y1 -> x2, y2 coord's 
 * output file name, and 
 * the quality factor
 *
 * read in the required file
 * copy the required "slice" into a new buffer
 * write the new buffer to the new file name
 *
 */
int main( int argc, char **argv ) {

	int quality;
	char *infile, *outfile, *scriptfile;
	
	
	if (argc != 5) {
	
		fprintf(stderr,"%s v%s usage : <input jpeg> <output jpeg prefix> <cut list file> <output quality>\n\n",argv[0],_VERSION);
		
		exit(1);
		}
	
	infile = argv[1];
	outfile = argv[2];
	scriptfile = argv[3];
	quality = atoi(argv[4]);
	
	/* 
	 * Check for errors in the input... 
	 *
	 */
	
	if ((quality > 100)||(quality < 1)) {
		fprintf(stderr,"Invalid JPEG quality setting, please choose a value between 1 and 100\n");
		exit(1);
		}
	 
	 
	/* Now, let us read the input JPEG file to the buffer */
	read_JPEG_file(infile);
	
	/* Test to see if this picture isn't too large for our jslice program
	 */
	if ((image_width >= MAX_WIDTH) || (image_height >= MAX_HEIGHT)){
		fprintf(stderr,"Sorry, but jslice has been compiled only to handle pictures up to 2048x2048 pixels\n");
		exit(1);
		}
		
	fprintf(stdout,"<HTML>\n<BODY bgcolor=#ffffff text=#000000>\n");

	script_cut(scriptfile, outfile, quality);
	fprintf(stdout,"</body></html>\n");
	return 0;
	}
