/* * jslice.c * * This program will take a slice of a given picture and save it to * a second file name * */ /* * CHANGES * * 31/05/2000 - Added JavaScript enhancements and direct embedding * 21/05/2000 - Added image-dimentions * 25/05/2000 - Fixed up hyperlink detection from slice file * */ #include #include #include #include /* * 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 JSAMPROW * arow; /* points to a row of JSAMPLES*/ JSAMPLE * buffer; /* Points to large array of R,G,B-order data */ JSAMPLE * image; JSAMPROW wimage[4097]; /* array of rows for the "whole" image */ JSAMPROW pimage[4097]; /* array of rows for the "partial" image */ #define _VERSION "1.0.0" #define _MESSAGE "\n\n" #define _HELP "jslice v1.0.0 - (C) Paul L Daniels 2000\n\n\ Options Available -\n\ -i Input JPEG file [ie frontPage.jpg]\n\ -p Ouput JPEG file prefix [ie fpactive]\n\ -c Cut/Slice file [ie frontpage.slices]\n\ -q Quality of JPEG output [ie 85]\n\ -h OPTIONAL - Header file to prefix to HTML output [ie header.html]\n\ -f OPTIONAL - Footer file to suffix to HTML output [ie footer.html]\n\ --help produce this help.\n\ \n\ example of using jslice...\n\ jslice -i frontpage_active.jpg -p fpa -c frontpage.slices -q 75 -h header.html -f footer.html > index.html\n\n\ end of help.\n\n" #define MAX_HEIGHT 4096 #define MAX_WIDTH 4096 #define MAX_LINK_SIZE 1024 #define MAX_SLICES 1000 #define _CUT 1 #define _UNCUT 0 #define _QUALITY_DEFAULT 75 #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[MAX_LINK_SIZE+1]; }; typedef struct _aslice _tslice; int slicesx[MAX_WIDTH]; int slicesy[MAX_HEIGHT]; /* And of course, create a global variable out of it. */ _tslice slices[MAX_SLICES]; 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 every pixel across the horizontal...*/ for (cx = sx; cx < fx; cx++){ /* if we've found a cut mark...*/ if (slicesx[cx] != _UNCUT) { /* increase our cut count */ cuts++; /* if the next pixel is also cut, but from the same row * then we must skip it, as it's just consecutive cutouts */ if (slicesx[cx+1] == slicesx[cx]) { cx++; } } /* if slices != UNCUT */ } /* for */ 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; /* get our dimentions of the image, * remember, that a image has a MINIMUM size of 1x1 * NOT 0x0 */ p_width = bx - ax +1; p_height = by - ay +1; /* for every row... */ for (cy = 0; cy < p_height; cy++) { /* allocate the memory for the new row of the image */ pimage[cy] = malloc(sizeof(JSAMPLE) *(p_width*3)); /* for every pixel */ for (cx = 0; cx < (p_width*3); cx++) { /* copy across the pixels */ pimage[cy][cx] = wimage[cy+ay][cx+(ax*3)]; } /* for cx */ } /* for cy */ return 0; } /* * dump_slices * * Debugging and information routine */ int dump_slices( void ) { int i; fprintf(stdout,"\n\n"); return 0; } int script_cut( char *script_name, char *foutprefix, int quality ){ #define MAX 1023 FILE *script; int count; int ax; int i; 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 < MAX_WIDTH; 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)){ /* get the coordinates... */ 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; } /* get our link... */ link = strtok(NULL,"\n\r"); /* * 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 * * 0.1.3 - We mark in the top-y value in the ax and bx here * so that we can determin if we are counting one or two * columns when we come to counting up the number of cuts * between a given ax -> bx * */ slicesx[slices[count].ax] = slices[count].ay +_CUT; slicesx[slices[count].bx] = slices[count].ay +_CUT; slicesy[slices[count].ay] = _CUT; slicesy[slices[count].by] = _CUT; count++; } } /* * Let the HTML Table start flowing * */ fprintf(stdout,"\n"); /* * Print out our SLICING SPACERS! * */ slicewidth = tally = 1; for ( ax = 1; ax <= image_width; ax++ ){ if (slicesx[ax] != _UNCUT) { if (slicesx[ax-1] != slicesx[ax]){ fprintf(stdout,"",slicewidth); tally += slicewidth; slicewidth=0; } } slicewidth++; } fprintf(stdout,"\n\n",tally); fprintf(stdout,"\n\n"); /* print out commentory information about the slices * to the HTML file */ dump_slices(); lastx = -1; ffr = 1; tally = 0; for ( i = 0; i < count; i++ ){ /* create our filename of the output slice */ sprintf(foutname,"%s%d.jpg",foutprefix,i+1); /* determine how many columns we have for this slice */ 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,"\n\n"); } lastx = slices[i].ax; slicew = slices[i].bx -slices[i].ax +1; sliceh = slices[i].by -slices[i].ay +1; if (slices[i].link[0] != '\0') { fprintf(stdout,"\n",cols,slices[i].link,foutname,slicew,sliceh); } else { fprintf(stdout,"\n",cols,foutname,slicew, sliceh); } cut(slices[i].ax, slices[i].ay, slices[i].bx, slices[i].by); write_JPEG_file(foutname,quality); ffr = 0; } fprintf(stdout,"\n\n",tally); fprintf(stdout,"
"); return 0; } /* * filetostdout * * prints a given file to stdout * */ int filetostdout( char *fname ){ FILE *f = fopen(fname,"r"); char achar; /* if the file opened okay... */ if (f) { while ( (achar = fgetc(f))!= EOF) fprintf(stdout,"%c",achar); fclose(f); } else { fprintf(stderr,"Error: could not open file %s\n",fname); } 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; int aindex; /* argv index */ char *infile=NULL, *outfile=NULL, *scriptfile=NULL, *headerfile=NULL, *footerfile=NULL; /* if (argc < 9) { fprintf(stderr,"jslice v%s usage :\n %s -i -p \ -c -q \ [-h
] [-f