diff options
author | Daniel Friesel <derf@derf.homelinux.org> | 2010-02-21 10:51:14 +0100 |
---|---|---|
committer | Daniel Friesel <derf@derf.homelinux.org> | 2010-02-21 10:51:14 +0100 |
commit | 15ede2653b45a1fe73bc8c7c9151e3e8dc1c32dc (patch) | |
tree | 59cedab94c8087ae9d87df6906b17a541305d086 | |
parent | cd4cf4cab94b06b477afe77c561c1554342af773 (diff) |
Fix lossless image rotation
Apparently, the old code was based on libjpeg6. However, most systems use
libjpeg8 now, which has a few new features like cropping, leading to a lot
of fuckup with feh on libjpeg8 systems. Now feh works with libjpeg8,
but probably doesn't support libjpeg6 anymore.
Also, this code copypasta really sucks :-)
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | src/jpegint.h | 114 | ||||
-rw-r--r-- | src/transupp.c | 1069 | ||||
-rw-r--r-- | src/transupp.h | 129 |
4 files changed, 780 insertions, 535 deletions
@@ -27,3 +27,6 @@ Control thumbnail mode (image selection, mainly) with keys. Xinerama support is present, but far from perfect. Some day I will debug that, I guess. + +We have a lot of copypasta from libjpeg (jpegint.h, transupp.[ch]). +I wonder if they can be #included from somewhere. diff --git a/src/jpegint.h b/src/jpegint.h index e55a2be..eb6ec8b 100644 --- a/src/jpegint.h +++ b/src/jpegint.h @@ -2,6 +2,7 @@ * jpegint.h * * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -10,6 +11,7 @@ * applications using the library shouldn't need to include this file. */ + /* Declarations for both compression & decompression */ typedef enum { /* Operating modes for buffer controllers */ @@ -37,6 +39,7 @@ typedef enum { /* Operating modes for buffer controllers */ #define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ #define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + /* Declarations for compression modules */ /* Master control module */ @@ -53,17 +56,19 @@ struct jpeg_comp_master { /* Main buffer control (downsampled-data buffer) */ struct jpeg_c_main_controller { JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); - JMETHOD(void, process_data, - (j_compress_ptr cinfo, JSAMPARRAY input_buf, JDIMENSION * in_row_ctr, JDIMENSION in_rows_avail)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION * in_row_ctr, JDIMENSION in_rows_avail)); }; /* Compression preprocessing (downsampling input buffer control) */ struct jpeg_c_prep_controller { JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); - JMETHOD(void, pre_process_data, - (j_compress_ptr cinfo, JSAMPARRAY input_buf, - JDIMENSION * in_row_ctr, JDIMENSION in_rows_avail, - JSAMPIMAGE output_buf, JDIMENSION * out_row_group_ctr, JDIMENSION out_row_groups_avail)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION * in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION * out_row_group_ctr, JDIMENSION out_row_groups_avail)); }; /* Coefficient buffer control */ @@ -76,29 +81,30 @@ struct jpeg_c_coef_controller { struct jpeg_color_converter { JMETHOD(void, start_pass, (j_compress_ptr cinfo)); JMETHOD(void, color_convert, (j_compress_ptr cinfo, - JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows)); + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); }; /* Downsampling */ struct jpeg_downsampler { JMETHOD(void, start_pass, (j_compress_ptr cinfo)); JMETHOD(void, downsample, (j_compress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_index, JSAMPIMAGE output_buf, JDIMENSION out_row_group_index)); + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index)); boolean need_context_rows; /* TRUE if need rows above & below */ }; /* Forward DCT (also controls coefficient quantization) */ +typedef JMETHOD(void, forward_DCT_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks)); + struct jpeg_forward_dct { JMETHOD(void, start_pass, (j_compress_ptr cinfo)); - /* perhaps this should be an array??? */ - JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, - jpeg_component_info * compptr, - JSAMPARRAY sample_data, - JBLOCKROW coef_blocks, - JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks)); + /* It is useful to allow each component to have a separate FDCT method. */ + forward_DCT_ptr forward_DCT[MAX_COMPONENTS]; }; /* Entropy encoding */ @@ -121,6 +127,7 @@ struct jpeg_marker_writer { JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); }; + /* Declarations for decompression modules */ /* Master control module */ @@ -147,8 +154,8 @@ struct jpeg_input_controller { /* Main buffer control (downsampled-data buffer) */ struct jpeg_d_main_controller { JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); - JMETHOD(void, process_data, - (j_decompress_ptr cinfo, JSAMPARRAY output_buf, JDIMENSION * out_row_ctr, JDIMENSION out_rows_avail)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION * out_row_ctr, JDIMENSION out_rows_avail)); }; /* Coefficient buffer control */ @@ -164,11 +171,11 @@ struct jpeg_d_coef_controller { /* Decompression postprocessing (color quantization buffer control) */ struct jpeg_d_post_controller { JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); - JMETHOD(void, post_process_data, - (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION * in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, - JDIMENSION * out_row_ctr, JDIMENSION out_rows_avail)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION * in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION * out_row_ctr, JDIMENSION out_rows_avail)); }; /* Marker reading & parsing */ @@ -195,10 +202,6 @@ struct jpeg_marker_reader { struct jpeg_entropy_decoder { JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, JBLOCKROW * MCU_data)); - - /* This is here to share code between baseline and progressive decoders; */ - /* other modules probably should not use it */ - boolean insufficient_data; /* set TRUE after emitting warning */ }; /* Inverse DCT (also performs dequantization) */ @@ -228,19 +231,20 @@ struct jpeg_upsampler { struct jpeg_color_deconverter { JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); JMETHOD(void, color_convert, (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows)); + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); }; /* Color quantization or color precision reduction */ struct jpeg_color_quantizer { JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); - JMETHOD(void, color_quantize, - (j_decompress_ptr cinfo, JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)); JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); }; + /* Miscellaneous useful macros */ #undef MAX @@ -248,6 +252,7 @@ struct jpeg_color_quantizer { #undef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) + /* We assume that right shift corresponds to signed division by 2 with * rounding towards minus infinity. This is correct for typical "arithmetic * shift" instructions that shift in copies of the sign bit. But some @@ -269,6 +274,7 @@ struct jpeg_color_quantizer { #define RIGHT_SHIFT(x,shft) ((x) >> (shft)) #endif + /* Short forms of external names for systems with brain-damaged linkers. */ #ifdef NEED_SHORT_EXTERNAL_NAMES @@ -281,7 +287,7 @@ struct jpeg_color_quantizer { #define jinit_downsampler jIDownsampler #define jinit_forward_dct jIFDCT #define jinit_huff_encoder jIHEncoder -#define jinit_phuff_encoder jIPHEncoder +#define jinit_arith_encoder jIAEncoder #define jinit_marker_writer jIMWriter #define jinit_master_decompress jIDMaster #define jinit_d_main_controller jIDMainC @@ -290,7 +296,7 @@ struct jpeg_color_quantizer { #define jinit_input_controller jIInCtlr #define jinit_marker_reader jIMReader #define jinit_huff_decoder jIHDecoder -#define jinit_phuff_decoder jIPHDecoder +#define jinit_arith_decoder jIADecoder #define jinit_inverse_dct jIIDCT #define jinit_upsampler jIUpsampler #define jinit_color_deconverter jIDColor @@ -305,16 +311,27 @@ struct jpeg_color_quantizer { #define jzero_far jZeroFar #define jpeg_zigzag_order jZIGTable #define jpeg_natural_order jZAGTable +#define jpeg_natural_order7 jZAGTable7 +#define jpeg_natural_order6 jZAGTable6 +#define jpeg_natural_order5 jZAGTable5 +#define jpeg_natural_order4 jZAGTable4 +#define jpeg_natural_order3 jZAGTable3 +#define jpeg_natural_order2 jZAGTable2 +#define jpeg_aritab jAriTab #endif /* NEED_SHORT_EXTERNAL_NAMES */ + /* Compression module initialization routines */ EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, boolean transcode_only)); -EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_c_main_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_c_prep_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_c_coef_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); EXTERN(void) @@ -324,15 +341,18 @@ jinit_forward_dct JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); EXTERN(void) -jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +jinit_arith_encoder JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); /* Decompression module initialization routines */ EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_d_main_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_d_coef_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); +EXTERN(void) +jinit_d_post_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); EXTERN(void) @@ -340,7 +360,7 @@ jinit_marker_reader JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); EXTERN(void) -jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +jinit_arith_decoder JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); EXTERN(void) @@ -365,7 +385,8 @@ jround_up JPP((long a, long b)); EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, JSAMPARRAY output_array, int dest_row, int num_rows, JDIMENSION num_cols)); -EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks)); +EXTERN(void) +jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks)); EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); /* Constant tables in jutils.c */ @@ -373,6 +394,15 @@ jzero_far JPP((void FAR * target, size_t bytestozero)); extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ #endif extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ +extern const int jpeg_natural_order7[]; /* zz to natural order for 7x7 block */ +extern const int jpeg_natural_order6[]; /* zz to natural order for 6x6 block */ +extern const int jpeg_natural_order5[]; /* zz to natural order for 5x5 block */ +extern const int jpeg_natural_order4[]; /* zz to natural order for 4x4 block */ +extern const int jpeg_natural_order3[]; /* zz to natural order for 3x3 block */ +extern const int jpeg_natural_order2[]; /* zz to natural order for 2x2 block */ + +/* Arithmetic coding probability estimation tables in jaricom.c */ +extern const INT32 jpeg_aritab[]; /* Suppress undefined-structure complaints if necessary. */ diff --git a/src/transupp.c b/src/transupp.c index 162cc3f..7a114be 100644 --- a/src/transupp.c +++ b/src/transupp.c @@ -1,7 +1,7 @@ /* * transupp.c * - * Copyright (C) 1997, Thomas G. Lane. + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -27,12 +27,13 @@ #define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) #define SIZEOF(object) ((size_t) sizeof(object)) #define JFREAD(file,buf,sizeofbuf) \ - ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) #define JFWRITE(file,buf,sizeofbuf) \ - ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) - + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) #include "jpeglib.h" #include "transupp.h" /* My own external interface */ +#include <ctype.h> /* to declare isdigit() */ + #if TRANSFORMS_SUPPORTED @@ -40,7 +41,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 @@ -54,6 +56,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 @@ -66,17 +75,25 @@ * 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 */ +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; @@ -88,16 +105,18 @@ LOCAL(void) do_flip_h(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, jvirt_ba * 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->output_width / (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); 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); + ((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]; @@ -113,17 +132,31 @@ LOCAL(void) do_flip_h(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, jvirt_ba *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_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; @@ -137,33 +170,35 @@ do_flip_v(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, * 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->output_height / (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); 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) { + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + 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 - + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + 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, (JDIMENSION) compptr->v_samp_factor, FALSE); + ((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]; src_ptr = src_row_ptr[dst_blk_x]; @@ -178,20 +213,22 @@ do_flip_v(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } else { /* Just copy row verbatim. */ - jcopy_block_row(src_buffer - [offset_y], dst_buffer[offset_y], compptr->width_in_blocks); + jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, + dst_buffer[offset_y], compptr->width_in_blocks); } } } } } + 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; @@ -204,25 +241,24 @@ do_transpose(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ 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); + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); 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) { + 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); + ((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]; + 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]; + dst_ptr[j * DCTSIZE + i] = src_ptr[i * DCTSIZE + j]; } } } @@ -230,8 +266,10 @@ do_transpose(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } + 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 * 1. Transposing the image; @@ -240,6 +278,7 @@ do_rot_90(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ { 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; @@ -249,45 +288,57 @@ do_rot_90(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, * 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->output_height / (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); 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); + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); 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); + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; i++; for (j = 0; j < DCTSIZE; j++) - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; } } 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; } } } @@ -296,8 +347,10 @@ do_rot_90(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } + 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 * 1. Horizontal mirroring; @@ -306,6 +359,7 @@ do_rot_270(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ { 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; @@ -315,45 +369,46 @@ do_rot_270(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, * 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->output_width / (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); 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); + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); 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) { + 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); + ((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) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; j++; - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; } } } else { /* Edge blocks are transposed but not mirrored. */ src_ptr = src_buffer[offset_x] - [dst_blk_y + offset_y]; + [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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; } } } @@ -362,8 +417,10 @@ do_rot_270(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } + 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 * 1. Vertical mirroring; @@ -372,97 +429,101 @@ do_rot_180(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ { 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->output_width / (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_height / (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); 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) { + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + 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 - + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + 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, (JDIMENSION) compptr->v_samp_factor, FALSE); + ((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++; - } } } } } } + 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 * 1. 180 degree rotation; @@ -475,91 +536,105 @@ do_transverse(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ { 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->output_height / (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_width / (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); 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); + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); 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); + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; j++; - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; } i++; for (j = 0; j < DCTSIZE; j++) { - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; j++; - dst_ptr[j * DCTSIZE + i] - = src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; } } } 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; j++; - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; } } } } 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; i++; for (j = 0; j < DCTSIZE; j++) - dst_ptr[j * DCTSIZE + i] - = -src_ptr[i * DCTSIZE + j]; + dst_ptr[j * DCTSIZE + i] = + -src_ptr[i * DCTSIZE + j]; } } 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]; + dst_ptr[j * DCTSIZE + i] = + src_ptr[i * DCTSIZE + j]; } } } @@ -569,8 +644,114 @@ do_transverse(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } + +/* 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->iMCU_sample_width; + if (MCU_cols > 0 && info->x_crop_offset + MCU_cols == full_width / info->iMCU_sample_width) + info->output_width = MCU_cols * info->iMCU_sample_width; +} + +LOCAL(void) trim_bottom_edge(jpeg_transform_info * info, JDIMENSION full_height) +{ + JDIMENSION MCU_rows; + + MCU_rows = info->output_height / info->iMCU_sample_height; + if (MCU_rows > 0 && info->y_crop_offset + MCU_rows == full_height / info->iMCU_sample_height) + info->output_height = MCU_rows * info->iMCU_sample_height; +} + + /* 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. @@ -579,87 +760,221 @@ do_transverse(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, * the source's virtual arrays). */ -GLOBAL(void) jtransform_request_workspace(j_decompress_ptr srcinfo, jpeg_transform_info * info) +GLOBAL(boolean) + jtransform_request_workspace(j_decompress_ptr srcinfo, jpeg_transform_info * info) { - jvirt_barray_ptr *coef_arrays = NULL; + jvirt_barray_ptr *coef_arrays; + boolean need_workspace, transpose_it; jpeg_component_info *compptr; - int ci; + JDIMENSION xoffset, yoffset; + JDIMENSION width_in_iMCUs, height_in_iMCUs; + JDIMENSION width_in_blocks, height_in_blocks; + int ci, h_samp_factor, v_samp_factor; - if (info->force_grayscale && srcinfo->jpeg_color_space == JCS_YCbCr && srcinfo->num_components == 3) { + /* Determine number of components in output image */ + if (info->force_grayscale && srcinfo->jpeg_color_space == JCS_YCbCr && srcinfo->num_components == 3) /* We'll only process the first component */ info->num_components = 1; - } else { + else /* Process all the components */ info->num_components = srcinfo->num_components; + + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(srcinfo); + + /* 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->output_height; + info->output_height = srcinfo->output_width; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_h_scaled_size; + } else { + info->iMCU_sample_width = srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + } + break; + default: + info->output_width = srcinfo->output_width; + info->output_height = srcinfo->output_height; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_v_scaled_size; + } else { + info->iMCU_sample_width = srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + } + 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->iMCU_sample_width); + info->output_height = info->crop_height + (yoffset % info->iMCU_sample_height); + /* Save x/y offsets measured in iMCUs */ + info->x_crop_offset = xoffset / info->iMCU_sample_width; + info->y_crop_offset = yoffset / info->iMCU_sample_height; + } 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->output_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->output_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->output_height); + trim_bottom_edge(info, srcinfo->output_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->output_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->output_width); + trim_bottom_edge(info, srcinfo->output_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->output_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); + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + width_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_width, (long) info->iMCU_sample_width); + height_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_height, (long) info->iMCU_sample_height); 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); + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); } - break; - } - info->workspace_coef_arrays = coef_arrays; + info->workspace_coef_arrays = coef_arrays; + } else + info->workspace_coef_arrays = NULL; + + return TRUE; } + /* Transpose destination image parameters */ -LOCAL(void) transpose_critical_parameters(j_compress_ptr dstinfo) +LOCAL(void) + transpose_critical_parameters(j_compress_ptr dstinfo) { int tblno, i, j, ci, itemp; jpeg_component_info *compptr; JQUANT_TBL *qtblptr; - JDIMENSION dtemp; + JDIMENSION jtemp; UINT16 qtemp; - /* Transpose basic image dimensions */ - dtemp = dstinfo->image_width; + /* Transpose image dimensions */ + jtemp = dstinfo->image_width; dstinfo->image_width = dstinfo->image_height; - dstinfo->image_height = dtemp; + dstinfo->image_height = jtemp; + itemp = dstinfo->min_DCT_h_scaled_size; + dstinfo->min_DCT_h_scaled_size = dstinfo->min_DCT_v_scaled_size; + dstinfo->min_DCT_v_scaled_size = itemp; /* Transpose sampling factors */ for (ci = 0; ci < dstinfo->num_components; ci++) { @@ -684,156 +999,6 @@ LOCAL(void) transpose_critical_parameters(j_compress_ptr dstinfo) } } -/* Trim off any partial iMCUs on the indicated destination edge */ - -LOCAL(void) trim_right_edge(j_compress_ptr dstinfo) -{ - int ci, max_h_samp_factor; - JDIMENSION MCU_cols; - - /* 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); - } - 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); -} - -LOCAL(void) trim_bottom_edge(j_compress_ptr dstinfo) -{ - int ci, max_v_samp_factor; - JDIMENSION MCU_rows; - - /* 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); - } - 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); -} - -LOCAL(void) set_exif_orientation(JOCTET FAR * data, unsigned int length, unsigned char new_orient) -{ - boolean is_motorola; /* Flag for byte order */ - unsigned int number_of_tags, tagnum; - unsigned int firstoffset, offset; - - 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; - } - - /* 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]); - } - if (firstoffset > length - 2) - return; /* check end of data segment */ - - /* 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; - - /* Search for Orientation 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 == 0x0112) - break; /* found Orientation Tag */ - if (--number_of_tags == 0) - return; - firstoffset += 12; - } - - if (is_motorola) { - data[firstoffset + 2] = 0; /* Format = unsigned short (2 octets) */ - data[firstoffset + 3] = 3; - data[firstoffset + 4] = 0; /* Number Of Components = 1 */ - data[firstoffset + 5] = 0; - data[firstoffset + 6] = 0; - data[firstoffset + 7] = 1; - data[firstoffset + 8] = 0; - data[firstoffset + 9] = (unsigned char) new_orient; - data[firstoffset + 10] = 0; - data[firstoffset + 11] = 0; - } else { - data[firstoffset + 2] = 3; /* Format = unsigned short (2 octets) */ - data[firstoffset + 3] = 0; - data[firstoffset + 4] = 1; /* Number Of Components = 1 */ - data[firstoffset + 5] = 0; - data[firstoffset + 6] = 0; - data[firstoffset + 7] = 0; - data[firstoffset + 8] = (unsigned char) new_orient; - data[firstoffset + 9] = 0; - data[firstoffset + 10] = 0; - data[firstoffset + 11] = 0; - } -} /* Adjust Exif image parameters. * @@ -845,7 +1010,6 @@ LOCAL(void) adjust_exif_parameters(JOCTET FAR * data, unsigned int length, JDIME boolean is_motorola; /* Flag for byte order */ unsigned int number_of_tags, tagnum; unsigned int firstoffset, offset; - unsigned int new_orient; JDIMENSION new_value; if (length < 12) @@ -978,11 +1142,10 @@ LOCAL(void) adjust_exif_parameters(JOCTET FAR * data, unsigned int length, JDIME tagnum += GETJOCTET(data[offset]); } if (tagnum == 0xA002 || tagnum == 0xA003) { - if (tagnum == 0xA002) { + if (tagnum == 0xA002) new_value = new_width; /* ExifImageWidth Tag */ - } else { + else new_value = new_height; /* ExifImageHeight Tag */ - } if (is_motorola) { data[offset + 2] = 0; /* Format = unsigned long (4 octets) */ data[offset + 3] = 4; @@ -1011,6 +1174,7 @@ LOCAL(void) adjust_exif_parameters(JOCTET FAR * data, unsigned int length, JDIME } while (--number_of_tags); } + /* Adjust output image parameters as needed. * * This must be called after jpeg_copy_critical_parameters() @@ -1026,20 +1190,24 @@ GLOBAL(jvirt_barray_ptr *) jtransform_adjust_parameters(j_decompress_ptr srcinfo, j_compress_ptr dstinfo, jvirt_barray_ptr * src_coef_arrays, jpeg_transform_info * info) { - jpeg_saved_marker_ptr marker; /* 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; @@ -1047,79 +1215,50 @@ GLOBAL(jvirt_barray_ptr *) /* 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 rotate/flip, resize, and crop operations. + */ + dstinfo->jpeg_width = info->output_width; + dstinfo->jpeg_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; + default: break; } - for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { - if (marker->marker != JPEG_APP0 + 1) - continue; - /* Adjust Exif properties */ - if (marker->marker == JPEG_APP0 + 1 && - marker->data_length >= 6 && - GETJOCTET(marker->data[0]) == 0x45 && - GETJOCTET(marker->data[1]) == 0x78 && - GETJOCTET(marker->data[2]) == 0x69 && - GETJOCTET(marker->data[3]) == 0x66 && - GETJOCTET(marker->data[4]) == 0 && GETJOCTET(marker->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(marker->data + 6, - marker->data_length - 6, - dstinfo->image_width, dstinfo->image_height); - /* I'm honestly not sure what the right thing to do is here.. The - * existing orientation tag may be incorrect, so making a change based - * on the previous value seems like the wrong thing to do. For now, I'm - * going to assume that the user is always "fixing" the orientation, - * i.e. putting the image the "right way up". In this case, we want to - * set the orientation to "top left". - */ - set_exif_orientation(marker->data + 6, marker->data_length - 6, 1); - } + /* 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->jpeg_width != srcinfo->image_width || dstinfo->jpeg_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->jpeg_width, dstinfo->jpeg_height); } /* Return the appropriate output data set */ @@ -1128,6 +1267,7 @@ GLOBAL(jvirt_barray_ptr *) return src_coef_arrays; } + /* Execute the actual transformation, if any. * * This must be called *after* jpeg_write_coefficients, because it depends @@ -1138,48 +1278,51 @@ GLOBAL(jvirt_barray_ptr *) */ 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: break; case JXFORM_FLIP_H: - do_flip_h(srcinfo, dstinfo, src_coef_arrays); + do_flip_h_no_crop(srcinfo, dstinfo, 0, src_coef_arrays); break; case JXFORM_FLIP_V: - do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + do_flip_v(srcinfo, dstinfo, 0, 0, src_coef_arrays, dst_coef_arrays); break; case JXFORM_TRANSPOSE: - do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + do_transpose(srcinfo, dstinfo, 0, 0, + src_coef_arrays, dst_coef_arrays); break; case JXFORM_TRANSVERSE: - do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + do_transverse(srcinfo, dstinfo, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, src_coef_arrays, dst_coef_arrays); break; } } #endif /* TRANSFORMS_SUPPORTED */ + /* Setup decompression object to save desired markers in memory. * This must be called before jpeg_read_header() to have the desired effect. */ -GLOBAL(void) jcopy_markers_setup(j_decompress_ptr srcinfo, JCOPY_OPTION option) +GLOBAL(void) + jcopy_markers_setup(j_decompress_ptr srcinfo, JCOPY_OPTION option) { #ifdef SAVE_MARKERS_SUPPORTED int m; @@ -1213,13 +1356,13 @@ GLOBAL(void) jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dsti * if the encoder library already wrote one. */ for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { - if (dstinfo->write_JFIF_header - && marker->marker == JPEG_APP0 - && marker->data_length >= 5 - && GETJOCTET(marker->data[0]) == 0x4A - && GETJOCTET(marker->data[1]) == 0x46 - && GETJOCTET(marker->data[2]) == 0x49 - && GETJOCTET(marker->data[3]) == 0x46 && GETJOCTET(marker->data[4]) == 0) + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && GETJOCTET(marker->data[4]) == 0) continue; /* reject duplicate JFIF */ if (dstinfo->write_Adobe_marker && marker->marker == JPEG_APP0 + 14 && diff --git a/src/transupp.h b/src/transupp.h index 056a282..4f01d29 100644 --- a/src/transupp.h +++ b/src/transupp.h @@ -1,7 +1,7 @@ /* * transupp.h * - * Copyright (C) 1997, Thomas G. Lane. + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -22,31 +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 @@ -74,6 +49,24 @@ typedef enum { * (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.) + * + * We also provide a lossless-resize option, which is kind of a lossless-crop + * operation in the DCT coefficient block domain - it discards higher-order + * coefficients and losslessly preserves lower-order coefficients of a + * sub-block. + * + * Rotate/flip transform, resize, and crop can be requested together in a + * single invocation. The crop is applied last --- that is, the crop region + * is specified in terms of the destination image after transform/resize. + * * 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 @@ -82,30 +75,107 @@ typedef enum { * 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 iMCU_sample_width; /* destination iMCU size */ + int iMCU_sample_height; } 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)); +EXTERN(boolean) jtransform_request_workspace JPP((j_decompress_ptr srcinfo, jpeg_transform_info * info)); /* Adjust output image parameters */ EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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)); +/* 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 */ + /* * Support for copying optional markers from source to destination file. */ @@ -119,7 +189,6 @@ typedef enum { #define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ /* Setup decompression object to save desired markers in memory */ -EXTERN(void) -jcopy_markers_setup JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +EXTERN(void) jcopy_markers_setup JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); /* Copy markers saved in the given source object to the destination object */ EXTERN(void) jcopy_markers_execute JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, JCOPY_OPTION option)); |