/*

 * 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 "jinclude.h"

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

}





/* 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]);

    }

#else

    jpeg_write_marker(dstinfo, marker->marker,

		      marker->data, marker->data_length);

#endif

  }

}

