| Upstream-Status: Pending |
| |
| Add patch by Ondrej Sury to migrate to libjpeg-turbo (Closes: #763263) |
| |
| Index: fbida-2.10/jpeg/62/jpegcomp.h |
| =================================================================== |
| --- /dev/null |
| +++ fbida-2.10/jpeg/62/jpegcomp.h |
| @@ -0,0 +1,30 @@ |
| +/* |
| + * jpegcomp.h |
| + * |
| + * Copyright (C) 2010, D. R. Commander |
| + * For conditions of distribution and use, see the accompanying README file. |
| + * |
| + * JPEG compatibility macros |
| + * These declarations are considered internal to the JPEG library; most |
| + * applications using the library shouldn't need to include this file. |
| + */ |
| + |
| +#if JPEG_LIB_VERSION >= 70 |
| +#define _DCT_scaled_size DCT_h_scaled_size |
| +#define _DCT_h_scaled_size DCT_h_scaled_size |
| +#define _DCT_v_scaled_size DCT_v_scaled_size |
| +#define _min_DCT_scaled_size min_DCT_h_scaled_size |
| +#define _min_DCT_h_scaled_size min_DCT_h_scaled_size |
| +#define _min_DCT_v_scaled_size min_DCT_v_scaled_size |
| +#define _jpeg_width jpeg_width |
| +#define _jpeg_height jpeg_height |
| +#else |
| +#define _DCT_scaled_size DCT_scaled_size |
| +#define _DCT_h_scaled_size DCT_scaled_size |
| +#define _DCT_v_scaled_size DCT_scaled_size |
| +#define _min_DCT_scaled_size min_DCT_scaled_size |
| +#define _min_DCT_h_scaled_size min_DCT_scaled_size |
| +#define _min_DCT_v_scaled_size min_DCT_scaled_size |
| +#define _jpeg_width image_width |
| +#define _jpeg_height image_height |
| +#endif |
| Index: fbida-2.10/jpeg/62/transupp.c |
| =================================================================== |
| --- fbida-2.10.orig/jpeg/62/transupp.c |
| +++ fbida-2.10/jpeg/62/transupp.c |
| @@ -1,8 +1,10 @@ |
| /* |
| * transupp.c |
| * |
| - * Copyright (C) 1997, Thomas G. Lane. |
| - * This file is part of the Independent JPEG Group's software. |
| + * This file was part of the Independent JPEG Group's software: |
| + * Copyright (C) 1997-2011, Thomas G. Lane, Guido Vollbeding. |
| + * libjpeg-turbo Modifications: |
| + * Copyright (C) 2010, D. R. Commander. |
| * For conditions of distribution and use, see the accompanying README file. |
| * |
| * This file contains image transformation routines and other utility code |
| @@ -20,6 +22,17 @@ |
| #include "jinclude.h" |
| #include "jpeglib.h" |
| #include "transupp.h" /* My own external interface */ |
| +#include "jpegcomp.h" |
| +#include <ctype.h> /* to declare isdigit() */ |
| + |
| + |
| +#if JPEG_LIB_VERSION >= 70 |
| +#define dstinfo_min_DCT_h_scaled_size dstinfo->min_DCT_h_scaled_size |
| +#define dstinfo_min_DCT_v_scaled_size dstinfo->min_DCT_v_scaled_size |
| +#else |
| +#define dstinfo_min_DCT_h_scaled_size DCTSIZE |
| +#define dstinfo_min_DCT_v_scaled_size DCTSIZE |
| +#endif |
| |
| |
| #if TRANSFORMS_SUPPORTED |
| @@ -28,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 |
| @@ -42,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 |
| @@ -54,20 +75,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 +145,20 @@ do_flip_h (j_decompress_ptr srcinfo, j_c |
| * 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); |
| 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 +174,80 @@ do_flip_h (j_decompress_ptr srcinfo, j_c |
| *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->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; |
| + 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 +256,13 @@ do_flip_h (j_decompress_ptr srcinfo, j_c |
| |
| 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 +276,39 @@ do_flip_v (j_decompress_ptr srcinfo, j_c |
| * 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) { |
| + 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 +324,8 @@ do_flip_v (j_decompress_ptr srcinfo, j_c |
| } |
| } 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 +336,12 @@ do_flip_v (j_decompress_ptr srcinfo, j_c |
| |
| 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 +354,8 @@ do_transpose (j_decompress_ptr srcinfo, |
| */ |
| 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 +365,12 @@ do_transpose (j_decompress_ptr srcinfo, |
| 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 +384,7 @@ do_transpose (j_decompress_ptr srcinfo, |
| |
| 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 +394,7 @@ do_rot_90 (j_decompress_ptr srcinfo, j_c |
| */ |
| { |
| 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 +404,14 @@ do_rot_90 (j_decompress_ptr srcinfo, j_c |
| * 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) |
| @@ -259,15 +420,26 @@ do_rot_90 (j_decompress_ptr srcinfo, j_c |
| 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 +449,8 @@ do_rot_90 (j_decompress_ptr srcinfo, j_c |
| } |
| } 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 +465,7 @@ do_rot_90 (j_decompress_ptr srcinfo, j_c |
| |
| 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 +475,7 @@ do_rot_270 (j_decompress_ptr srcinfo, j_ |
| */ |
| { |
| 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 +485,14 @@ do_rot_270 (j_decompress_ptr srcinfo, j_ |
| * 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) |
| @@ -324,14 +502,15 @@ do_rot_270 (j_decompress_ptr srcinfo, j_ |
| 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 +520,8 @@ do_rot_270 (j_decompress_ptr srcinfo, j_ |
| } |
| } 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 +536,7 @@ do_rot_270 (j_decompress_ptr srcinfo, j_ |
| |
| 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 +546,95 @@ do_rot_180 (j_decompress_ptr srcinfo, j_ |
| */ |
| { |
| 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) { |
| + 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 +644,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_ |
| |
| 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 +658,23 @@ do_transverse (j_decompress_ptr srcinfo, |
| */ |
| { |
| 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) |
| @@ -490,17 +683,26 @@ do_transverse (j_decompress_ptr srcinfo, |
| 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 +718,8 @@ do_transverse (j_decompress_ptr srcinfo, |
| } |
| } 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 +729,10 @@ do_transverse (j_decompress_ptr srcinfo, |
| } |
| } |
| } 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 +742,8 @@ do_transverse (j_decompress_ptr srcinfo, |
| } |
| } 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,83 +757,372 @@ do_transverse (j_decompress_ptr srcinfo, |
| } |
| |
| |
| +/* 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>[f]x<height>[f]{+-}<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; |
| + if (*spec == 'f' || *spec == 'F') { |
| + spec++; |
| + info->crop_width_set = JCROP_FORCE; |
| + } else |
| + info->crop_width_set = JCROP_POS; |
| + } |
| + if (*spec == 'x' || *spec == 'X') { |
| + /* fetch height */ |
| + spec++; |
| + if (! jt_read_integer(&spec, &info->crop_height)) |
| + return FALSE; |
| + if (*spec == 'f' || *spec == 'F') { |
| + spec++; |
| + info->crop_height_set = JCROP_FORCE; |
| + } else |
| + 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. |
| * Hence, this routine must be called after jpeg_read_header (which reads |
| * the image dimensions) and before jpeg_read_coefficients (which realizes |
| * the source's virtual arrays). |
| + * |
| + * This function returns FALSE right away if -perfect is given |
| + * and transformation is not perfect. Otherwise returns TRUE. |
| */ |
| |
| -GLOBAL(void) |
| +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; |
| |
| + /* Determine number of components in output image */ |
| if (info->force_grayscale && |
| srcinfo->jpeg_color_space == JCS_YCbCr && |
| - srcinfo->num_components == 3) { |
| + 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. */ |
| +#if JPEG_LIB_VERSION >= 80 |
| + jpeg_core_output_dimensions(srcinfo); |
| +#else |
| + srcinfo->output_width = srcinfo->image_width; |
| + srcinfo->output_height = srcinfo->image_height; |
| +#endif |
| + |
| + /* Return right away if -perfect is given and transformation is not perfect. |
| + */ |
| + if (info->perfect) { |
| + if (info->num_components == 1) { |
| + if (!jtransform_perfect_transform(srcinfo->output_width, |
| + srcinfo->output_height, |
| + srcinfo->_min_DCT_h_scaled_size, |
| + srcinfo->_min_DCT_v_scaled_size, |
| + info->transform)) |
| + return FALSE; |
| + } else { |
| + if (!jtransform_perfect_transform(srcinfo->output_width, |
| + srcinfo->output_height, |
| + srcinfo->max_h_samp_factor * srcinfo->_min_DCT_h_scaled_size, |
| + srcinfo->max_v_samp_factor * srcinfo->_min_DCT_v_scaled_size, |
| + info->transform)) |
| + return FALSE; |
| + } |
| + } |
| + |
| + /* 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 */ |
| + if (info->crop_width_set == JCROP_FORCE) |
| + info->output_width = info->crop_width; |
| + else |
| + info->output_width = |
| + info->crop_width + (xoffset % info->iMCU_sample_width); |
| + if (info->crop_height_set == JCROP_FORCE) |
| + info->output_height = info->crop_height; |
| + else |
| + 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 || info->slow_hflip) |
| + 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); |
| + 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); |
| + 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; |
| } |
| |
| |
| @@ -642,13 +1134,18 @@ transpose_critical_parameters (j_compres |
| 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; |
| +#if JPEG_LIB_VERSION >= 70 |
| + 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; |
| +#endif |
| |
| /* Transpose sampling factors */ |
| for (ci = 0; ci < dstinfo->num_components; ci++) { |
| @@ -674,47 +1171,162 @@ transpose_critical_parameters (j_compres |
| } |
| |
| |
| -/* 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. |
| + */ |
| |
| +#if JPEG_LIB_VERSION >= 70 |
| 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; |
| + } |
| + |
| + /* 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]); |
| } |
| - 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); |
| + 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); |
| } |
| +#endif |
| |
| |
| /* Adjust output image parameters as needed. |
| @@ -736,18 +1348,22 @@ jtransform_adjust_parameters (j_decompre |
| { |
| /* 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 +1371,66 @@ jtransform_adjust_parameters (j_decompre |
| /* 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. |
| + */ |
| +#if JPEG_LIB_VERSION >= 70 |
| + dstinfo->jpeg_width = info->output_width; |
| + dstinfo->jpeg_height = info->output_height; |
| +#endif |
| + |
| + /* 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: |
| +#if JPEG_LIB_VERSION < 70 |
| + dstinfo->image_width = info->output_height; |
| + dstinfo->image_height = info->output_width; |
| +#endif |
| transpose_critical_parameters(dstinfo); |
| - if (info->trim) |
| - trim_bottom_edge(dstinfo); |
| + break; |
| + default: |
| +#if JPEG_LIB_VERSION < 70 |
| + dstinfo->image_width = info->output_width; |
| + dstinfo->image_height = info->output_height; |
| +#endif |
| 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; |
| +#if JPEG_LIB_VERSION >= 70 |
| + /* 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); |
| +#endif |
| + } |
| + |
| /* Return the appropriate output data set */ |
| if (info->workspace_coef_arrays != NULL) |
| return info->workspace_coef_arrays; |
| @@ -816,40 +1448,110 @@ jtransform_adjust_parameters (j_decompre |
| */ |
| |
| 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 || info->slow_hflip) |
| + 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 * cinfo.block_size |
| + * MCU_height = cinfo.max_v_samp_factor * cinfo.block_size |
| + * 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; |
| + default: |
| + break; |
| + } |
| + |
| + return result; |
| +} |
| + |
| #endif /* TRANSFORMS_SUPPORTED */ |
| |
| |
| Index: fbida-2.10/jpeg/62/transupp.h |
| =================================================================== |
| --- fbida-2.10.orig/jpeg/62/transupp.h |
| +++ fbida-2.10/jpeg/62/transupp.h |
| @@ -1,7 +1,7 @@ |
| /* |
| * transupp.h |
| * |
| - * Copyright (C) 1997, Thomas G. Lane. |
| + * Copyright (C) 1997-2011, 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,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,25 @@ 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.) |
| + * The adjustment of the region dimensions may be optionally disabled. |
| + * |
| + * 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 |
| @@ -83,22 +76,100 @@ 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 for xoffset or yoffset, |
| + * positive or forced for width or height. |
| + */ |
| + |
| +typedef enum { |
| + JCROP_UNSET, |
| + JCROP_POS, |
| + JCROP_NEG, |
| + JCROP_FORCE |
| +} 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 */ |
| + boolean slow_hflip; /* For best performance, the JXFORM_FLIP_H transform |
| + normally modifies the source coefficients in place. |
| + Setting this to TRUE will instead use a slower, |
| + double-buffered algorithm, which leaves the source |
| + coefficients in tact (necessary if other transformed |
| + images must be generated from the same set of |
| + coefficients. */ |
| + |
| + /* 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; /* (forced disables adjustment) */ |
| + JDIMENSION crop_height; /* Height of selected region */ |
| + JCROP_CODE crop_height_set; /* (forced disables adjustment) */ |
| + 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 |
| +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 |
| @@ -106,10 +177,24 @@ EXTERN(jvirt_barray_ptr *) jtransform_ad |
| 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 */ |
| |