/*
 * 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.
 */

/* 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

#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>

#include <string.h>
#define MEMZERO(target,size)  memset((void *)(target), 0, (size_t)(size))
#define MEMCOPY(dest,src,size)  memcpy((void *)(dest), (const void *)(src), (size_t)(size))
#define SIZEOF(object)  ((size_t) sizeof(object))
#define JFREAD(file,buf,sizeofbuf)  \
  ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
#define JFWRITE(file,buf,sizeofbuf)  \
    ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))

#include "jpeglib.h"
#include "transupp.h"		/* My own external interface */

#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);
}

LOCAL(void) set_exif_orientation(JOCTET FAR * data, unsigned int length, unsigned char new_orient)
{
	boolean is_motorola;	/* Flag for byte order */
	unsigned int number_of_tags, tagnum;
	unsigned int firstoffset, offset;

	if (length < 12)
		return;		/* Length of an IFD entry */

	/* Discover byte order */
	if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49)
		is_motorola = FALSE;
	else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D)
		is_motorola = TRUE;
	else
		return;

	/* Check Tag Mark */
	if (is_motorola) {
		if (GETJOCTET(data[2]) != 0)
			return;
		if (GETJOCTET(data[3]) != 0x2A)
			return;
	} else {
		if (GETJOCTET(data[3]) != 0)
			return;
		if (GETJOCTET(data[2]) != 0x2A)
			return;
	}

	/* Get first IFD offset (offset to IFD0) */
	if (is_motorola) {
		if (GETJOCTET(data[4]) != 0)
			return;
		if (GETJOCTET(data[5]) != 0)
			return;
		firstoffset = GETJOCTET(data[6]);
		firstoffset <<= 8;
		firstoffset += GETJOCTET(data[7]);
	} else {
		if (GETJOCTET(data[7]) != 0)
			return;
		if (GETJOCTET(data[6]) != 0)
			return;
		firstoffset = GETJOCTET(data[5]);
		firstoffset <<= 8;
		firstoffset += GETJOCTET(data[4]);
	}
	if (firstoffset > length - 2)
		return;		/* check end of data segment */

	/* Get the number of directory entries contained in this IFD */
	if (is_motorola) {
		number_of_tags = GETJOCTET(data[firstoffset]);
		number_of_tags <<= 8;
		number_of_tags += GETJOCTET(data[firstoffset + 1]);
	} else {
		number_of_tags = GETJOCTET(data[firstoffset + 1]);
		number_of_tags <<= 8;
		number_of_tags += GETJOCTET(data[firstoffset]);
	}
	if (number_of_tags == 0)
		return;
	firstoffset += 2;

	/* Search for Orientation offset Tag in IFD0 */
	for (;;) {
		if (firstoffset > length - 12)
			return;	/* check end of data segment */
		/* Get Tag number */
		if (is_motorola) {
			tagnum = GETJOCTET(data[firstoffset]);
			tagnum <<= 8;
			tagnum += GETJOCTET(data[firstoffset + 1]);
		} else {
			tagnum = GETJOCTET(data[firstoffset + 1]);
			tagnum <<= 8;
			tagnum += GETJOCTET(data[firstoffset]);
		}
		if (tagnum == 0x0112)
			break;	/* found Orientation Tag */
		if (--number_of_tags == 0)
			return;
		firstoffset += 12;
	}

	if (is_motorola) {
		data[firstoffset + 2] = 0;	/* Format = unsigned short (2 octets) */
		data[firstoffset + 3] = 3;
		data[firstoffset + 4] = 0;	/* Number Of Components = 1 */
		data[firstoffset + 5] = 0;
		data[firstoffset + 6] = 0;
		data[firstoffset + 7] = 1;
		data[firstoffset + 8] = 0;
		data[firstoffset + 9] = (unsigned char) new_orient;
		data[firstoffset + 10] = 0;
		data[firstoffset + 11] = 0;
	} else {
		data[firstoffset + 2] = 3;	/* Format = unsigned short (2 octets) */
		data[firstoffset + 3] = 0;
		data[firstoffset + 4] = 1;	/* Number Of Components = 1 */
		data[firstoffset + 5] = 0;
		data[firstoffset + 6] = 0;
		data[firstoffset + 7] = 0;
		data[firstoffset + 8] = (unsigned char) new_orient;
		data[firstoffset + 9] = 0;
		data[firstoffset + 10] = 0;
		data[firstoffset + 11] = 0;
	}
}

/* Adjust Exif image parameters.
 *
 * 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;
	unsigned int new_orient;
	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)
{
	jpeg_saved_marker_ptr marker;
	/* If force-to-grayscale is requested, adjust destination parameters */
	if (info->force_grayscale) {
		/* We use jpeg_set_colorspace to make sure subsidiary settings get fixed
		 * properly.  Among other things, the target h_samp_factor & v_samp_factor
		 * will get set to 1, which typically won't match the source.
		 * In fact we do this even if the source is already grayscale; that
		 * provides an easy way of coercing a grayscale JPEG with funny sampling
		 * factors to the customary 1,1.  (Some decoders fail on other factors.)
		 */
		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;
	}

	for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
		if (marker->marker != JPEG_APP0 + 1)
			continue;
		/* Adjust Exif properties */
		if (marker->marker == JPEG_APP0 + 1 &&
		    marker->data_length >= 6 &&
		    GETJOCTET(marker->data[0]) == 0x45 &&
		    GETJOCTET(marker->data[1]) == 0x78 &&
		    GETJOCTET(marker->data[2]) == 0x69 &&
		    GETJOCTET(marker->data[3]) == 0x66 &&
		    GETJOCTET(marker->data[4]) == 0 && GETJOCTET(marker->data[5]) == 0) {
			/* Suppress output of JFIF marker */
			dstinfo->write_JFIF_header = FALSE;
			/* Adjust Exif image parameters */
			if (dstinfo->image_width != srcinfo->image_width ||
			    dstinfo->image_height != srcinfo->image_height)
				/* Align data segment to start of TIFF structure for parsing */
				adjust_exif_parameters(marker->data + 6,
						       marker->data_length - 6,
						       dstinfo->image_width, dstinfo->image_height);
			/* I'm honestly not sure what the right thing to do is here.. The
			 * existing orientation tag may be incorrect, so making a change based
			 * on the previous value seems like the wrong thing to do. For now, I'm
			 * going to assume that the user is always "fixing" the orientation,
			 * i.e. putting the image the "right way up". In this case, we want to
			 * set the orientation to "top left".
			 */
			set_exif_orientation(marker->data + 6, marker->data_length - 6, 1);
		}
	}

	/* 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]);
		}
#else
		jpeg_write_marker(dstinfo, marker->marker, marker->data, marker->data_length);
#endif
	}
}