You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1905 lines
						
					
					
						
							72 KiB
						
					
					
				
			
		
		
	
	
							1905 lines
						
					
					
						
							72 KiB
						
					
					
				| # --- T2-COPYRIGHT-NOTE-BEGIN --- | |
| # This copyright note is auto-generated by ./scripts/Create-CopyPatch. | |
| #  | |
| # T2 SDE: package/.../libjpeg/crop.patch | |
| # Copyright (C) 2004 - 2006 The T2 SDE Project | |
| #  | |
| # More information can be found in the files COPYING and README. | |
| #  | |
| # This patch file is dual-licensed. It is available under the license the | |
| # patched project is licensed under, as long as it is an OpenSource license | |
| # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms | |
| # of the GNU General Public License as published by the Free Software | |
| # Foundation; either version 2 of the License, or (at your option) any later | |
| # version. | |
| # --- T2-COPYRIGHT-NOTE-END --- | |
|  | |
| Cropping feature from http://sylvana.net/jpegcrop/jpegtran/ - which was | |
| released as tarball and not patch ... | |
|  | |
|   - Rene Rebe <[email protected]> | |
|  | |
| diff -u jpeg-6b/jerror.h jpeg-6b-new/jerror.h | |
| --- jpeg-6b/jerror.h	1997-10-18 20:59:10.000000000 +0200 | |
| +++ jpeg-6b-new/jerror.h	2000-03-05 23:34:27.000000000 +0100 | |
| @@ -45,6 +45,7 @@ | |
|  JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") | |
|  JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") | |
|  JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") | |
| +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") | |
|  JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") | |
|  JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") | |
|  JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") | |
| diff -u jpeg-6b/jpegtran.c jpeg-6b-new/jpegtran.c | |
| --- jpeg-6b/jpegtran.c	1997-07-24 04:37:26.000000000 +0200 | |
| +++ jpeg-6b-new/jpegtran.c	2003-09-21 21:30:21.000000000 +0200 | |
| @@ -1,7 +1,7 @@ | |
|  /* | |
|   * jpegtran.c | |
|   * | |
| - * Copyright (C) 1995-1997, Thomas G. Lane. | |
| + * Copyright (C) 1995-2001, Thomas G. Lane. | |
|   * This file is part of the Independent JPEG Group's software. | |
|   * For conditions of distribution and use, see the accompanying README file. | |
|   * | |
| @@ -64,8 +64,10 @@ | |
|  #endif | |
|  #if TRANSFORMS_SUPPORTED | |
|    fprintf(stderr, "Switches for modifying the image:\n"); | |
| +  fprintf(stderr, "  -crop WxH+X+Y  Crop to a rectangular subarea\n"); | |
|    fprintf(stderr, "  -grayscale     Reduce to grayscale (omit color data)\n"); | |
|    fprintf(stderr, "  -flip [horizontal|vertical]  Mirror image (left-right or top-bottom)\n"); | |
| +  fprintf(stderr, "  -perfect       Fail if there is non-transformable edge blocks\n"); | |
|    fprintf(stderr, "  -rotate [90|180|270]         Rotate image (degrees clockwise)\n"); | |
|    fprintf(stderr, "  -transpose     Transpose image\n"); | |
|    fprintf(stderr, "  -transverse    Transverse transpose image\n"); | |
| @@ -133,7 +135,9 @@ | |
|    copyoption = JCOPYOPT_DEFAULT; | |
|    transformoption.transform = JXFORM_NONE; | |
|    transformoption.trim = FALSE; | |
| +  transformoption.perfect = FALSE; | |
|    transformoption.force_grayscale = FALSE; | |
| +  transformoption.crop = FALSE; | |
|    cinfo->err->trace_level = 0; | |
|   | |
|    /* Scan command line options, adjust parameters */ | |
| @@ -160,7 +164,7 @@ | |
|        exit(EXIT_FAILURE); | |
|  #endif | |
|   | |
| -    } else if (keymatch(arg, "copy", 1)) { | |
| +    } else if (keymatch(arg, "copy", 2)) { | |
|        /* Select which extra markers to copy. */ | |
|        if (++argn >= argc)	/* advance to next argument */ | |
|  	usage(); | |
| @@ -173,6 +177,20 @@ | |
|        } else | |
|  	usage(); | |
|   | |
| +    } else if (keymatch(arg, "crop", 2)) { | |
| +      /* Perform lossless cropping. */ | |
| +#if TRANSFORMS_SUPPORTED | |
| +      if (++argn >= argc)	/* advance to next argument */ | |
| +	usage(); | |
| +      if (! jtransform_parse_crop_spec(&transformoption, argv[argn])) { | |
| +	fprintf(stderr, "%s: bogus -crop argument '%s'\n", | |
| +		progname, argv[argn]); | |
| +	exit(EXIT_FAILURE); | |
| +      } | |
| +#else | |
| +      select_transform(JXFORM_NONE);	/* force an error */ | |
| +#endif | |
| + | |
|      } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { | |
|        /* Enable debug printouts. */ | |
|        /* On first -d, print version identification */ | |
| @@ -233,7 +251,12 @@ | |
|  	usage(); | |
|        outfilename = argv[argn];	/* save it away for later use */ | |
|   | |
| -    } else if (keymatch(arg, "progressive", 1)) { | |
| +    } else if (keymatch(arg, "perfect", 2)) { | |
| +      /* Fail if there is any partial edge MCUs that the transform can't | |
| +       * handle. */ | |
| +      transformoption.perfect = TRUE; | |
| + | |
| +    } else if (keymatch(arg, "progressive", 2)) { | |
|        /* Select simple progressive mode. */ | |
|  #ifdef C_PROGRESSIVE_SUPPORTED | |
|        simple_progressive = TRUE; | |
| @@ -342,8 +365,10 @@ | |
|    jvirt_barray_ptr * src_coef_arrays; | |
|    jvirt_barray_ptr * dst_coef_arrays; | |
|    int file_index; | |
| -  FILE * input_file; | |
| -  FILE * output_file; | |
| +  /* We assume all-in-memory processing and can therefore use only a | |
| +   * single file pointer for sequential input and output operation.  | |
| +   */ | |
| +  FILE * fp; | |
|   | |
|    /* On Mac, fetch a command line. */ | |
|  #ifdef USE_CCOMMAND | |
| @@ -406,24 +431,13 @@ | |
|   | |
|    /* Open the input file. */ | |
|    if (file_index < argc) { | |
| -    if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { | |
| -      fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); | |
| +    if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) { | |
| +      fprintf(stderr, "%s: can't open %s for reading\n", progname, argv[file_index]); | |
|        exit(EXIT_FAILURE); | |
|      } | |
|    } else { | |
|      /* default input file is stdin */ | |
| -    input_file = read_stdin(); | |
| -  } | |
| - | |
| -  /* Open the output file. */ | |
| -  if (outfilename != NULL) { | |
| -    if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { | |
| -      fprintf(stderr, "%s: can't open %s\n", progname, outfilename); | |
| -      exit(EXIT_FAILURE); | |
| -    } | |
| -  } else { | |
| -    /* default output file is stdout */ | |
| -    output_file = write_stdout(); | |
| +    fp = read_stdin(); | |
|    } | |
|   | |
|  #ifdef PROGRESS_REPORT | |
| @@ -431,7 +445,7 @@ | |
|  #endif | |
|   | |
|    /* Specify data source for decompression */ | |
| -  jpeg_stdio_src(&srcinfo, input_file); | |
| +  jpeg_stdio_src(&srcinfo, fp); | |
|   | |
|    /* Enable saving of extra markers that we want to copy */ | |
|    jcopy_markers_setup(&srcinfo, copyoption); | |
| @@ -443,6 +457,15 @@ | |
|     * jpeg_read_coefficients so that memory allocation will be done right. | |
|     */ | |
|  #if TRANSFORMS_SUPPORTED | |
| +  /* Fails right away if -perfect is given and transformation is not perfect. | |
| +   */ | |
| +  if (transformoption.perfect && | |
| +      !jtransform_perfect_transform(srcinfo.image_width, srcinfo.image_height, | |
| +      srcinfo.max_h_samp_factor * DCTSIZE, srcinfo.max_v_samp_factor * DCTSIZE, | |
| +      transformoption.transform)) { | |
| +    fprintf(stderr, "%s: transformation is not perfect\n", progname); | |
| +    exit(EXIT_FAILURE); | |
| +  } | |
|    jtransform_request_workspace(&srcinfo, &transformoption); | |
|  #endif | |
|   | |
| @@ -463,11 +486,32 @@ | |
|    dst_coef_arrays = src_coef_arrays; | |
|  #endif | |
|   | |
| +  /* Close input file, if we opened it. | |
| +   * Note: we assume that jpeg_read_coefficients consumed all input | |
| +   * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will | |
| +   * only consume more while (! cinfo->inputctl->eoi_reached). | |
| +   * We cannot call jpeg_finish_decompress here since we still need the | |
| +   * virtual arrays allocated from the source object for processing. | |
| +   */ | |
| +  if (fp != stdin) | |
| +    fclose(fp); | |
| + | |
| +  /* Open the output file. */ | |
| +  if (outfilename != NULL) { | |
| +    if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) { | |
| +      fprintf(stderr, "%s: can't open %s for writing\n", progname, outfilename); | |
| +      exit(EXIT_FAILURE); | |
| +    } | |
| +  } else { | |
| +    /* default output file is stdout */ | |
| +    fp = write_stdout(); | |
| +  } | |
| + | |
|    /* Adjust default compression parameters by re-parsing the options */ | |
|    file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); | |
|   | |
|    /* Specify data destination for compression */ | |
| -  jpeg_stdio_dest(&dstinfo, output_file); | |
| +  jpeg_stdio_dest(&dstinfo, fp); | |
|   | |
|    /* Start compressor (note no image data is actually written here) */ | |
|    jpeg_write_coefficients(&dstinfo, dst_coef_arrays); | |
| @@ -488,11 +532,9 @@ | |
|    (void) jpeg_finish_decompress(&srcinfo); | |
|    jpeg_destroy_decompress(&srcinfo); | |
|   | |
| -  /* Close files, if we opened them */ | |
| -  if (input_file != stdin) | |
| -    fclose(input_file); | |
| -  if (output_file != stdout) | |
| -    fclose(output_file); | |
| +  /* Close output file, if we opened it */ | |
| +  if (fp != stdout) | |
| +    fclose(fp); | |
|   | |
|  #ifdef PROGRESS_REPORT | |
|    end_progress_monitor((j_common_ptr) &dstinfo); | |
| diff -u jpeg-6b/transupp.c jpeg-6b-new/transupp.c | |
| --- jpeg-6b/transupp.c	1997-08-10 02:15:26.000000000 +0200 | |
| +++ jpeg-6b-new/transupp.c	2003-09-21 22:50:33.000000000 +0200 | |
| @@ -1,7 +1,7 @@ | |
|  /* | |
|   * transupp.c | |
|   * | |
| - * Copyright (C) 1997, Thomas G. Lane. | |
| + * Copyright (C) 1997-2001, Thomas G. Lane. | |
|   * This file is part of the Independent JPEG Group's software. | |
|   * For conditions of distribution and use, see the accompanying README file. | |
|   * | |
| @@ -20,6 +20,7 @@ | |
|  #include "jinclude.h" | |
|  #include "jpeglib.h" | |
|  #include "transupp.h"		/* My own external interface */ | |
| +#include <ctype.h>		/* to declare isdigit() */ | |
|   | |
|   | |
|  #if TRANSFORMS_SUPPORTED | |
| @@ -28,7 +29,8 @@ | |
|   * Lossless image transformation routines.  These routines work on DCT | |
|   * coefficient arrays and thus do not require any lossy decompression | |
|   * or recompression of the image. | |
| - * Thanks to Guido Vollbeding for the initial design and code of this feature. | |
| + * Thanks to Guido Vollbeding for the initial design and code of this feature, | |
| + * and to Ben Jackson for introducing the cropping feature. | |
|   * | |
|   * Horizontal flipping is done in-place, using a single top-to-bottom | |
|   * pass through the virtual source array.  It will thus be much the | |
| @@ -42,6 +44,13 @@ | |
|   * arrays for most of the transforms.  That could result in much thrashing | |
|   * if the image is larger than main memory. | |
|   * | |
| + * If cropping or trimming is involved, the destination arrays may be smaller | |
| + * than the source arrays.  Note it is not possible to do horizontal flip | |
| + * in-place when a nonzero Y crop offset is specified, since we'd have to move | |
| + * data from one block row to another but the virtual array manager doesn't | |
| + * guarantee we can touch more than one row at a time.  So in that case, | |
| + * we have to use a separate destination array. | |
| + * | |
|   * Some notes about the operating environment of the individual transform | |
|   * routines: | |
|   * 1. Both the source and destination virtual arrays are allocated from the | |
| @@ -54,20 +63,65 @@ | |
|   *    and we may as well take that as the effective iMCU size. | |
|   * 4. When "trim" is in effect, the destination's dimensions will be the | |
|   *    trimmed values but the source's will be untrimmed. | |
| - * 5. All the routines assume that the source and destination buffers are | |
| + * 5. When "crop" is in effect, the destination's dimensions will be the | |
| + *    cropped values but the source's will be uncropped.  Each transform | |
| + *    routine is responsible for picking up source data starting at the | |
| + *    correct X and Y offset for the crop region.  (The X and Y offsets | |
| + *    passed to the transform routines are measured in iMCU blocks of the | |
| + *    destination.) | |
| + * 6. All the routines assume that the source and destination buffers are | |
|   *    padded out to a full iMCU boundary.  This is true, although for the | |
|   *    source buffer it is an undocumented property of jdcoefct.c. | |
| - * Notes 2,3,4 boil down to this: generally we should use the destination's | |
| - * dimensions and ignore the source's. | |
|   */ | |
|   | |
|   | |
|  LOCAL(void) | |
| -do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| -	   jvirt_barray_ptr *src_coef_arrays) | |
| -/* Horizontal flip; done in-place, so no separate dest array is required */ | |
| +do_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	 JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
| +	 jvirt_barray_ptr *src_coef_arrays, | |
| +	 jvirt_barray_ptr *dst_coef_arrays) | |
| +/* Crop.  This is only used when no rotate/flip is requested with the crop. */ | |
| +{ | |
| +  JDIMENSION dst_blk_y, x_crop_blocks, y_crop_blocks; | |
| +  int ci, offset_y; | |
| +  JBLOCKARRAY src_buffer, dst_buffer; | |
| +  jpeg_component_info *compptr; | |
| + | |
| +  /* We simply have to copy the right amount of data (the destination's | |
| +   * image size) starting at the given X and Y offsets in the source. | |
| +   */ | |
| +  for (ci = 0; ci < dstinfo->num_components; ci++) { | |
| +    compptr = dstinfo->comp_info + ci; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
| +    for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
| +	 dst_blk_y += compptr->v_samp_factor) { | |
| +      dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, | |
| +	 (JDIMENSION) compptr->v_samp_factor, TRUE); | |
| +      src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	 dst_blk_y + y_crop_blocks, | |
| +	 (JDIMENSION) compptr->v_samp_factor, FALSE); | |
| +      for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
| +	jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, | |
| +			dst_buffer[offset_y], | |
| +			compptr->width_in_blocks); | |
| +      } | |
| +    } | |
| +  } | |
| +} | |
| + | |
| + | |
| +LOCAL(void) | |
| +do_flip_h_no_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +		   JDIMENSION x_crop_offset, | |
| +		   jvirt_barray_ptr *src_coef_arrays) | |
| +/* Horizontal flip; done in-place, so no separate dest array is required. | |
| + * NB: this only works when y_crop_offset is zero. | |
| + */ | |
|  { | |
| -  JDIMENSION MCU_cols, comp_width, blk_x, blk_y; | |
| +  JDIMENSION MCU_cols, comp_width, blk_x, blk_y, x_crop_blocks; | |
|    int ci, k, offset_y; | |
|    JBLOCKARRAY buffer; | |
|    JCOEFPTR ptr1, ptr2; | |
| @@ -79,17 +133,19 @@ | |
|     * mirroring by changing the signs of odd-numbered columns. | |
|     * Partial iMCUs at the right edge are left untouched. | |
|     */ | |
| -  MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| +  MCU_cols = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_width = MCU_cols * compptr->h_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
|      for (blk_y = 0; blk_y < compptr->height_in_blocks; | |
|  	 blk_y += compptr->v_samp_factor) { | |
|        buffer = (*srcinfo->mem->access_virt_barray) | |
|  	((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, | |
|  	 (JDIMENSION) compptr->v_samp_factor, TRUE); | |
|        for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
| +	/* Do the mirroring */ | |
|  	for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { | |
|  	  ptr1 = buffer[offset_y][blk_x]; | |
|  	  ptr2 = buffer[offset_y][comp_width - blk_x - 1]; | |
| @@ -105,6 +161,79 @@ | |
|  	    *ptr2++ = -temp1; | |
|  	  } | |
|  	} | |
| +	if (x_crop_blocks > 0) { | |
| +	  /* Now left-justify the portion of the data to be kept. | |
| +	   * We can't use a single jcopy_block_row() call because that routine | |
| +	   * depends on memcpy(), whose behavior is unspecified for overlapping | |
| +	   * source and destination areas.  Sigh. | |
| +	   */ | |
| +	  for (blk_x = 0; blk_x < compptr->width_in_blocks; blk_x++) { | |
| +	    jcopy_block_row(buffer[offset_y] + blk_x + x_crop_blocks, | |
| +			    buffer[offset_y] + blk_x, | |
| +			    (JDIMENSION) 1); | |
| +	  } | |
| +	} | |
| +      } | |
| +    } | |
| +  } | |
| +} | |
| + | |
| + | |
| +LOCAL(void) | |
| +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	   JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
| +	   jvirt_barray_ptr *src_coef_arrays, | |
| +	   jvirt_barray_ptr *dst_coef_arrays) | |
| +/* Horizontal flip in general cropping case */ | |
| +{ | |
| +  JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
| +  int ci, k, offset_y; | |
| +  JBLOCKARRAY src_buffer, dst_buffer; | |
| +  JBLOCKROW src_row_ptr, dst_row_ptr; | |
| +  JCOEFPTR src_ptr, dst_ptr; | |
| +  jpeg_component_info *compptr; | |
| + | |
| +  /* Here we must output into a separate array because we can't touch | |
| +   * different rows of a single virtual array simultaneously.  Otherwise, | |
| +   * this is essentially the same as the routine above. | |
| +   */ | |
| +  MCU_cols = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| + | |
| +  for (ci = 0; ci < dstinfo->num_components; ci++) { | |
| +    compptr = dstinfo->comp_info + ci; | |
| +    comp_width = MCU_cols * compptr->h_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
| +    for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
| +	 dst_blk_y += compptr->v_samp_factor) { | |
| +      dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, | |
| +	 (JDIMENSION) compptr->v_samp_factor, TRUE); | |
| +      src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	 dst_blk_y + y_crop_blocks, | |
| +	 (JDIMENSION) compptr->v_samp_factor, FALSE); | |
| +      for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
| +	dst_row_ptr = dst_buffer[offset_y]; | |
| +	src_row_ptr = src_buffer[offset_y]; | |
| +	for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { | |
| +	  if (x_crop_blocks + dst_blk_x < comp_width) { | |
| +	    /* Do the mirrorable blocks */ | |
| +	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| +	    src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; | |
| +	    /* this unrolled loop doesn't need to know which row it's on... */ | |
| +	    for (k = 0; k < DCTSIZE2; k += 2) { | |
| +	      *dst_ptr++ = *src_ptr++;	 /* copy even column */ | |
| +	      *dst_ptr++ = - *src_ptr++; /* copy odd column with sign change */ | |
| +	    } | |
| +	  } else { | |
| +	    /* Copy last partial block(s) verbatim */ | |
| +	    jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, | |
| +			    dst_row_ptr + dst_blk_x, | |
| +			    (JDIMENSION) 1); | |
| +	  } | |
| +	} | |
|        } | |
|      } | |
|    } | |
| @@ -113,11 +242,13 @@ | |
|   | |
|  LOCAL(void) | |
|  do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	   JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	   jvirt_barray_ptr *src_coef_arrays, | |
|  	   jvirt_barray_ptr *dst_coef_arrays) | |
|  /* Vertical flip */ | |
|  { | |
|    JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JBLOCKROW src_row_ptr, dst_row_ptr; | |
| @@ -131,33 +262,38 @@ | |
|     * of odd-numbered rows. | |
|     * Partial iMCUs at the bottom edge are copied verbatim. | |
|     */ | |
| -  MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
| +  MCU_rows = srcinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_height = MCU_rows * compptr->v_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
|  	((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, | |
|  	 (JDIMENSION) compptr->v_samp_factor, TRUE); | |
| -      if (dst_blk_y < comp_height) { | |
| +      if (y_crop_blocks + dst_blk_y < comp_height) { | |
|  	/* Row is within the mirrorable area. */ | |
|  	src_buffer = (*srcinfo->mem->access_virt_barray) | |
|  	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| -	   comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, | |
| +	   comp_height - y_crop_blocks - dst_blk_y - | |
| +	   (JDIMENSION) compptr->v_samp_factor, | |
|  	   (JDIMENSION) compptr->v_samp_factor, FALSE); | |
|        } else { | |
|  	/* Bottom-edge blocks will be copied verbatim. */ | |
|  	src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, | |
| +	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	   dst_blk_y + y_crop_blocks, | |
|  	   (JDIMENSION) compptr->v_samp_factor, FALSE); | |
|        } | |
|        for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
| -	if (dst_blk_y < comp_height) { | |
| +	if (y_crop_blocks + dst_blk_y < comp_height) { | |
|  	  /* Row is within the mirrorable area. */ | |
|  	  dst_row_ptr = dst_buffer[offset_y]; | |
|  	  src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; | |
| +	  src_row_ptr += x_crop_blocks; | |
|  	  for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; | |
|  	       dst_blk_x++) { | |
|  	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| @@ -173,7 +309,8 @@ | |
|  	  } | |
|  	} else { | |
|  	  /* Just copy row verbatim. */ | |
| -	  jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], | |
| +	  jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, | |
| +			  dst_buffer[offset_y], | |
|  			  compptr->width_in_blocks); | |
|  	} | |
|        } | |
| @@ -184,11 +321,12 @@ | |
|   | |
|  LOCAL(void) | |
|  do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	      JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	      jvirt_barray_ptr *src_coef_arrays, | |
|  	      jvirt_barray_ptr *dst_coef_arrays) | |
|  /* Transpose source into destination */ | |
|  { | |
| -  JDIMENSION dst_blk_x, dst_blk_y; | |
| +  JDIMENSION dst_blk_x, dst_blk_y, x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_x, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JCOEFPTR src_ptr, dst_ptr; | |
| @@ -201,6 +339,8 @@ | |
|     */ | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| @@ -210,11 +350,12 @@ | |
|  	for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; | |
|  	     dst_blk_x += compptr->h_samp_factor) { | |
|  	  src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, | |
| +	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	     dst_blk_x + x_crop_blocks, | |
|  	     (JDIMENSION) compptr->h_samp_factor, FALSE); | |
|  	  for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { | |
| -	    src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; | |
|  	    dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +	    src_ptr = src_buffer[offset_x][dst_blk_y + offset_y + y_crop_blocks]; | |
|  	    for (i = 0; i < DCTSIZE; i++) | |
|  	      for (j = 0; j < DCTSIZE; j++) | |
|  		dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -228,6 +369,7 @@ | |
|   | |
|  LOCAL(void) | |
|  do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	   JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	   jvirt_barray_ptr *src_coef_arrays, | |
|  	   jvirt_barray_ptr *dst_coef_arrays) | |
|  /* 90 degree rotation is equivalent to | |
| @@ -237,6 +379,7 @@ | |
|   */ | |
|  { | |
|    JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_x, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JCOEFPTR src_ptr, dst_ptr; | |
| @@ -246,11 +389,13 @@ | |
|     * at the (output) right edge properly.  They just get transposed and | |
|     * not mirrored. | |
|     */ | |
| -  MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| +  MCU_cols = srcinfo->image_height / (dstinfo->max_h_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_width = MCU_cols * compptr->h_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| @@ -259,15 +404,26 @@ | |
|        for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
|  	for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; | |
|  	     dst_blk_x += compptr->h_samp_factor) { | |
| -	  src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, | |
| -	     (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  if (x_crop_blocks + dst_blk_x < comp_width) { | |
| +	    /* Block is within the mirrorable area. */ | |
| +	    src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	      ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	       comp_width - x_crop_blocks - dst_blk_x - | |
| +	       (JDIMENSION) compptr->h_samp_factor, | |
| +	       (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  } else { | |
| +	    /* Edge blocks are transposed but not mirrored. */ | |
| +	    src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	      ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	       dst_blk_x + x_crop_blocks, | |
| +	       (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  } | |
|  	  for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { | |
| -	    src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; | |
| -	    if (dst_blk_x < comp_width) { | |
| +	    dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +	    if (x_crop_blocks + dst_blk_x < comp_width) { | |
|  	      /* Block is within the mirrorable area. */ | |
| -	      dst_ptr = dst_buffer[offset_y] | |
| -		[comp_width - dst_blk_x - offset_x - 1]; | |
| +	      src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] | |
| +		[dst_blk_y + offset_y + y_crop_blocks]; | |
|  	      for (i = 0; i < DCTSIZE; i++) { | |
|  		for (j = 0; j < DCTSIZE; j++) | |
|  		  dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -277,7 +433,8 @@ | |
|  	      } | |
|  	    } else { | |
|  	      /* Edge blocks are transposed but not mirrored. */ | |
| -	      dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +	      src_ptr = src_buffer[offset_x] | |
| +		[dst_blk_y + offset_y + y_crop_blocks]; | |
|  	      for (i = 0; i < DCTSIZE; i++) | |
|  		for (j = 0; j < DCTSIZE; j++) | |
|  		  dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -292,6 +449,7 @@ | |
|   | |
|  LOCAL(void) | |
|  do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	    JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	    jvirt_barray_ptr *src_coef_arrays, | |
|  	    jvirt_barray_ptr *dst_coef_arrays) | |
|  /* 270 degree rotation is equivalent to | |
| @@ -301,6 +459,7 @@ | |
|   */ | |
|  { | |
|    JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_x, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JCOEFPTR src_ptr, dst_ptr; | |
| @@ -310,11 +469,13 @@ | |
|     * at the (output) bottom edge properly.  They just get transposed and | |
|     * not mirrored. | |
|     */ | |
| -  MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
| +  MCU_rows = srcinfo->image_width / (dstinfo->max_v_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_height = MCU_rows * compptr->v_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| @@ -324,14 +485,15 @@ | |
|  	for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; | |
|  	     dst_blk_x += compptr->h_samp_factor) { | |
|  	  src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, | |
| +	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	     dst_blk_x + x_crop_blocks, | |
|  	     (JDIMENSION) compptr->h_samp_factor, FALSE); | |
|  	  for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { | |
|  	    dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| -	    if (dst_blk_y < comp_height) { | |
| +	    if (y_crop_blocks + dst_blk_y < comp_height) { | |
|  	      /* Block is within the mirrorable area. */ | |
|  	      src_ptr = src_buffer[offset_x] | |
| -		[comp_height - dst_blk_y - offset_y - 1]; | |
| +		[comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; | |
|  	      for (i = 0; i < DCTSIZE; i++) { | |
|  		for (j = 0; j < DCTSIZE; j++) { | |
|  		  dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -341,7 +503,8 @@ | |
|  	      } | |
|  	    } else { | |
|  	      /* Edge blocks are transposed but not mirrored. */ | |
| -	      src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; | |
| +	      src_ptr = src_buffer[offset_x] | |
| +		[dst_blk_y + offset_y + y_crop_blocks]; | |
|  	      for (i = 0; i < DCTSIZE; i++) | |
|  		for (j = 0; j < DCTSIZE; j++) | |
|  		  dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -356,6 +519,7 @@ | |
|   | |
|  LOCAL(void) | |
|  do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	    JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	    jvirt_barray_ptr *src_coef_arrays, | |
|  	    jvirt_barray_ptr *dst_coef_arrays) | |
|  /* 180 degree rotation is equivalent to | |
| @@ -365,89 +529,93 @@ | |
|   */ | |
|  { | |
|    JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JBLOCKROW src_row_ptr, dst_row_ptr; | |
|    JCOEFPTR src_ptr, dst_ptr; | |
|    jpeg_component_info *compptr; | |
|   | |
| -  MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| -  MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
| +  MCU_cols = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| +  MCU_rows = srcinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_width = MCU_cols * compptr->h_samp_factor; | |
|      comp_height = MCU_rows * compptr->v_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
|  	((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, | |
|  	 (JDIMENSION) compptr->v_samp_factor, TRUE); | |
| -      if (dst_blk_y < comp_height) { | |
| +      if (y_crop_blocks + dst_blk_y < comp_height) { | |
|  	/* Row is within the vertically mirrorable area. */ | |
|  	src_buffer = (*srcinfo->mem->access_virt_barray) | |
|  	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| -	   comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, | |
| +	   comp_height - y_crop_blocks - dst_blk_y - | |
| +	   (JDIMENSION) compptr->v_samp_factor, | |
|  	   (JDIMENSION) compptr->v_samp_factor, FALSE); | |
|        } else { | |
|  	/* Bottom-edge rows are only mirrored horizontally. */ | |
|  	src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, | |
| +	  ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	   dst_blk_y + y_crop_blocks, | |
|  	   (JDIMENSION) compptr->v_samp_factor, FALSE); | |
|        } | |
|        for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
| -	if (dst_blk_y < comp_height) { | |
| +	dst_row_ptr = dst_buffer[offset_y]; | |
| +	if (y_crop_blocks + dst_blk_y < comp_height) { | |
|  	  /* Row is within the mirrorable area. */ | |
| -	  dst_row_ptr = dst_buffer[offset_y]; | |
|  	  src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; | |
| -	  /* Process the blocks that can be mirrored both ways. */ | |
| -	  for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { | |
| +	  for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { | |
|  	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| -	    src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; | |
| -	    for (i = 0; i < DCTSIZE; i += 2) { | |
| -	      /* For even row, negate every odd column. */ | |
| -	      for (j = 0; j < DCTSIZE; j += 2) { | |
| -		*dst_ptr++ = *src_ptr++; | |
| -		*dst_ptr++ = - *src_ptr++; | |
| +	    if (x_crop_blocks + dst_blk_x < comp_width) { | |
| +	      /* Process the blocks that can be mirrored both ways. */ | |
| +	      src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; | |
| +	      for (i = 0; i < DCTSIZE; i += 2) { | |
| +		/* For even row, negate every odd column. */ | |
| +		for (j = 0; j < DCTSIZE; j += 2) { | |
| +		  *dst_ptr++ = *src_ptr++; | |
| +		  *dst_ptr++ = - *src_ptr++; | |
| +		} | |
| +		/* For odd row, negate every even column. */ | |
| +		for (j = 0; j < DCTSIZE; j += 2) { | |
| +		  *dst_ptr++ = - *src_ptr++; | |
| +		  *dst_ptr++ = *src_ptr++; | |
| +		} | |
|  	      } | |
| -	      /* For odd row, negate every even column. */ | |
| -	      for (j = 0; j < DCTSIZE; j += 2) { | |
| -		*dst_ptr++ = - *src_ptr++; | |
| -		*dst_ptr++ = *src_ptr++; | |
| +	    } else { | |
| +	      /* Any remaining right-edge blocks are only mirrored vertically. */ | |
| +	      src_ptr = src_row_ptr[x_crop_blocks + dst_blk_x]; | |
| +	      for (i = 0; i < DCTSIZE; i += 2) { | |
| +		for (j = 0; j < DCTSIZE; j++) | |
| +		  *dst_ptr++ = *src_ptr++; | |
| +		for (j = 0; j < DCTSIZE; j++) | |
| +		  *dst_ptr++ = - *src_ptr++; | |
|  	      } | |
|  	    } | |
|  	  } | |
| -	  /* Any remaining right-edge blocks are only mirrored vertically. */ | |
| -	  for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { | |
| -	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| -	    src_ptr = src_row_ptr[dst_blk_x]; | |
| -	    for (i = 0; i < DCTSIZE; i += 2) { | |
| -	      for (j = 0; j < DCTSIZE; j++) | |
| -		*dst_ptr++ = *src_ptr++; | |
| -	      for (j = 0; j < DCTSIZE; j++) | |
| -		*dst_ptr++ = - *src_ptr++; | |
| -	    } | |
| -	  } | |
|  	} else { | |
|  	  /* Remaining rows are just mirrored horizontally. */ | |
| -	  dst_row_ptr = dst_buffer[offset_y]; | |
|  	  src_row_ptr = src_buffer[offset_y]; | |
| -	  /* Process the blocks that can be mirrored. */ | |
| -	  for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { | |
| -	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| -	    src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; | |
| -	    for (i = 0; i < DCTSIZE2; i += 2) { | |
| -	      *dst_ptr++ = *src_ptr++; | |
| -	      *dst_ptr++ = - *src_ptr++; | |
| +	  for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { | |
| +	    if (x_crop_blocks + dst_blk_x < comp_width) { | |
| +	      /* Process the blocks that can be mirrored. */ | |
| +	      dst_ptr = dst_row_ptr[dst_blk_x]; | |
| +	      src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; | |
| +	      for (i = 0; i < DCTSIZE2; i += 2) { | |
| +		*dst_ptr++ = *src_ptr++; | |
| +		*dst_ptr++ = - *src_ptr++; | |
| +	      } | |
| +	    } else { | |
| +	      /* Any remaining right-edge blocks are only copied. */ | |
| +	      jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, | |
| +			      dst_row_ptr + dst_blk_x, | |
| +			      (JDIMENSION) 1); | |
|  	    } | |
|  	  } | |
| -	  /* Any remaining right-edge blocks are only copied. */ | |
| -	  for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { | |
| -	    dst_ptr = dst_row_ptr[dst_blk_x]; | |
| -	    src_ptr = src_row_ptr[dst_blk_x]; | |
| -	    for (i = 0; i < DCTSIZE2; i++) | |
| -	      *dst_ptr++ = *src_ptr++; | |
| -	  } | |
|  	} | |
|        } | |
|      } | |
| @@ -457,6 +625,7 @@ | |
|   | |
|  LOCAL(void) | |
|  do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
| +	       JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, | |
|  	       jvirt_barray_ptr *src_coef_arrays, | |
|  	       jvirt_barray_ptr *dst_coef_arrays) | |
|  /* Transverse transpose is equivalent to | |
| @@ -470,18 +639,21 @@ | |
|   */ | |
|  { | |
|    JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; | |
| +  JDIMENSION x_crop_blocks, y_crop_blocks; | |
|    int ci, i, j, offset_x, offset_y; | |
|    JBLOCKARRAY src_buffer, dst_buffer; | |
|    JCOEFPTR src_ptr, dst_ptr; | |
|    jpeg_component_info *compptr; | |
|   | |
| -  MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| -  MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); | |
| +  MCU_cols = srcinfo->image_height / (dstinfo->max_h_samp_factor * DCTSIZE); | |
| +  MCU_rows = srcinfo->image_width / (dstinfo->max_v_samp_factor * DCTSIZE); | |
|   | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
|      comp_width = MCU_cols * compptr->h_samp_factor; | |
|      comp_height = MCU_rows * compptr->v_samp_factor; | |
| +    x_crop_blocks = x_crop_offset * compptr->h_samp_factor; | |
| +    y_crop_blocks = y_crop_offset * compptr->v_samp_factor; | |
|      for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; | |
|  	 dst_blk_y += compptr->v_samp_factor) { | |
|        dst_buffer = (*srcinfo->mem->access_virt_barray) | |
| @@ -490,17 +662,26 @@ | |
|        for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { | |
|  	for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; | |
|  	     dst_blk_x += compptr->h_samp_factor) { | |
| -	  src_buffer = (*srcinfo->mem->access_virt_barray) | |
| -	    ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, | |
| -	     (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  if (x_crop_blocks + dst_blk_x < comp_width) { | |
| +	    /* Block is within the mirrorable area. */ | |
| +	    src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	      ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	       comp_width - x_crop_blocks - dst_blk_x - | |
| +	       (JDIMENSION) compptr->h_samp_factor, | |
| +	       (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  } else { | |
| +	    src_buffer = (*srcinfo->mem->access_virt_barray) | |
| +	      ((j_common_ptr) srcinfo, src_coef_arrays[ci], | |
| +	       dst_blk_x + x_crop_blocks, | |
| +	       (JDIMENSION) compptr->h_samp_factor, FALSE); | |
| +	  } | |
|  	  for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { | |
| -	    if (dst_blk_y < comp_height) { | |
| -	      src_ptr = src_buffer[offset_x] | |
| -		[comp_height - dst_blk_y - offset_y - 1]; | |
| -	      if (dst_blk_x < comp_width) { | |
| +	    dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +	    if (y_crop_blocks + dst_blk_y < comp_height) { | |
| +	      if (x_crop_blocks + dst_blk_x < comp_width) { | |
|  		/* Block is within the mirrorable area. */ | |
| -		dst_ptr = dst_buffer[offset_y] | |
| -		  [comp_width - dst_blk_x - offset_x - 1]; | |
| +		src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] | |
| +		  [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; | |
|  		for (i = 0; i < DCTSIZE; i++) { | |
|  		  for (j = 0; j < DCTSIZE; j++) { | |
|  		    dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -516,7 +697,8 @@ | |
|  		} | |
|  	      } else { | |
|  		/* Right-edge blocks are mirrored in y only */ | |
| -		dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +		src_ptr = src_buffer[offset_x] | |
| +		  [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; | |
|  		for (i = 0; i < DCTSIZE; i++) { | |
|  		  for (j = 0; j < DCTSIZE; j++) { | |
|  		    dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -526,11 +708,10 @@ | |
|  		} | |
|  	      } | |
|  	    } else { | |
| -	      src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; | |
| -	      if (dst_blk_x < comp_width) { | |
| +	      if (x_crop_blocks + dst_blk_x < comp_width) { | |
|  		/* Bottom-edge blocks are mirrored in x only */ | |
| -		dst_ptr = dst_buffer[offset_y] | |
| -		  [comp_width - dst_blk_x - offset_x - 1]; | |
| +		src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] | |
| +		  [dst_blk_y + offset_y + y_crop_blocks]; | |
|  		for (i = 0; i < DCTSIZE; i++) { | |
|  		  for (j = 0; j < DCTSIZE; j++) | |
|  		    dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -540,7 +721,8 @@ | |
|  		} | |
|  	      } else { | |
|  		/* At lower right corner, just transpose, no mirroring */ | |
| -		dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; | |
| +		src_ptr = src_buffer[offset_x] | |
| +		  [dst_blk_y + offset_y + y_crop_blocks]; | |
|  		for (i = 0; i < DCTSIZE; i++) | |
|  		  for (j = 0; j < DCTSIZE; j++) | |
|  		    dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; | |
| @@ -554,8 +736,116 @@ | |
|  } | |
|   | |
|   | |
| +/* Parse an unsigned integer: subroutine for jtransform_parse_crop_spec. | |
| + * Returns TRUE if valid integer found, FALSE if not. | |
| + * *strptr is advanced over the digit string, and *result is set to its value. | |
| + */ | |
| + | |
| +LOCAL(boolean) | |
| +jt_read_integer (const char ** strptr, JDIMENSION * result) | |
| +{ | |
| +  const char * ptr = *strptr; | |
| +  JDIMENSION val = 0; | |
| + | |
| +  for (; isdigit(*ptr); ptr++) { | |
| +    val = val * 10 + (JDIMENSION) (*ptr - '0'); | |
| +  } | |
| +  *result = val; | |
| +  if (ptr == *strptr) | |
| +    return FALSE;		/* oops, no digits */ | |
| +  *strptr = ptr; | |
| +  return TRUE; | |
| +} | |
| + | |
| + | |
| +/* Parse a crop specification (written in X11 geometry style). | |
| + * The routine returns TRUE if the spec string is valid, FALSE if not. | |
| + * | |
| + * The crop spec string should have the format | |
| + *	<width>x<height>{+-}<xoffset>{+-}<yoffset> | |
| + * where width, height, xoffset, and yoffset are unsigned integers. | |
| + * Each of the elements can be omitted to indicate a default value. | |
| + * (A weakness of this style is that it is not possible to omit xoffset | |
| + * while specifying yoffset, since they look alike.) | |
| + * | |
| + * This code is loosely based on XParseGeometry from the X11 distribution. | |
| + */ | |
| + | |
| +GLOBAL(boolean) | |
| +jtransform_parse_crop_spec (jpeg_transform_info *info, const char *spec) | |
| +{ | |
| +  info->crop = FALSE; | |
| +  info->crop_width_set = JCROP_UNSET; | |
| +  info->crop_height_set = JCROP_UNSET; | |
| +  info->crop_xoffset_set = JCROP_UNSET; | |
| +  info->crop_yoffset_set = JCROP_UNSET; | |
| + | |
| +  if (isdigit(*spec)) { | |
| +    /* fetch width */ | |
| +    if (! jt_read_integer(&spec, &info->crop_width)) | |
| +      return FALSE; | |
| +    info->crop_width_set = JCROP_POS; | |
| +  } | |
| +  if (*spec == 'x' || *spec == 'X') {	 | |
| +    /* fetch height */ | |
| +    spec++; | |
| +    if (! jt_read_integer(&spec, &info->crop_height)) | |
| +      return FALSE; | |
| +    info->crop_height_set = JCROP_POS; | |
| +  } | |
| +  if (*spec == '+' || *spec == '-') { | |
| +    /* fetch xoffset */ | |
| +    info->crop_xoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; | |
| +    spec++; | |
| +    if (! jt_read_integer(&spec, &info->crop_xoffset)) | |
| +      return FALSE; | |
| +  } | |
| +  if (*spec == '+' || *spec == '-') { | |
| +    /* fetch yoffset */ | |
| +    info->crop_yoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; | |
| +    spec++; | |
| +    if (! jt_read_integer(&spec, &info->crop_yoffset)) | |
| +      return FALSE; | |
| +  } | |
| +  /* We had better have gotten to the end of the string. */ | |
| +  if (*spec != '\0') | |
| +    return FALSE; | |
| +  info->crop = TRUE; | |
| +  return TRUE; | |
| +} | |
| + | |
| + | |
| +/* Trim off any partial iMCUs on the indicated destination edge */ | |
| + | |
| +LOCAL(void) | |
| +trim_right_edge (jpeg_transform_info *info, JDIMENSION full_width) | |
| +{ | |
| +  JDIMENSION MCU_cols; | |
| + | |
| +  MCU_cols = info->output_width / (info->max_h_samp_factor * DCTSIZE); | |
| +  if (MCU_cols > 0 && info->x_crop_offset + MCU_cols == | |
| +      full_width / (info->max_h_samp_factor * DCTSIZE)) | |
| +    info->output_width = MCU_cols * (info->max_h_samp_factor * DCTSIZE); | |
| +} | |
| + | |
| +LOCAL(void) | |
| +trim_bottom_edge (jpeg_transform_info *info, JDIMENSION full_height) | |
| +{ | |
| +  JDIMENSION MCU_rows; | |
| + | |
| +  MCU_rows = info->output_height / (info->max_v_samp_factor * DCTSIZE); | |
| +  if (MCU_rows > 0 && info->y_crop_offset + MCU_rows == | |
| +      full_height / (info->max_v_samp_factor * DCTSIZE)) | |
| +    info->output_height = MCU_rows * (info->max_v_samp_factor * DCTSIZE); | |
| +} | |
| + | |
| + | |
|  /* Request any required workspace. | |
|   * | |
| + * This routine figures out the size that the output image will be | |
| + * (which implies that all the transform parameters must be set before | |
| + * it is called). | |
| + * | |
|   * We allocate the workspace virtual arrays from the source decompression | |
|   * object, so that all the arrays (both the original data and the workspace) | |
|   * will be taken into account while making memory management decisions. | |
| @@ -569,9 +859,13 @@ | |
|  			      jpeg_transform_info *info) | |
|  { | |
|    jvirt_barray_ptr *coef_arrays = NULL; | |
| +  boolean need_workspace, transpose_it; | |
|    jpeg_component_info *compptr; | |
| -  int ci; | |
| +  JDIMENSION xoffset, yoffset, width_in_iMCUs, height_in_iMCUs; | |
| +  JDIMENSION width_in_blocks, height_in_blocks; | |
| +  int ci, h_samp_factor, v_samp_factor; | |
|   | |
| +  /* Determine number of components in output image */ | |
|    if (info->force_grayscale && | |
|        srcinfo->jpeg_color_space == JCS_YCbCr && | |
|        srcinfo->num_components == 3) { | |
| @@ -581,55 +875,181 @@ | |
|      /* Process all the components */ | |
|      info->num_components = srcinfo->num_components; | |
|    } | |
| +  /* If there is only one output component, force the iMCU size to be 1; | |
| +   * else use the source iMCU size.  (This allows us to do the right thing | |
| +   * when reducing color to grayscale, and also provides a handy way of | |
| +   * cleaning up "funny" grayscale images whose sampling factors are not 1x1.) | |
| +   */ | |
| + | |
| +  switch (info->transform) { | |
| +  case JXFORM_TRANSPOSE: | |
| +  case JXFORM_TRANSVERSE: | |
| +  case JXFORM_ROT_90: | |
| +  case JXFORM_ROT_270: | |
| +    info->output_width = srcinfo->image_height; | |
| +    info->output_height = srcinfo->image_width; | |
| +    if (info->num_components == 1) { | |
| +      info->max_h_samp_factor = 1; | |
| +      info->max_v_samp_factor = 1; | |
| +    } else { | |
| +      info->max_h_samp_factor = srcinfo->max_v_samp_factor; | |
| +      info->max_v_samp_factor = srcinfo->max_h_samp_factor; | |
| +    } | |
| +    break; | |
| +  default: | |
| +    info->output_width = srcinfo->image_width; | |
| +    info->output_height = srcinfo->image_height; | |
| +    if (info->num_components == 1) { | |
| +      info->max_h_samp_factor = 1; | |
| +      info->max_v_samp_factor = 1; | |
| +    } else { | |
| +      info->max_h_samp_factor = srcinfo->max_h_samp_factor; | |
| +      info->max_v_samp_factor = srcinfo->max_v_samp_factor; | |
| +    } | |
| +    break; | |
| +  } | |
| + | |
| +  /* If cropping has been requested, compute the crop area's position and | |
| +   * dimensions, ensuring that its upper left corner falls at an iMCU boundary. | |
| +   */ | |
| +  if (info->crop) { | |
| +    /* Insert default values for unset crop parameters */ | |
| +    if (info->crop_xoffset_set == JCROP_UNSET) | |
| +      info->crop_xoffset = 0;	/* default to +0 */ | |
| +    if (info->crop_yoffset_set == JCROP_UNSET) | |
| +      info->crop_yoffset = 0;	/* default to +0 */ | |
| +    if (info->crop_xoffset >= info->output_width || | |
| +	info->crop_yoffset >= info->output_height) | |
| +      ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); | |
| +    if (info->crop_width_set == JCROP_UNSET) | |
| +      info->crop_width = info->output_width - info->crop_xoffset; | |
| +    if (info->crop_height_set == JCROP_UNSET) | |
| +      info->crop_height = info->output_height - info->crop_yoffset; | |
| +    /* Ensure parameters are valid */ | |
| +    if (info->crop_width <= 0 || info->crop_width > info->output_width || | |
| +	info->crop_height <= 0 || info->crop_height > info->output_height || | |
| +	info->crop_xoffset > info->output_width - info->crop_width || | |
| +	info->crop_yoffset > info->output_height - info->crop_height) | |
| +      ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); | |
| +    /* Convert negative crop offsets into regular offsets */ | |
| +    if (info->crop_xoffset_set == JCROP_NEG) | |
| +      xoffset = info->output_width - info->crop_width - info->crop_xoffset; | |
| +    else | |
| +      xoffset = info->crop_xoffset; | |
| +    if (info->crop_yoffset_set == JCROP_NEG) | |
| +      yoffset = info->output_height - info->crop_height - info->crop_yoffset; | |
| +    else | |
| +      yoffset = info->crop_yoffset; | |
| +    /* Now adjust so that upper left corner falls at an iMCU boundary */ | |
| +    info->output_width = | |
| +      info->crop_width + (xoffset % (info->max_h_samp_factor * DCTSIZE)); | |
| +    info->output_height = | |
| +      info->crop_height + (yoffset % (info->max_v_samp_factor * DCTSIZE)); | |
| +    /* Save x/y offsets measured in iMCUs */ | |
| +    info->x_crop_offset = xoffset / (info->max_h_samp_factor * DCTSIZE); | |
| +    info->y_crop_offset = yoffset / (info->max_v_samp_factor * DCTSIZE); | |
| +  } else { | |
| +    info->x_crop_offset = 0; | |
| +    info->y_crop_offset = 0; | |
| +  } | |
|   | |
| +  /* Figure out whether we need workspace arrays, | |
| +   * and if so whether they are transposed relative to the source. | |
| +   */ | |
| +  need_workspace = FALSE; | |
| +  transpose_it = FALSE; | |
|    switch (info->transform) { | |
|    case JXFORM_NONE: | |
| +    if (info->x_crop_offset != 0 || info->y_crop_offset != 0) | |
| +      need_workspace = TRUE; | |
| +    /* No workspace needed if neither cropping nor transforming */ | |
| +    break; | |
|    case JXFORM_FLIP_H: | |
| -    /* Don't need a workspace array */ | |
| +    if (info->trim) | |
| +      trim_right_edge(info, srcinfo->image_width); | |
| +    if (info->y_crop_offset != 0) | |
| +      need_workspace = TRUE; | |
| +    /* do_flip_h_no_crop doesn't need a workspace array */ | |
|      break; | |
|    case JXFORM_FLIP_V: | |
| -  case JXFORM_ROT_180: | |
| -    /* Need workspace arrays having same dimensions as source image. | |
| -     * Note that we allocate arrays padded out to the next iMCU boundary, | |
| -     * so that transform routines need not worry about missing edge blocks. | |
| -     */ | |
| -    coef_arrays = (jvirt_barray_ptr *) | |
| -      (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, | |
| -	SIZEOF(jvirt_barray_ptr) * info->num_components); | |
| -    for (ci = 0; ci < info->num_components; ci++) { | |
| -      compptr = srcinfo->comp_info + ci; | |
| -      coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) | |
| -	((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, | |
| -	 (JDIMENSION) jround_up((long) compptr->width_in_blocks, | |
| -				(long) compptr->h_samp_factor), | |
| -	 (JDIMENSION) jround_up((long) compptr->height_in_blocks, | |
| -				(long) compptr->v_samp_factor), | |
| -	 (JDIMENSION) compptr->v_samp_factor); | |
| -    } | |
| +    if (info->trim) | |
| +      trim_bottom_edge(info, srcinfo->image_height); | |
| +    /* Need workspace arrays having same dimensions as source image. */ | |
| +    need_workspace = TRUE; | |
|      break; | |
|    case JXFORM_TRANSPOSE: | |
| +    /* transpose does NOT have to trim anything */ | |
| +    /* Need workspace arrays having transposed dimensions. */ | |
| +    need_workspace = TRUE; | |
| +    transpose_it = TRUE; | |
| +    break; | |
|    case JXFORM_TRANSVERSE: | |
| +    if (info->trim) { | |
| +      trim_right_edge(info, srcinfo->image_height); | |
| +      trim_bottom_edge(info, srcinfo->image_width); | |
| +    } | |
| +    /* Need workspace arrays having transposed dimensions. */ | |
| +    need_workspace = TRUE; | |
| +    transpose_it = TRUE; | |
| +    break; | |
|    case JXFORM_ROT_90: | |
| +    if (info->trim) | |
| +      trim_right_edge(info, srcinfo->image_height); | |
| +    /* Need workspace arrays having transposed dimensions. */ | |
| +    need_workspace = TRUE; | |
| +    transpose_it = TRUE; | |
| +    break; | |
| +  case JXFORM_ROT_180: | |
| +    if (info->trim) { | |
| +      trim_right_edge(info, srcinfo->image_width); | |
| +      trim_bottom_edge(info, srcinfo->image_height); | |
| +    } | |
| +    /* Need workspace arrays having same dimensions as source image. */ | |
| +    need_workspace = TRUE; | |
| +    break; | |
|    case JXFORM_ROT_270: | |
| -    /* Need workspace arrays having transposed dimensions. | |
| -     * Note that we allocate arrays padded out to the next iMCU boundary, | |
| -     * so that transform routines need not worry about missing edge blocks. | |
| -     */ | |
| +    if (info->trim) | |
| +      trim_bottom_edge(info, srcinfo->image_width); | |
| +    /* Need workspace arrays having transposed dimensions. */ | |
| +    need_workspace = TRUE; | |
| +    transpose_it = TRUE; | |
| +    break; | |
| +  } | |
| + | |
| +  /* Allocate workspace if needed. | |
| +   * Note that we allocate arrays padded out to the next iMCU boundary, | |
| +   * so that transform routines need not worry about missing edge blocks. | |
| +   */ | |
| +  if (need_workspace) { | |
|      coef_arrays = (jvirt_barray_ptr *) | |
|        (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, | |
| -	SIZEOF(jvirt_barray_ptr) * info->num_components); | |
| +		SIZEOF(jvirt_barray_ptr) * info->num_components); | |
| +    width_in_iMCUs = (JDIMENSION) | |
| +      jdiv_round_up((long) info->output_width, | |
| +		    (long) (info->max_h_samp_factor * DCTSIZE)); | |
| +    height_in_iMCUs = (JDIMENSION) | |
| +      jdiv_round_up((long) info->output_height, | |
| +		    (long) (info->max_v_samp_factor * DCTSIZE)); | |
|      for (ci = 0; ci < info->num_components; ci++) { | |
|        compptr = srcinfo->comp_info + ci; | |
| +      if (info->num_components == 1) { | |
| +	/* we're going to force samp factors to 1x1 in this case */ | |
| +	h_samp_factor = v_samp_factor = 1; | |
| +      } else if (transpose_it) { | |
| +	h_samp_factor = compptr->v_samp_factor; | |
| +	v_samp_factor = compptr->h_samp_factor; | |
| +      } else { | |
| +	h_samp_factor = compptr->h_samp_factor; | |
| +	v_samp_factor = compptr->v_samp_factor; | |
| +      } | |
| +      width_in_blocks = width_in_iMCUs * h_samp_factor; | |
| +      height_in_blocks = height_in_iMCUs * v_samp_factor; | |
|        coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) | |
|  	((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, | |
| -	 (JDIMENSION) jround_up((long) compptr->height_in_blocks, | |
| -				(long) compptr->v_samp_factor), | |
| -	 (JDIMENSION) jround_up((long) compptr->width_in_blocks, | |
| -				(long) compptr->h_samp_factor), | |
| -	 (JDIMENSION) compptr->h_samp_factor); | |
| +	 width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); | |
|      } | |
| -    break; | |
|    } | |
| + | |
|    info->workspace_coef_arrays = coef_arrays; | |
|  } | |
|   | |
| @@ -642,14 +1062,8 @@ | |
|    int tblno, i, j, ci, itemp; | |
|    jpeg_component_info *compptr; | |
|    JQUANT_TBL *qtblptr; | |
| -  JDIMENSION dtemp; | |
|    UINT16 qtemp; | |
|   | |
| -  /* Transpose basic image dimensions */ | |
| -  dtemp = dstinfo->image_width; | |
| -  dstinfo->image_width = dstinfo->image_height; | |
| -  dstinfo->image_height = dtemp; | |
| - | |
|    /* Transpose sampling factors */ | |
|    for (ci = 0; ci < dstinfo->num_components; ci++) { | |
|      compptr = dstinfo->comp_info + ci; | |
| @@ -674,46 +1088,159 @@ | |
|  } | |
|   | |
|   | |
| -/* Trim off any partial iMCUs on the indicated destination edge */ | |
| +/* Adjust Exif image parameters. | |
| + * | |
| + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. | |
| + */ | |
|   | |
|  LOCAL(void) | |
| -trim_right_edge (j_compress_ptr dstinfo) | |
| +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, | |
| +			JDIMENSION new_width, JDIMENSION new_height) | |
|  { | |
| -  int ci, max_h_samp_factor; | |
| -  JDIMENSION MCU_cols; | |
| +  boolean is_motorola; /* Flag for byte order */ | |
| +  unsigned int number_of_tags, tagnum; | |
| +  unsigned int firstoffset, offset; | |
| +  JDIMENSION new_value; | |
| + | |
| +  if (length < 12) return; /* Length of an IFD entry */ | |
| + | |
| +  /* Discover byte order */ | |
| +  if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) | |
| +    is_motorola = FALSE; | |
| +  else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) | |
| +    is_motorola = TRUE; | |
| +  else | |
| +    return; | |
| + | |
| +  /* Check Tag Mark */ | |
| +  if (is_motorola) { | |
| +    if (GETJOCTET(data[2]) != 0) return; | |
| +    if (GETJOCTET(data[3]) != 0x2A) return; | |
| +  } else { | |
| +    if (GETJOCTET(data[3]) != 0) return; | |
| +    if (GETJOCTET(data[2]) != 0x2A) return; | |
| +  } | |
|   | |
| -  /* We have to compute max_h_samp_factor ourselves, | |
| -   * because it hasn't been set yet in the destination | |
| -   * (and we don't want to use the source's value). | |
| -   */ | |
| -  max_h_samp_factor = 1; | |
| -  for (ci = 0; ci < dstinfo->num_components; ci++) { | |
| -    int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; | |
| -    max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); | |
| +  /* Get first IFD offset (offset to IFD0) */ | |
| +  if (is_motorola) { | |
| +    if (GETJOCTET(data[4]) != 0) return; | |
| +    if (GETJOCTET(data[5]) != 0) return; | |
| +    firstoffset = GETJOCTET(data[6]); | |
| +    firstoffset <<= 8; | |
| +    firstoffset += GETJOCTET(data[7]); | |
| +  } else { | |
| +    if (GETJOCTET(data[7]) != 0) return; | |
| +    if (GETJOCTET(data[6]) != 0) return; | |
| +    firstoffset = GETJOCTET(data[5]); | |
| +    firstoffset <<= 8; | |
| +    firstoffset += GETJOCTET(data[4]); | |
|    } | |
| -  MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); | |
| -  if (MCU_cols > 0)		/* can't trim to 0 pixels */ | |
| -    dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); | |
| -} | |
| +  if (firstoffset > length - 2) return; /* check end of data segment */ | |
|   | |
| -LOCAL(void) | |
| -trim_bottom_edge (j_compress_ptr dstinfo) | |
| -{ | |
| -  int ci, max_v_samp_factor; | |
| -  JDIMENSION MCU_rows; | |
| +  /* Get the number of directory entries contained in this IFD */ | |
| +  if (is_motorola) { | |
| +    number_of_tags = GETJOCTET(data[firstoffset]); | |
| +    number_of_tags <<= 8; | |
| +    number_of_tags += GETJOCTET(data[firstoffset+1]); | |
| +  } else { | |
| +    number_of_tags = GETJOCTET(data[firstoffset+1]); | |
| +    number_of_tags <<= 8; | |
| +    number_of_tags += GETJOCTET(data[firstoffset]); | |
| +  } | |
| +  if (number_of_tags == 0) return; | |
| +  firstoffset += 2; | |
|   | |
| -  /* We have to compute max_v_samp_factor ourselves, | |
| -   * because it hasn't been set yet in the destination | |
| -   * (and we don't want to use the source's value). | |
| -   */ | |
| -  max_v_samp_factor = 1; | |
| -  for (ci = 0; ci < dstinfo->num_components; ci++) { | |
| -    int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; | |
| -    max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); | |
| +  /* Search for ExifSubIFD offset Tag in IFD0 */ | |
| +  for (;;) { | |
| +    if (firstoffset > length - 12) return; /* check end of data segment */ | |
| +    /* Get Tag number */ | |
| +    if (is_motorola) { | |
| +      tagnum = GETJOCTET(data[firstoffset]); | |
| +      tagnum <<= 8; | |
| +      tagnum += GETJOCTET(data[firstoffset+1]); | |
| +    } else { | |
| +      tagnum = GETJOCTET(data[firstoffset+1]); | |
| +      tagnum <<= 8; | |
| +      tagnum += GETJOCTET(data[firstoffset]); | |
| +    } | |
| +    if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */ | |
| +    if (--number_of_tags == 0) return; | |
| +    firstoffset += 12; | |
|    } | |
| -  MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); | |
| -  if (MCU_rows > 0)		/* can't trim to 0 pixels */ | |
| -    dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); | |
| + | |
| +  /* Get the ExifSubIFD offset */ | |
| +  if (is_motorola) { | |
| +    if (GETJOCTET(data[firstoffset+8]) != 0) return; | |
| +    if (GETJOCTET(data[firstoffset+9]) != 0) return; | |
| +    offset = GETJOCTET(data[firstoffset+10]); | |
| +    offset <<= 8; | |
| +    offset += GETJOCTET(data[firstoffset+11]); | |
| +  } else { | |
| +    if (GETJOCTET(data[firstoffset+11]) != 0) return; | |
| +    if (GETJOCTET(data[firstoffset+10]) != 0) return; | |
| +    offset = GETJOCTET(data[firstoffset+9]); | |
| +    offset <<= 8; | |
| +    offset += GETJOCTET(data[firstoffset+8]); | |
| +  } | |
| +  if (offset > length - 2) return; /* check end of data segment */ | |
| + | |
| +  /* Get the number of directory entries contained in this SubIFD */ | |
| +  if (is_motorola) { | |
| +    number_of_tags = GETJOCTET(data[offset]); | |
| +    number_of_tags <<= 8; | |
| +    number_of_tags += GETJOCTET(data[offset+1]); | |
| +  } else { | |
| +    number_of_tags = GETJOCTET(data[offset+1]); | |
| +    number_of_tags <<= 8; | |
| +    number_of_tags += GETJOCTET(data[offset]); | |
| +  } | |
| +  if (number_of_tags < 2) return; | |
| +  offset += 2; | |
| + | |
| +  /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */ | |
| +  do { | |
| +    if (offset > length - 12) return; /* check end of data segment */ | |
| +    /* Get Tag number */ | |
| +    if (is_motorola) { | |
| +      tagnum = GETJOCTET(data[offset]); | |
| +      tagnum <<= 8; | |
| +      tagnum += GETJOCTET(data[offset+1]); | |
| +    } else { | |
| +      tagnum = GETJOCTET(data[offset+1]); | |
| +      tagnum <<= 8; | |
| +      tagnum += GETJOCTET(data[offset]); | |
| +    } | |
| +    if (tagnum == 0xA002 || tagnum == 0xA003) { | |
| +      if (tagnum == 0xA002) | |
| +	new_value = new_width; /* ExifImageWidth Tag */ | |
| +      else | |
| +	new_value = new_height; /* ExifImageHeight Tag */ | |
| +      if (is_motorola) { | |
| +	data[offset+2] = 0; /* Format = unsigned long (4 octets) */ | |
| +	data[offset+3] = 4; | |
| +	data[offset+4] = 0; /* Number Of Components = 1 */ | |
| +	data[offset+5] = 0; | |
| +	data[offset+6] = 0; | |
| +	data[offset+7] = 1; | |
| +	data[offset+8] = 0; | |
| +	data[offset+9] = 0; | |
| +	data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF); | |
| +	data[offset+11] = (JOCTET)(new_value & 0xFF); | |
| +      } else { | |
| +	data[offset+2] = 4; /* Format = unsigned long (4 octets) */ | |
| +	data[offset+3] = 0; | |
| +	data[offset+4] = 1; /* Number Of Components = 1 */ | |
| +	data[offset+5] = 0; | |
| +	data[offset+6] = 0; | |
| +	data[offset+7] = 0; | |
| +	data[offset+8] = (JOCTET)(new_value & 0xFF); | |
| +	data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF); | |
| +	data[offset+10] = 0; | |
| +	data[offset+11] = 0; | |
| +      } | |
| +    } | |
| +    offset += 12; | |
| +  } while (--number_of_tags); | |
|  } | |
|   | |
|   | |
| @@ -736,18 +1263,22 @@ | |
|  { | |
|    /* If force-to-grayscale is requested, adjust destination parameters */ | |
|    if (info->force_grayscale) { | |
| -    /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed | |
| -     * properly.  Among other things, the target h_samp_factor & v_samp_factor | |
| -     * will get set to 1, which typically won't match the source. | |
| -     * In fact we do this even if the source is already grayscale; that | |
| -     * provides an easy way of coercing a grayscale JPEG with funny sampling | |
| -     * factors to the customary 1,1.  (Some decoders fail on other factors.) | |
| +    /* First, ensure we have YCbCr or grayscale data, and that the source's | |
| +     * Y channel is full resolution.  (No reasonable person would make Y | |
| +     * be less than full resolution, so actually coping with that case | |
| +     * isn't worth extra code space.  But we check it to avoid crashing.) | |
|       */ | |
| -    if ((dstinfo->jpeg_color_space == JCS_YCbCr && | |
| -	 dstinfo->num_components == 3) || | |
| -	(dstinfo->jpeg_color_space == JCS_GRAYSCALE && | |
| -	 dstinfo->num_components == 1)) { | |
| -      /* We have to preserve the source's quantization table number. */ | |
| +    if (((dstinfo->jpeg_color_space == JCS_YCbCr && | |
| +	  dstinfo->num_components == 3) || | |
| +	 (dstinfo->jpeg_color_space == JCS_GRAYSCALE && | |
| +	  dstinfo->num_components == 1)) && | |
| +	srcinfo->comp_info[0].h_samp_factor == srcinfo->max_h_samp_factor && | |
| +	srcinfo->comp_info[0].v_samp_factor == srcinfo->max_v_samp_factor) { | |
| +      /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed | |
| +       * properly.  Among other things, it sets the target h_samp_factor & | |
| +       * v_samp_factor to 1, which typically won't match the source. | |
| +       * We have to preserve the source's quantization table number, however. | |
| +       */ | |
|        int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; | |
|        jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); | |
|        dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; | |
| @@ -755,50 +1286,52 @@ | |
|        /* Sorry, can't do it */ | |
|        ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); | |
|      } | |
| +  } else if (info->num_components == 1) { | |
| +    /* For a single-component source, we force the destination sampling factors | |
| +     * to 1x1, with or without force_grayscale.  This is useful because some | |
| +     * decoders choke on grayscale images with other sampling factors. | |
| +     */ | |
| +    dstinfo->comp_info[0].h_samp_factor = 1; | |
| +    dstinfo->comp_info[0].v_samp_factor = 1; | |
|    } | |
|   | |
| -  /* Correct the destination's image dimensions etc if necessary */ | |
| +  /* Correct the destination's image dimensions as necessary | |
| +   * for crop and rotate/flip operations. | |
| +   */ | |
| +  dstinfo->image_width = info->output_width; | |
| +  dstinfo->image_height = info->output_height; | |
| + | |
| +  /* Transpose destination image parameters */ | |
|    switch (info->transform) { | |
| -  case JXFORM_NONE: | |
| -    /* Nothing to do */ | |
| -    break; | |
| -  case JXFORM_FLIP_H: | |
| -    if (info->trim) | |
| -      trim_right_edge(dstinfo); | |
| -    break; | |
| -  case JXFORM_FLIP_V: | |
| -    if (info->trim) | |
| -      trim_bottom_edge(dstinfo); | |
| -    break; | |
|    case JXFORM_TRANSPOSE: | |
| -    transpose_critical_parameters(dstinfo); | |
| -    /* transpose does NOT have to trim anything */ | |
| -    break; | |
|    case JXFORM_TRANSVERSE: | |
| -    transpose_critical_parameters(dstinfo); | |
| -    if (info->trim) { | |
| -      trim_right_edge(dstinfo); | |
| -      trim_bottom_edge(dstinfo); | |
| -    } | |
| -    break; | |
|    case JXFORM_ROT_90: | |
| -    transpose_critical_parameters(dstinfo); | |
| -    if (info->trim) | |
| -      trim_right_edge(dstinfo); | |
| -    break; | |
| -  case JXFORM_ROT_180: | |
| -    if (info->trim) { | |
| -      trim_right_edge(dstinfo); | |
| -      trim_bottom_edge(dstinfo); | |
| -    } | |
| -    break; | |
|    case JXFORM_ROT_270: | |
|      transpose_critical_parameters(dstinfo); | |
| -    if (info->trim) | |
| -      trim_bottom_edge(dstinfo); | |
|      break; | |
|    } | |
|   | |
| +  /* Adjust Exif properties */ | |
| +  if (srcinfo->marker_list != NULL && | |
| +      srcinfo->marker_list->marker == JPEG_APP0+1 && | |
| +      srcinfo->marker_list->data_length >= 6 && | |
| +      GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && | |
| +      GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && | |
| +      GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && | |
| +      GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && | |
| +      GETJOCTET(srcinfo->marker_list->data[4]) == 0 && | |
| +      GETJOCTET(srcinfo->marker_list->data[5]) == 0) { | |
| +    /* Suppress output of JFIF marker */ | |
| +    dstinfo->write_JFIF_header = FALSE; | |
| +    /* Adjust Exif image parameters */ | |
| +    if (dstinfo->image_width != srcinfo->image_width || | |
| +	dstinfo->image_height != srcinfo->image_height) | |
| +      /* Align data segment to start of TIFF structure for parsing */ | |
| +      adjust_exif_parameters(srcinfo->marker_list->data + 6, | |
| +	srcinfo->marker_list->data_length - 6, | |
| +	dstinfo->image_width, dstinfo->image_height); | |
| +  } | |
| + | |
|    /* Return the appropriate output data set */ | |
|    if (info->workspace_coef_arrays != NULL) | |
|      return info->workspace_coef_arrays; | |
| @@ -816,40 +1349,108 @@ | |
|   */ | |
|   | |
|  GLOBAL(void) | |
| -jtransform_execute_transformation (j_decompress_ptr srcinfo, | |
| -				   j_compress_ptr dstinfo, | |
| -				   jvirt_barray_ptr *src_coef_arrays, | |
| -				   jpeg_transform_info *info) | |
| +jtransform_execute_transform (j_decompress_ptr srcinfo, | |
| +			      j_compress_ptr dstinfo, | |
| +			      jvirt_barray_ptr *src_coef_arrays, | |
| +			      jpeg_transform_info *info) | |
|  { | |
|    jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; | |
|   | |
| +  /* Note: conditions tested here should match those in switch statement | |
| +   * in jtransform_request_workspace() | |
| +   */ | |
|    switch (info->transform) { | |
|    case JXFORM_NONE: | |
| +    if (info->x_crop_offset != 0 || info->y_crop_offset != 0) | |
| +      do_crop(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +	      src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_FLIP_H: | |
| -    do_flip_h(srcinfo, dstinfo, src_coef_arrays); | |
| +    if (info->y_crop_offset != 0) | |
| +      do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +		src_coef_arrays, dst_coef_arrays); | |
| +    else | |
| +      do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset, | |
| +			src_coef_arrays); | |
|      break; | |
|    case JXFORM_FLIP_V: | |
| -    do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +	      src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_TRANSPOSE: | |
| -    do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_transpose(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +		 src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_TRANSVERSE: | |
| -    do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_transverse(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +		  src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_ROT_90: | |
| -    do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_rot_90(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +	      src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_ROT_180: | |
| -    do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_rot_180(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +	       src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    case JXFORM_ROT_270: | |
| -    do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); | |
| +    do_rot_270(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, | |
| +	       src_coef_arrays, dst_coef_arrays); | |
|      break; | |
|    } | |
|  } | |
|   | |
| +/* jtransform_perfect_transform | |
| + * | |
| + * Determine whether lossless transformation is perfectly | |
| + * possible for a specified image and transformation. | |
| + * | |
| + * Inputs: | |
| + *   image_width, image_height: source image dimensions. | |
| + *   MCU_width, MCU_height: pixel dimensions of MCU. | |
| + *   transform: transformation identifier. | |
| + * Parameter sources from initialized jpeg_struct | |
| + * (after reading source header): | |
| + *   image_width = cinfo.image_width | |
| + *   image_height = cinfo.image_height | |
| + *   MCU_width = cinfo.max_h_samp_factor * DCTSIZE | |
| + *   MCU_height = cinfo.max_v_samp_factor * DCTSIZE | |
| + * Result: | |
| + *   TRUE = perfect transformation possible | |
| + *   FALSE = perfect transformation not possible | |
| + *           (may use custom action then) | |
| + */ | |
| + | |
| +GLOBAL(boolean) | |
| +jtransform_perfect_transform(JDIMENSION image_width, JDIMENSION image_height, | |
| +			     int MCU_width, int MCU_height, | |
| +			     JXFORM_CODE transform) | |
| +{ | |
| +  boolean result = TRUE; /* initialize TRUE */ | |
| + | |
| +  switch (transform) { | |
| +  case JXFORM_FLIP_H: | |
| +  case JXFORM_ROT_270: | |
| +    if (image_width % (JDIMENSION) MCU_width) | |
| +      result = FALSE; | |
| +    break; | |
| +  case JXFORM_FLIP_V: | |
| +  case JXFORM_ROT_90: | |
| +    if (image_height % (JDIMENSION) MCU_height) | |
| +      result = FALSE; | |
| +    break; | |
| +  case JXFORM_TRANSVERSE: | |
| +  case JXFORM_ROT_180: | |
| +    if (image_width % (JDIMENSION) MCU_width) | |
| +      result = FALSE; | |
| +    if (image_height % (JDIMENSION) MCU_height) | |
| +      result = FALSE; | |
| +    break; | |
| +  } | |
| + | |
| +  return result; | |
| +} | |
| + | |
|  #endif /* TRANSFORMS_SUPPORTED */ | |
|   | |
|   | |
| diff -u jpeg-6b/transupp.h jpeg-6b-new/transupp.h | |
| --- jpeg-6b/transupp.h	1997-07-24 04:39:12.000000000 +0200 | |
| +++ jpeg-6b-new/transupp.h	2003-09-21 22:53:08.000000000 +0200 | |
| @@ -1,7 +1,7 @@ | |
|  /* | |
|   * transupp.h | |
|   * | |
| - * Copyright (C) 1997, Thomas G. Lane. | |
| + * Copyright (C) 1997-2001, Thomas G. Lane. | |
|   * This file is part of the Independent JPEG Group's software. | |
|   * For conditions of distribution and use, see the accompanying README file. | |
|   * | |
| @@ -22,32 +22,6 @@ | |
|  #define TRANSFORMS_SUPPORTED 1		/* 0 disables transform code */ | |
|  #endif | |
|   | |
| -/* Short forms of external names for systems with brain-damaged linkers. */ | |
| - | |
| -#ifdef NEED_SHORT_EXTERNAL_NAMES | |
| -#define jtransform_request_workspace		jTrRequest | |
| -#define jtransform_adjust_parameters		jTrAdjust | |
| -#define jtransform_execute_transformation	jTrExec | |
| -#define jcopy_markers_setup			jCMrkSetup | |
| -#define jcopy_markers_execute			jCMrkExec | |
| -#endif /* NEED_SHORT_EXTERNAL_NAMES */ | |
| - | |
| - | |
| -/* | |
| - * Codes for supported types of image transformations. | |
| - */ | |
| - | |
| -typedef enum { | |
| -	JXFORM_NONE,		/* no transformation */ | |
| -	JXFORM_FLIP_H,		/* horizontal flip */ | |
| -	JXFORM_FLIP_V,		/* vertical flip */ | |
| -	JXFORM_TRANSPOSE,	/* transpose across UL-to-LR axis */ | |
| -	JXFORM_TRANSVERSE,	/* transpose across UR-to-LL axis */ | |
| -	JXFORM_ROT_90,		/* 90-degree clockwise rotation */ | |
| -	JXFORM_ROT_180,		/* 180-degree rotation */ | |
| -	JXFORM_ROT_270		/* 270-degree clockwise (or 90 ccw) */ | |
| -} JXFORM_CODE; | |
| - | |
|  /* | |
|   * Although rotating and flipping data expressed as DCT coefficients is not | |
|   * hard, there is an asymmetry in the JPEG format specification for images | |
| @@ -75,6 +49,19 @@ | |
|   * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim | |
|   * followed by -rot 180 -trim trims both edges.) | |
|   * | |
| + * We also offer a lossless-crop option, which discards data outside a given | |
| + * image region but losslessly preserves what is inside.  Like the rotate and | |
| + * flip transforms, lossless crop is restricted by the JPEG format: the upper | |
| + * left corner of the selected region must fall on an iMCU boundary.  If this | |
| + * does not hold for the given crop parameters, we silently move the upper left | |
| + * corner up and/or left to make it so, simultaneously increasing the region | |
| + * dimensions to keep the lower right crop corner unchanged.  (Thus, the | |
| + * output image covers at least the requested region, but may cover more.) | |
| + * | |
| + * If both crop and a rotate/flip transform are requested, the crop is applied | |
| + * last --- that is, the crop region is specified in terms of the destination | |
| + * image. | |
| + * | |
|   * We also offer a "force to grayscale" option, which simply discards the | |
|   * chrominance channels of a YCbCr image.  This is lossless in the sense that | |
|   * the luminance channel is preserved exactly.  It's not the same kind of | |
| @@ -83,20 +70,89 @@ | |
|   * be aware of the option to know how many components to work on. | |
|   */ | |
|   | |
| + | |
| +/* Short forms of external names for systems with brain-damaged linkers. */ | |
| + | |
| +#ifdef NEED_SHORT_EXTERNAL_NAMES | |
| +#define jtransform_parse_crop_spec	jTrParCrop | |
| +#define jtransform_request_workspace	jTrRequest | |
| +#define jtransform_adjust_parameters	jTrAdjust | |
| +#define jtransform_execute_transform	jTrExec | |
| +#define jtransform_perfect_transform	jTrPerfect | |
| +#define jcopy_markers_setup		jCMrkSetup | |
| +#define jcopy_markers_execute		jCMrkExec | |
| +#endif /* NEED_SHORT_EXTERNAL_NAMES */ | |
| + | |
| + | |
| +/* | |
| + * Codes for supported types of image transformations. | |
| + */ | |
| + | |
| +typedef enum { | |
| +	JXFORM_NONE,		/* no transformation */ | |
| +	JXFORM_FLIP_H,		/* horizontal flip */ | |
| +	JXFORM_FLIP_V,		/* vertical flip */ | |
| +	JXFORM_TRANSPOSE,	/* transpose across UL-to-LR axis */ | |
| +	JXFORM_TRANSVERSE,	/* transpose across UR-to-LL axis */ | |
| +	JXFORM_ROT_90,		/* 90-degree clockwise rotation */ | |
| +	JXFORM_ROT_180,		/* 180-degree rotation */ | |
| +	JXFORM_ROT_270		/* 270-degree clockwise (or 90 ccw) */ | |
| +} JXFORM_CODE; | |
| + | |
| +/* | |
| + * Codes for crop parameters, which can individually be unspecified, | |
| + * positive, or negative.  (Negative width or height makes no sense, though.) | |
| + */ | |
| + | |
| +typedef enum { | |
| +	JCROP_UNSET, | |
| +	JCROP_POS, | |
| +	JCROP_NEG | |
| +} JCROP_CODE; | |
| + | |
| +/* | |
| + * Transform parameters struct. | |
| + * NB: application must not change any elements of this struct after | |
| + * calling jtransform_request_workspace. | |
| + */ | |
| + | |
|  typedef struct { | |
|    /* Options: set by caller */ | |
|    JXFORM_CODE transform;	/* image transform operator */ | |
| +  boolean perfect;		/* if TRUE, fail if partial MCUs are requested */ | |
|    boolean trim;			/* if TRUE, trim partial MCUs as needed */ | |
|    boolean force_grayscale;	/* if TRUE, convert color image to grayscale */ | |
| +  boolean crop;			/* if TRUE, crop source image */ | |
| + | |
| +  /* Crop parameters: application need not set these unless crop is TRUE. | |
| +   * These can be filled in by jtransform_parse_crop_spec(). | |
| +   */ | |
| +  JDIMENSION crop_width;	/* Width of selected region */ | |
| +  JCROP_CODE crop_width_set; | |
| +  JDIMENSION crop_height;	/* Height of selected region */ | |
| +  JCROP_CODE crop_height_set; | |
| +  JDIMENSION crop_xoffset;	/* X offset of selected region */ | |
| +  JCROP_CODE crop_xoffset_set;	/* (negative measures from right edge) */ | |
| +  JDIMENSION crop_yoffset;	/* Y offset of selected region */ | |
| +  JCROP_CODE crop_yoffset_set;	/* (negative measures from bottom edge) */ | |
|   | |
|    /* Internal workspace: caller should not touch these */ | |
|    int num_components;		/* # of components in workspace */ | |
|    jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ | |
| +  JDIMENSION output_width;	/* cropped destination dimensions */ | |
| +  JDIMENSION output_height; | |
| +  JDIMENSION x_crop_offset;	/* destination crop offsets measured in iMCUs */ | |
| +  JDIMENSION y_crop_offset; | |
| +  int max_h_samp_factor;	/* destination iMCU size */ | |
| +  int max_v_samp_factor; | |
|  } jpeg_transform_info; | |
|   | |
|   | |
|  #if TRANSFORMS_SUPPORTED | |
|   | |
| +/* Parse a crop specification (written in X11 geometry style) */ | |
| +EXTERN(boolean) jtransform_parse_crop_spec | |
| +	JPP((jpeg_transform_info *info, const char *spec)); | |
|  /* Request any required workspace */ | |
|  EXTERN(void) jtransform_request_workspace | |
|  	JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); | |
| @@ -106,10 +162,24 @@ | |
|  	     jvirt_barray_ptr *src_coef_arrays, | |
|  	     jpeg_transform_info *info)); | |
|  /* Execute the actual transformation, if any */ | |
| -EXTERN(void) jtransform_execute_transformation | |
| +EXTERN(void) jtransform_execute_transform | |
|  	JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, | |
|  	     jvirt_barray_ptr *src_coef_arrays, | |
|  	     jpeg_transform_info *info)); | |
| +/* Determine whether lossless transformation is perfectly | |
| + * possible for a specified image and transformation. | |
| + */ | |
| +EXTERN(boolean) jtransform_perfect_transform | |
| +	JPP((JDIMENSION image_width, JDIMENSION image_height, | |
| +	     int MCU_width, int MCU_height, | |
| +	     JXFORM_CODE transform)); | |
| + | |
| +/* jtransform_execute_transform used to be called | |
| + * jtransform_execute_transformation, but some compilers complain about | |
| + * routine names that long.  This macro is here to avoid breaking any | |
| + * old source code that uses the original name... | |
| + */ | |
| +#define jtransform_execute_transformation	jtransform_execute_transform | |
|   | |
|  #endif /* TRANSFORMS_SUPPORTED */ | |
|  
 | |
| 
 |