Index: jpeglossless/transupp.v7.cpp =================================================================== --- jpeglossless/transupp.v7.cpp (Revision 0) +++ jpeglossless/transupp.v7.cpp (Revision 1115335) @@ -0,0 +1,1552 @@ +/* + * transupp.c + * + * 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. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +// LibJPEG includes + +extern "C" +{ +#include "jinclude.h" +#include "jpeglib.h" +} + +// Local includes + +#include "transupp.h" /* My own external interface */ +#include /* to declare isdigit() */ + +namespace KIPIJPEGLossLessPlugin +{ + +#if TRANSFORMS_SUPPORTED + +/* + * 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, + * 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 + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * 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 + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * 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. 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. + */ + + +LOCAL(void) +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, x_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + /* Do the mirroring */ + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + if (x_crop_blocks > 0) { + /* Now left-justify the portion of the data to be kept. + * We can't use a single jcopy_block_row() call because that routine + * depends on memcpy(), whose behavior is unspecified for overlapping + * source and destination areas. Sigh. + */ + for (blk_x = 0; blk_x < compptr->width_in_blocks; blk_x++) { + jcopy_block_row(buffer[offset_y] + blk_x + x_crop_blocks, + buffer[offset_y] + blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Horizontal flip in general cropping case */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Here we must output into a separate array because we can't touch + * different rows of a single virtual array simultaneously. Otherwise, + * this is essentially the same as the routine above. + */ + MCU_cols = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Do the mirrorable blocks */ + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + *dst_ptr++ = *src_ptr++; /* copy even column */ + *dst_ptr++ = - *src_ptr++; /* copy odd column with sign change */ + } + } else { + /* Copy last partial block(s) verbatim */ + jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, + dst_row_ptr + dst_blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +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; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = srcinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (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 - 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 + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; 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]; + 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]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + 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, 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; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + 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); + 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 + 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]; + 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]; + } + } + } + } + } +} + + +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; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + 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; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = srcinfo->image_height / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((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) { + 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++) { + 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. */ + 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]; + i++; + for (j = 0; j < 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 + 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]; + } + } + } + } + } + } +} + + +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; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + 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; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = srcinfo->image_width / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((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 + 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 (y_crop_blocks + dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + 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]; + 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 + 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]; + } + } + } + } + } + } +} + + +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; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + 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 = srcinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = srcinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (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 - 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 + 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]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + 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++; + } + } + } 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++; + } + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + 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) { + /* 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); + } + } + } + } + } + } +} + + +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; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + 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 = srcinfo->image_height / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = srcinfo->image_width / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((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) { + 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++) { + 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. */ + 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]; + 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]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + 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]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + 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]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + 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]; + } + } + } + } + } + } + } +} + + +/* 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 + * x{+-}{+-} + * 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. + * 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). + */ + +// NB: changed from transupp.c in libjpeg +GLOBAL(boolean) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + boolean need_workspace, transpose_it; + jpeg_component_info *compptr; + JDIMENSION xoffset, yoffset, width_in_iMCUs, height_in_iMCUs; + JDIMENSION width_in_blocks, height_in_blocks; + int ci, h_samp_factor, v_samp_factor; + + /* Determine number of components in output image */ + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + /* If there is only one output component, force the iMCU size to be 1; + * else use the source iMCU size. (This allows us to do the right thing + * when reducing color to grayscale, and also provides a handy way of + * cleaning up "funny" grayscale images whose sampling factors are not 1x1.) + */ + + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + info->output_width = srcinfo->image_height; + info->output_height = srcinfo->image_width; + if (info->num_components == 1) { + info->iMCU_sample_width = DCTSIZE; + info->iMCU_sample_height = DCTSIZE; + } else { + info->iMCU_sample_width = + srcinfo->max_v_samp_factor * DCTSIZE; + info->iMCU_sample_height = + srcinfo->max_h_samp_factor * DCTSIZE; + } + break; + default: + info->output_width = srcinfo->image_width; + info->output_height = srcinfo->image_height; + if (info->num_components == 1) { + info->iMCU_sample_width = DCTSIZE; + info->iMCU_sample_height = DCTSIZE; + } else { + info->iMCU_sample_width = + srcinfo->max_h_samp_factor * DCTSIZE; + info->iMCU_sample_height = + srcinfo->max_v_samp_factor * DCTSIZE; + } + 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: + if (info->trim) + trim_right_edge(info, srcinfo->image_width); + if (info->y_crop_offset != 0) + need_workspace = TRUE; + /* do_flip_h_no_crop doesn't need a workspace array */ + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(info, srcinfo->image_height); + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_TRANSPOSE: + /* transpose does NOT have to trim anything */ + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_TRANSVERSE: + if (info->trim) { + trim_right_edge(info, srcinfo->image_height); + trim_bottom_edge(info, srcinfo->image_width); + } + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_90: + if (info->trim) + trim_right_edge(info, srcinfo->image_height); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(info, srcinfo->image_width); + trim_bottom_edge(info, srcinfo->image_height); + } + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_ROT_270: + if (info->trim) + trim_bottom_edge(info, srcinfo->image_width); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + } + + /* Allocate workspace if needed. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + if (need_workspace) { + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + 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, + width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); + } + } + + info->workspace_coef_arrays = coef_arrays; + + return TRUE; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + UINT16 qtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Adjust Exif image parameters. + * + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. + */ + +LOCAL(void) +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, + JDIMENSION new_width, JDIMENSION new_height) +{ + 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; + } + + /* 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 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]); + } + 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); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +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) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* 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)) && + 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; + } else { + /* 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 as necessary + * for crop and rotate/flip operations. + */ + dstinfo->image_width = info->output_width; + dstinfo->image_height = info->output_height; + + /* Transpose destination image parameters */ + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + break; + default: + break; + } + + /* Adjust Exif properties */ + if (srcinfo->marker_list != NULL && + srcinfo->marker_list->marker == JPEG_APP0+1 && + srcinfo->marker_list->data_length >= 6 && + GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && + GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && + GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && + GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && + GETJOCTET(srcinfo->marker_list->data[4]) == 0 && + GETJOCTET(srcinfo->marker_list->data[5]) == 0) { + /* Suppress output of JFIF marker */ + dstinfo->write_JFIF_header = FALSE; + /* Adjust Exif image parameters */ + if (dstinfo->image_width != srcinfo->image_width || + dstinfo->image_height != srcinfo->image_height) + /* Align data segment to start of TIFF structure for parsing */ + adjust_exif_parameters(srcinfo->marker_list->data + 6, + srcinfo->marker_list->data_length - 6, + dstinfo->image_width, dstinfo->image_height); + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +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: + if (info->y_crop_offset != 0) + do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + else + do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset, + src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + 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, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + 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, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + } +} + +/* jtransform_perfect_transform + * + * Determine whether lossless transformation is perfectly + * possible for a specified image and transformation. + * + * Inputs: + * image_width, image_height: source image dimensions. + * MCU_width, MCU_height: pixel dimensions of MCU. + * transform: transformation identifier. + * Parameter sources from initialized jpeg_struct + * (after reading source header): + * image_width = cinfo.image_width + * image_height = cinfo.image_height + * MCU_width = cinfo.max_h_samp_factor * DCTSIZE + * MCU_height = cinfo.max_v_samp_factor * DCTSIZE + * Result: + * TRUE = perfect transformation possible + * FALSE = perfect transformation not possible + * (may use custom action then) + */ + +GLOBAL(boolean) +jtransform_perfect_transform(JDIMENSION image_width, JDIMENSION image_height, + int MCU_width, int MCU_height, + JXFORM_CODE transform) +{ + boolean result = TRUE; /* initialize TRUE */ + + switch (transform) { + case JXFORM_FLIP_H: + case JXFORM_ROT_270: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_90: + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + case JXFORM_TRANSVERSE: + case JXFORM_ROT_180: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + default: + break; + } + + return result; +} + +#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) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * 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) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} + +} // namespace KIPIJPEGLossLessPlugin Index: jpeglossless/transupp.v6b.cpp =================================================================== --- jpeglossless/transupp.v6b.cpp (Revision 0) +++ jpeglossless/transupp.v6b.cpp (Revision 1115335) @@ -0,0 +1,984 @@ +/* + * transupp.c + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +// Local includes + +#include "transupp.h" /* My own external interface */ + +namespace KIPIJPEGLossLessPlugin +{ + +#if TRANSFORMS_SUPPORTED + +/* + * 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. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * 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 + * 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 */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * 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); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * 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++) { + 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]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + 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; + 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; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + 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) { + /* 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, + (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); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (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]; + 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]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + 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); + 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 (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]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + 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); + 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 (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) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < 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]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + 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); + 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 (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) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - 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]; + 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]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + 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); + + 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; + 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) { + /* 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, + (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); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (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++) { + 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++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *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++; + } + } + /* 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, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + 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); + + 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; + 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); + 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 (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) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + 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]; + 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]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + 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) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < 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]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Request any required workspace. + * + * 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). + */ + +// NB: changed from transupp.c in libjpeg +GLOBAL(boolean) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + 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 { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don'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); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + 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. + */ + 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->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); + } + break; + } + info->workspace_coef_arrays = coef_arrays; + + return TRUE; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* 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); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +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) +{ + /* 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.) + */ + 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. */ + 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; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + 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; + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +// NB: changed from transupp.c in libjpeg +GLOBAL(void) +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; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, 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 */ + + +/* 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) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * 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) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} + +} // namespace KIPIJPEGLossLessPlugin Index: jpeglossless/transupp.v8a.cpp =================================================================== --- jpeglossless/transupp.v8a.cpp (Revision 0) +++ jpeglossless/transupp.v8a.cpp (Revision 1115335) @@ -0,0 +1,1596 @@ +/* + * transupp.c + * + * 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. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +// LibJPEG includes + +extern "C" +{ +#include "jinclude.h" +#include "jpeglib.h" +} + +// Local includes + +#include "transupp.h" /* My own external interface */ +#include /* to declare isdigit() */ + +namespace KIPIJPEGLossLessPlugin +{ + +#if TRANSFORMS_SUPPORTED + +/* + * 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, + * 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 + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * 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 + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * 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. 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. + */ + + + +LOCAL(void) +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, x_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + 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]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *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); + } + } + } + } + } +} + + +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; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + 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 (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 - 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 + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; 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]; + 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]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + 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, 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; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + 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); + 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 + 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]; + 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]; + } + } + } + } + } +} + + +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; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + 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; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + 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); + 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) { + 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++) { + 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. */ + 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]; + i++; + for (j = 0; j < 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 + 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]; + } + } + } + } + } + } +} + + +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; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + 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; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + 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); + 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 + 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 (y_crop_blocks + dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + 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]; + 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 + 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]; + } + } + } + } + } + } +} + + +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; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + 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 = 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 (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 - 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 + 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]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + 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++; + } + } + } 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++; + } + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + 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) { + /* 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); + } + } + } + } + } + } +} + + +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; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + 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 = 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); + 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) { + 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++) { + 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. */ + 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]; + 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]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + 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]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + 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]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + 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]; + } + } + } + } + } + } + } +} + + +/* 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 + * x{+-}{+-} + * 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. + * 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(boolean) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays; + boolean need_workspace, transpose_it; + jpeg_component_info *compptr; + 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) + /* We'll only process the first component */ + info->num_components = 1; + else + /* Process all the components */ + info->num_components = srcinfo->num_components; + + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(srcinfo); + + /* 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 */ + 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: + 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: + 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: + 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); + 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, + width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); + } + 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) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION jtemp; + UINT16 qtemp; + + /* Transpose image dimensions */ + jtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + 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++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Adjust Exif image parameters. + * + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. + */ + +LOCAL(void) +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, + JDIMENSION new_width, JDIMENSION new_height) +{ + 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; + } + + /* 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 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]); + } + 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); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +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) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* 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)) && + 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; + } else { + /* 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 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_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + break; + default: + break; + } + + /* Adjust Exif properties */ + if (srcinfo->marker_list != NULL && + srcinfo->marker_list->marker == JPEG_APP0+1 && + srcinfo->marker_list->data_length >= 6 && + GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && + GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && + GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && + GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && + GETJOCTET(srcinfo->marker_list->data[4]) == 0 && + GETJOCTET(srcinfo->marker_list->data[5]) == 0) { + /* Suppress output of JFIF marker */ + dstinfo->write_JFIF_header = FALSE; + /* Adjust Exif image parameters */ + if (dstinfo->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 */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +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: + if (info->y_crop_offset != 0) + do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + else + do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset, + src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + 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, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + 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, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + 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 */ + + +/* 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) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION /*option*/) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * 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) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} + +} // namespace KIPIJPEGLossLessPlugin Index: jpeglossless/transupp.cpp =================================================================== --- jpeglossless/transupp.cpp (Revision 1115334) +++ jpeglossless/transupp.cpp (Revision 1115335) @@ -1,7 +1,7 @@ /* - * transupp.c + * transupp.cpp * - * 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. * @@ -25,916 +25,16 @@ #include "jpeglib.h" } -// Local includes +// transupp.c appears to be tied rather tightly to the library version. +// For example, the one from libjpeg v6b will not work properly with +// libjpeg v8. So include the correct one for the current version of +// libjpeg. -#include "transupp.h" /* My own external interface */ - -namespace KIPIJPEGLossLessPlugin -{ - -#if TRANSFORMS_SUPPORTED - -/* - * 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. - * - * Horizontal flipping is done in-place, using a single top-to-bottom - * pass through the virtual source array. It will thus be much the - * fastest option for images larger than main memory. - * - * The other routines require a set of destination virtual arrays, so they - * need twice as much memory as jpegtran normally does. The destination - * arrays are always written in normal scan order (top to bottom) because - * the virtual array manager expects this. The source arrays will be scanned - * in the corresponding order, which means multiple passes through the source - * arrays for most of the transforms. That could result in much thrashing - * if the image is larger than main memory. - * - * Some notes about the operating environment of the individual transform - * routines: - * 1. Both the source and destination virtual arrays are allocated from the - * source JPEG object, and therefore should be manipulated by calling the - * source's memory manager. - * 2. The destination's component count should be used. It may be smaller - * than the source's when forcing to grayscale. - * 3. Likewise the destination's sampling factors should be used. When - * forcing to grayscale the destination's sampling factors will be all 1, - * 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 - * 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 */ -{ - JDIMENSION MCU_cols, comp_width, blk_x, blk_y; - int ci, k, offset_y; - JBLOCKARRAY buffer; - JCOEFPTR ptr1, ptr2; - JCOEF temp1, temp2; - jpeg_component_info *compptr; - - /* Horizontal mirroring of DCT blocks is accomplished by swapping - * pairs of blocks in-place. Within a DCT block, we perform horizontal - * 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); - - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - comp_width = MCU_cols * 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++) { - 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]; - /* this unrolled loop doesn't need to know which row it's on... */ - for (k = 0; k < DCTSIZE2; k += 2) { - temp1 = *ptr1; /* swap even column */ - temp2 = *ptr2; - *ptr1++ = temp2; - *ptr2++ = temp1; - temp1 = *ptr1; /* swap odd column with sign change */ - temp2 = *ptr2; - *ptr1++ = -temp2; - *ptr2++ = -temp1; - } - } - } - } - } -} - - -LOCAL(void) -do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - 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; - 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; - - /* We output into a separate array because we can't touch different - * rows of the source virtual array simultaneously. Otherwise, this - * is a pretty straightforward analog of horizontal flip. - * Within a DCT block, vertical mirroring is done by changing the signs - * of odd-numbered rows. - * Partial iMCUs at the bottom edge are copied verbatim. - */ - MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); - - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - comp_height = MCU_rows * compptr->v_samp_factor; - 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) { - /* 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, - (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); - } - for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - if (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]; - 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]; - for (i = 0; i < DCTSIZE; i += 2) { - /* copy even row */ - for (j = 0; j < DCTSIZE; j++) - *dst_ptr++ = *src_ptr++; - /* copy odd row with sign change */ - for (j = 0; j < DCTSIZE; j++) - *dst_ptr++ = - *src_ptr++; - } - } - } else { - /* Just copy row verbatim. */ - jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], - compptr->width_in_blocks); - } - } - } - } -} - - -LOCAL(void) -do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jvirt_barray_ptr *dst_coef_arrays) -/* Transpose source into destination */ -{ - JDIMENSION dst_blk_x, dst_blk_y; - int ci, i, j, offset_x, offset_y; - JBLOCKARRAY src_buffer, dst_buffer; - JCOEFPTR src_ptr, dst_ptr; - jpeg_component_info *compptr; - - /* Transposing pixels within a block just requires transposing the - * DCT coefficients. - * Partial iMCUs at the edges require no special treatment; we simply - * process all the available DCT blocks for every component. - */ - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - 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); - 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 (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]; - for (i = 0; i < DCTSIZE; i++) - for (j = 0; j < DCTSIZE; j++) - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - } - } - } - } - } -} - - -LOCAL(void) -do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jvirt_barray_ptr *dst_coef_arrays) -/* 90 degree rotation is equivalent to - * 1. Transposing the image; - * 2. Horizontal mirroring. - * These two steps are merged into a single processing routine. - */ -{ - JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; - int ci, i, j, offset_x, offset_y; - JBLOCKARRAY src_buffer, dst_buffer; - JCOEFPTR src_ptr, dst_ptr; - jpeg_component_info *compptr; - - /* Because of the horizontal mirror step, we can't process partial iMCUs - * at the (output) right edge properly. They just get transposed and - * not mirrored. - */ - MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); - - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - comp_width = MCU_cols * compptr->h_samp_factor; - 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); - 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 (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) { - /* Block is within the mirrorable area. */ - dst_ptr = dst_buffer[offset_y] - [comp_width - dst_blk_x - offset_x - 1]; - for (i = 0; i < DCTSIZE; i++) { - for (j = 0; j < 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]; - } - } else { - /* Edge blocks are transposed but not mirrored. */ - dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; - for (i = 0; i < DCTSIZE; i++) - for (j = 0; j < DCTSIZE; j++) - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - } - } - } - } - } - } -} - - -LOCAL(void) -do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jvirt_barray_ptr *dst_coef_arrays) -/* 270 degree rotation is equivalent to - * 1. Horizontal mirroring; - * 2. Transposing the image. - * These two steps are merged into a single processing routine. - */ -{ - JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; - int ci, i, j, offset_x, offset_y; - JBLOCKARRAY src_buffer, dst_buffer; - JCOEFPTR src_ptr, dst_ptr; - jpeg_component_info *compptr; - - /* Because of the horizontal mirror step, we can't process partial iMCUs - * at the (output) bottom edge properly. They just get transposed and - * not mirrored. - */ - MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); - - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - comp_height = MCU_rows * compptr->v_samp_factor; - 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); - 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 (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) { - /* Block is within the mirrorable area. */ - src_ptr = src_buffer[offset_x] - [comp_height - 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]; - 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]; - for (i = 0; i < DCTSIZE; i++) - for (j = 0; j < DCTSIZE; j++) - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - } - } - } - } - } - } -} - - -LOCAL(void) -do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jvirt_barray_ptr *dst_coef_arrays) -/* 180 degree rotation is equivalent to - * 1. Vertical mirroring; - * 2. Horizontal mirroring. - * These two steps are merged into a single processing routine. - */ -{ - JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; - 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); - - 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; - 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) { - /* 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, - (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); - } - for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - if (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++) { - 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++; - } - /* For odd row, negate every even column. */ - for (j = 0; j < DCTSIZE; j += 2) { - *dst_ptr++ = - *src_ptr++; - *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++; - } - } - /* 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, - jvirt_barray_ptr *src_coef_arrays, - jvirt_barray_ptr *dst_coef_arrays) -/* Transverse transpose is equivalent to - * 1. 180 degree rotation; - * 2. Transposition; - * or - * 1. Horizontal mirroring; - * 2. Transposition; - * 3. Horizontal mirroring. - * These steps are merged into a single processing routine. - */ -{ - JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; - 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); - - 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; - 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); - 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 (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) { - /* Block is within the mirrorable area. */ - dst_ptr = dst_buffer[offset_y] - [comp_width - dst_blk_x - offset_x - 1]; - for (i = 0; i < DCTSIZE; i++) { - for (j = 0; j < DCTSIZE; j++) { - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - 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]; - 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]; - for (i = 0; i < DCTSIZE; i++) { - for (j = 0; j < DCTSIZE; j++) { - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - 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) { - /* Bottom-edge blocks are mirrored in x only */ - dst_ptr = dst_buffer[offset_y] - [comp_width - dst_blk_x - offset_x - 1]; - for (i = 0; i < DCTSIZE; i++) { - for (j = 0; j < 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]; - } - } else { - /* At lower right corner, just transpose, no mirroring */ - dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; - for (i = 0; i < DCTSIZE; i++) - for (j = 0; j < DCTSIZE; j++) - dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; - } - } - } - } - } - } - } -} - - -/* Request any required workspace. - * - * 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). - */ - -GLOBAL(void) -jtransform_request_workspace (j_decompress_ptr srcinfo, - jpeg_transform_info *info) -{ - jvirt_barray_ptr *coef_arrays = NULL; - jpeg_component_info *compptr; - int ci; - - 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 { - /* Process all the components */ - info->num_components = srcinfo->num_components; - } - - switch (info->transform) { - case JXFORM_NONE: - case JXFORM_FLIP_H: - /* Don'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); - } - break; - case JXFORM_TRANSPOSE: - case JXFORM_TRANSVERSE: - case JXFORM_ROT_90: - 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. - */ - 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->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); - } - break; - } - info->workspace_coef_arrays = coef_arrays; -} - - -/* Transpose destination image parameters */ - -LOCAL(void) -transpose_critical_parameters (j_compress_ptr dstinfo) -{ - int tblno, i, j, ci, itemp; - jpeg_component_info *compptr; - JQUANT_TBL *qtblptr; - JDIMENSION dtemp; - UINT16 qtemp; - - /* Transpose basic image dimensions */ - dtemp = dstinfo->image_width; - dstinfo->image_width = dstinfo->image_height; - dstinfo->image_height = dtemp; - - /* Transpose sampling factors */ - for (ci = 0; ci < dstinfo->num_components; ci++) { - compptr = dstinfo->comp_info + ci; - itemp = compptr->h_samp_factor; - compptr->h_samp_factor = compptr->v_samp_factor; - compptr->v_samp_factor = itemp; - } - - /* Transpose quantization tables */ - for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { - qtblptr = dstinfo->quant_tbl_ptrs[tblno]; - if (qtblptr != NULL) { - for (i = 0; i < DCTSIZE; i++) { - for (j = 0; j < i; j++) { - qtemp = qtblptr->quantval[i*DCTSIZE+j]; - qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; - qtblptr->quantval[j*DCTSIZE+i] = qtemp; - } - } - } - } -} - - -/* 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); -} - - -/* Adjust output image parameters as needed. - * - * This must be called after jpeg_copy_critical_parameters() - * and before jpeg_write_coefficients(). - * - * The return value is the set of virtual coefficient arrays to be written - * (either the ones allocated by jtransform_request_workspace, or the - * original source data arrays). The caller will need to pass this value - * to jpeg_write_coefficients(). - */ - -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) -{ - /* 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.) - */ - 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. */ - 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; - } else { - /* Sorry, can't do it */ - ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); - } - } - - /* Correct the destination's image dimensions etc if necessary */ - 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; - } - - /* Return the appropriate output data set */ - if (info->workspace_coef_arrays != NULL) - return info->workspace_coef_arrays; - return src_coef_arrays; -} - - -/* Execute the actual transformation, if any. - * - * This must be called *after* jpeg_write_coefficients, because it depends - * on jpeg_write_coefficients to have computed subsidiary values such as - * the per-component width and height fields in the destination object. - * - * Note that some transformations will modify the source data arrays! - */ - -GLOBAL(void) -jtransform_execute_transformation (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; - - switch (info->transform) { - case JXFORM_NONE: - break; - case JXFORM_FLIP_H: - do_flip_h(srcinfo, dstinfo, src_coef_arrays); - break; - case JXFORM_FLIP_V: - do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); - break; - case JXFORM_TRANSPOSE: - do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); - break; - case JXFORM_TRANSVERSE: - do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); - break; - case JXFORM_ROT_90: - do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); - break; - case JXFORM_ROT_180: - do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); - break; - case JXFORM_ROT_270: - do_rot_270(srcinfo, dstinfo, 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) -{ -#ifdef SAVE_MARKERS_SUPPORTED - int m; - - /* Save comments except under NONE option */ - if (option != JCOPYOPT_NONE) { - jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); - } - /* Save all types of APPn markers iff ALL option */ - if (option == JCOPYOPT_ALL) { - for (m = 0; m < 16; m++) - jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); - } -#endif /* SAVE_MARKERS_SUPPORTED */ -} - -/* Copy markers saved in the given source object to the destination object. - * This should be called just after jpeg_start_compress() or - * jpeg_write_coefficients(). - * Note that those routines will have written the SOI, and also the - * JFIF APP0 or Adobe APP14 markers if selected. - */ - -GLOBAL(void) -jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - JCOPY_OPTION /*option*/) -{ - jpeg_saved_marker_ptr marker; - - /* In the current implementation, we don't actually need to examine the - * option flag here; we just copy everything that got saved. - * But to avoid confusion, we do not output JFIF and Adobe APP14 markers - * 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) - continue; /* reject duplicate JFIF */ - if (dstinfo->write_Adobe_marker && - marker->marker == JPEG_APP0+14 && - marker->data_length >= 5 && - GETJOCTET(marker->data[0]) == 0x41 && - GETJOCTET(marker->data[1]) == 0x64 && - GETJOCTET(marker->data[2]) == 0x6F && - GETJOCTET(marker->data[3]) == 0x62 && - GETJOCTET(marker->data[4]) == 0x65) - continue; /* reject duplicate Adobe */ -#ifdef NEED_FAR_POINTERS - /* We could use jpeg_write_marker if the data weren't FAR... */ - { - unsigned int i; - jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); - for (i = 0; i < marker->data_length; i++) - jpeg_write_m_byte(dstinfo, marker->data[i]); - } +#if JPEG_LIB_VERSION < 70 +#include "transupp.v6b.cpp" +#elif JPEG_LIB_VERSION < 80 +#include "transupp.v7.cpp" #else - jpeg_write_marker(dstinfo, marker->marker, - marker->data, marker->data_length); +#include "transupp.v8a.cpp" #endif - } -} -} // namespace KIPIJPEGLossLessPlugin Index: jpeglossless/jpegtransform.cpp =================================================================== --- jpeglossless/jpegtransform.cpp (Revision 1115334) +++ jpeglossless/jpegtransform.cpp (Revision 1115335) @@ -189,8 +189,10 @@ JCOPY_OPTION copyoption = JCOPYOPT_ALL; jpeg_transform_info transformoption; + transformoption.perfect = false; transformoption.force_grayscale = false; transformoption.trim = false; + transformoption.crop = false; struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; Index: jpeglossless/transupp.h =================================================================== --- jpeglossless/transupp.h (Revision 1115334) +++ jpeglossless/transupp.h (Revision 1115335) @@ -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. * @@ -25,36 +25,10 @@ /* If you happen not to want the image transform support, disable it here */ #ifndef TRANSFORMS_SUPPORTED -#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#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 * whose dimensions aren't multiples of the iMCU size. The right and bottom @@ -81,6 +55,24 @@ * (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 @@ -89,22 +81,94 @@ * 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 +#if 0 +// requires libjpeg v7+, so disabled until we require that version +/* Parse a crop specification (written in X11 geometry style) */ +EXTERN(boolean) jtransform_parse_crop_spec + JPP((jpeg_transform_info *info, const char *spec)); +#endif /* 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 @@ -112,11 +176,25 @@ 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 */