/*
 * jcmcu.c
 *
 * Copyright (C) 1991, 1992, 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 MCU extraction routines and quantization scaling.
 * These routines are invoked via the extract_MCUs and
 * extract_init/term methods.
 */

#include "jinclude.h"


/*
 * If this file is compiled with -DDCT_ERR_STATS, it will reverse-DCT each
 * block and sum the total errors across the whole picture.  This provides
 * a convenient method of using real picture data to test the roundoff error
 * of a DCT algorithm.  DCT_ERR_STATS should *not* be defined for a production
 * compression program, since compression is much slower with it defined.
 * Also note that jrevdct.o must be linked into the compressor when this
 * switch is defined.
 */

#ifdef DCT_ERR_STATS
static int dcterrorsum;		/* these hold the error statistics */
static int dcterrormax;
static int dctcoefcount;	/* This will probably overflow on a 16-bit-int machine */
#endif


/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */

static const short ZAG[DCTSIZE2] = {
  0,  1,  8, 16,  9,  2,  3, 10,
 17, 24, 32, 25, 18, 11,  4,  5,
 12, 19, 26, 33, 40, 48, 41, 34,
 27, 20, 13,  6,  7, 14, 21, 28,
 35, 42, 49, 56, 57, 50, 43, 36,
 29, 22, 15, 23, 30, 37, 44, 51,
 58, 59, 52, 45, 38, 31, 39, 46,
 53, 60, 61, 54, 47, 55, 62, 63
};


LOCAL void
extract_block (JSAMPARRAY input_data, int start_row, long start_col,
	       JBLOCK output_data, QUANT_TBL_PTR quanttbl)
/* Extract one 8x8 block from the specified location in the sample array; */
/* perform forward DCT, quantization scaling, and zigzag reordering on it. */
{
  /* This routine is heavily used, so it's worth coding it tightly. */
  DCTBLOCK block;
#ifdef DCT_ERR_STATS
  DCTBLOCK svblock;		/* saves input data for comparison */
#endif

  { register JSAMPROW elemptr;
    register DCTELEM *localblkptr = block;
#if DCTSIZE != 8
    register short elemc;
#endif
    register short elemr;

    for (elemr = DCTSIZE; elemr > 0; elemr--) {
      elemptr = input_data[start_row++] + start_col;
#if DCTSIZE == 8		/* unroll the inner loop */
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      *localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
#else
      for (elemc = DCTSIZE; elemc > 0; elemc--) {
	*localblkptr++ = (DCTELEM) GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
      }
#endif
    }
  }

#ifdef DCT_ERR_STATS
  memcpy((void *) svblock, (void *) block, SIZEOF(DCTBLOCK));
#endif

  j_fwd_dct(block);

  { register JCOEF temp;
    register short i;

    for (i = 0; i < DCTSIZE2; i++) {
      temp = (JCOEF) block[ZAG[i]];
      /* divide by *quanttbl, ensuring proper rounding */
      if (temp < 0) {
	temp = -temp;
	temp += *quanttbl>>1;
	temp /= *quanttbl;
	temp = -temp;
      } else {
	temp += *quanttbl>>1;
	temp /= *quanttbl;
      }
      *output_data++ = temp;
      quanttbl++;
    }
  }

#ifdef DCT_ERR_STATS
  j_rev_dct(block);

  { register int diff;
    register short i;

    for (i = 0; i < DCTSIZE2; i++) {
      diff = block[i] - svblock[i];
      if (diff < 0) diff = -diff;
      dcterrorsum += diff;
      if (dcterrormax < diff) dcterrormax = diff;
    }
    dctcoefcount += DCTSIZE2;
  }
#endif
}


/*
 * Extract samples in MCU order, process & hand off to output_method.
 * The input is always exactly N MCU rows worth of data.
 */

METHODDEF void
extract_MCUs (compress_info_ptr cinfo,
	      JSAMPIMAGE image_data,
	      int num_mcu_rows,
	      MCU_output_method_ptr output_method)
{
  JBLOCK MCU_data[MAX_BLOCKS_IN_MCU];
  int mcurow;
  long mcuindex;
  short blkn, ci, xpos, ypos;
  jpeg_component_info * compptr;
  QUANT_TBL_PTR quant_ptr;

  for (mcurow = 0; mcurow < num_mcu_rows; mcurow++) {
    for (mcuindex = 0; mcuindex < cinfo->MCUs_per_row; mcuindex++) {
      /* Extract data from the image array, DCT it, and quantize it */
      blkn = 0;
      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
	compptr = cinfo->cur_comp_info[ci];
	quant_ptr = cinfo->quant_tbl_ptrs[compptr->quant_tbl_no];
	for (ypos = 0; ypos < compptr->MCU_height; ypos++) {
	  for (xpos = 0; xpos < compptr->MCU_width; xpos++) {
	    extract_block(image_data[ci],
			  (mcurow * compptr->MCU_height + ypos)*DCTSIZE,
			  (mcuindex * compptr->MCU_width + xpos)*DCTSIZE,
			  MCU_data[blkn], quant_ptr);
	    blkn++;
	  }
	}
      }
      /* Send the MCU whereever the pipeline controller wants it to go */
      (*output_method) (cinfo, MCU_data);
    }
  }
}


/*
 * Initialize for processing a scan.
 */

METHODDEF void
extract_init (compress_info_ptr cinfo)
{
  /* no work for now */
#ifdef DCT_ERR_STATS
  dcterrorsum = dcterrormax = dctcoefcount = 0;
#endif
}


/*
 * Clean up after a scan.
 */

METHODDEF void
extract_term (compress_info_ptr cinfo)
{
  /* no work for now */
#ifdef DCT_ERR_STATS
  TRACEMS3(cinfo->emethods, 0, "DCT roundoff errors = %d/%d,  max = %d",
	   dcterrorsum, dctcoefcount, dcterrormax);
#endif
}



/*
 * The method selection routine for MCU extraction.
 */

GLOBAL void
jselcmcu (compress_info_ptr cinfo)
{
  /* just one implementation for now */
  cinfo->methods->extract_init = extract_init;
  cinfo->methods->extract_MCUs = extract_MCUs;
  cinfo->methods->extract_term = extract_term;
}
