/*
 * JPEG software copyright:
 *
 * The authors make NO WARRANTY or representation, either express or implied,
 * with respect to this software, its quality, accuracy, merchantability, or
 * fitness for a particular purpose.  This software is provided "AS IS", and you,
 * its user, assume the entire risk as to its quality and accuracy.
 * 
 * This software is copyright (C) 1991, Thomas G. Lane.
 * All Rights Reserved except as specified below.
 * 
 * Permission is hereby granted to use, copy, modify, and distribute this
 * software (or portions thereof) for any purpose, without fee, subject to these
 * conditions:
 * (1) If any part of the source code for this software is distributed, then this
 * README file must be included, with this copyright and no-warranty notice
 * unaltered; and any additions, deletions, or changes to the original files
 * must be clearly indicated in accompanying documentation.
 * (2) If only executable code is distributed, then the accompanying
 * documentation must state that "this software is based in part on the work of
 * the Independent JPEG Group".
 * (3) Permission for use of this software is granted only if the user accepts
 * full responsibility for any undesirable consequences; the authors accept
 * NO LIABILITY for damages of any kind.
 * 
 * Permission is NOT granted for the use of any author's name or author's company
 * name in advertising or publicity relating to this software or products derived
 * from it.  This software may be referred to only as "the Independent JPEG
 * Group's software".
 * 
 * We specifically permit and encourage the use of this software as the basis of
 * commercial products, provided that all warranty or liability claims are
 * assumed by the product vendor.
 *
 */

/* 
 * jpeg - This is a quick hack to give xloadimage the ability to
 * read a JFIF file. The jpeg sofware is based on the first public
 * release of the Independent JPEG Group's free JPEG software.
 * 
 * Graeme Gill 18/11/91
 * 
 */

#undef  DEBUG

#ifdef DEBUG
# define debug(xx)	fprintf(stderr,xx)
#else
# define debug(xx)
#endif

#include "image.h"
#include "jpeg.h"

METHODDEF int read_jpeg_data ();

int jpegIdent(fullname, name)
     char *fullname, *name;
{
	struct decompress_info_struct cinfo;
	struct decompress_methods_struct dc_methods;
	struct external_methods_struct e_methods;
	int retv = 0;

	/* Initialize the system-dependent method pointers. */
	cinfo.methods = &dc_methods;
	cinfo.emethods = &e_methods;
	jselerror(&e_methods);				/* error/trace message routines */
	jselvirtmem(&e_methods);				/* memory allocation routines */
	dc_methods.d_ui_method_selection = jselwxli;
	dc_methods.read_jpeg_data = read_jpeg_data;

	/* Tell methods about input file */
	cinfo.filename = name;
	cinfo.input_file = zopen(fullname);
	cinfo.verbose = 1;
	cinfo.image = NULL;	/* in case we fail */

	/* Allocate memory for input buffer. */
	cinfo.input_buffer = (char *) (*cinfo.emethods->alloc_small)
																				((size_t) (JPEG_BUF_SIZE + MIN_UNGET));
	cinfo.bytes_in_buffer = 0;				/* initialize buffer to empty */
	cinfo.eof_found = FALSE;

	/* Set up default parameters. */
	e_methods.trace_level = 0;
	cinfo.do_block_smoothing = FALSE;
	cinfo.do_pixel_smoothing = FALSE;
	cinfo.out_color_space = CS_RGB;
	cinfo.jpeg_color_space = CS_UNKNOWN;

	/* Set up to read a JFIF or baseline-JPEG file. */
	/* A smarter UI would inspect the first few bytes of the input file */
	/* to determine its type. */
	jselrjfif(&cinfo);

	/* Do it to it! */
	if (cinfo.input_file != NULL)
	{
		if (jpeg_decompress(&cinfo, TRUE))
			retv = 1;
		zclose(cinfo.input_file);
	}

	/* Release memory. */
	(*cinfo.emethods->free_small) ((void *) cinfo.input_buffer);

	/* All done. */
	return retv;
}

Image *jpegLoad(fullname,name,verbose)
     char *fullname,*name;
     unsigned int verbose;
{
	struct decompress_info_struct cinfo;
	struct decompress_methods_struct dc_methods;
	struct external_methods_struct e_methods;

	/* Initialize the system-dependent method pointers. */
	cinfo.methods = &dc_methods;
	cinfo.emethods = &e_methods;
	jselerror(&e_methods);				/* error/trace message routines */
	jselvirtmem(&e_methods);				/* memory allocation routines */
	dc_methods.d_ui_method_selection = jselwxli;
	dc_methods.read_jpeg_data = read_jpeg_data;
	cinfo.eof_found = FALSE;

	/* Tell methods about input file */
	cinfo.filename = name;
	cinfo.input_file = zopen(fullname);
	cinfo.verbose = verbose;
	cinfo.image = NULL;	/* in case we fail */
	cinfo.rows_put = 0;

	/* Allocate memory for input buffer. */
	cinfo.input_buffer = (char *) (*cinfo.emethods->alloc_small)
																				((size_t) (JPEG_BUF_SIZE + MIN_UNGET));
	cinfo.bytes_in_buffer = 0;				/* initialize buffer to empty */

	/* Set up default parameters. */
	e_methods.trace_level = 0;
	cinfo.do_block_smoothing = FALSE;
	cinfo.do_pixel_smoothing = FALSE;
	cinfo.out_color_space = CS_RGB;
	cinfo.jpeg_color_space = CS_UNKNOWN;

	/* Set up to read a JFIF or baseline-JPEG file. */
	/* A smarter UI would inspect the first few bytes of the input file */
	/* to determine its type. */
	jselrjfif(&cinfo);

	/* Do it to it! */
	if (cinfo.input_file != NULL)
	{
		jpeg_decompress(&cinfo, FALSE);
		if (cinfo.image != NULL && cinfo.eof_found)
			fprintf(stderr, "jpeg: Short read data in file");
		zclose(cinfo.input_file);
	}

	/* Release memory. */
	(*cinfo.emethods->free_small) ((void *) cinfo.input_buffer);

	/* All done. */
	return (cinfo.image);
}


/*****************************************/
/* Output stuff                          */
/*****************************************/

METHODDEF void
output_init (cinfo)
decompress_info_ptr cinfo;
{
	int i;
	if (cinfo->out_color_space == CS_GRAYSCALE)
	{
		cinfo->image = newRGBImage(cinfo->image_width,cinfo->image_height,8);
		cinfo->image->title = dupString(cinfo->filename);
		/* set a linear map */
		for(i=0;i<256;i++)
		{
			*(cinfo->image->rgb.red + i) = 
			*(cinfo->image->rgb.green + i) = 
			*(cinfo->image->rgb.blue + i) = i<<8;
		}
		cinfo->image->rgb.used = 256;
	}
	else if (cinfo->out_color_space == CS_RGB)
	{
		cinfo->image = newTrueImage(cinfo->image_width,cinfo->image_height);
		cinfo->image->title = dupString(cinfo->filename);
	}
	else
	{
		cinfo->image = NULL;
		fprintf(stderr, "Can't cope with JPEG image type\n");
	}
}

/*
 * Write some pixel data.
 */

METHODDEF void
put_pixel_rows (cinfo, num_rows, pixel_data)
decompress_info_ptr cinfo;
int num_rows;
JSAMPIMAGE pixel_data;
{
	register unsigned char *bufp;
	register JSAMPROW ptr0, ptr1, ptr2;
	register long col;
	register long width = cinfo->image_width;
	register int row;
	
	/* Assume JSAMPLE == chars */
	if (cinfo->out_color_space == CS_GRAYSCALE)
	{
		bufp = cinfo->image->data + cinfo->rows_put * width;
		for (row = 0; row < num_rows; row++)
		{
			memcpy(bufp, pixel_data[0][row], width);
			bufp += width;
		}
	}
	else
	{
		bufp = cinfo->image->data + cinfo->rows_put * width * 3;
		for (row = 0; row < num_rows; row++)
		{
			ptr0 = pixel_data[0][row];
			ptr1 = pixel_data[1][row];
			ptr2 = pixel_data[2][row];
			for (col = width; col > 0; col--)
			{
				*bufp = *ptr0;
				bufp++; ptr0++;
				*bufp = *ptr1;
				bufp++; ptr1++;
				*bufp = *ptr2;
				bufp++; ptr2++;
			}
		}
	}
	cinfo->rows_put += num_rows;
}

/*
 * Finish up at the end of the file.
 */

METHODDEF void
output_term (cinfo)
decompress_info_ptr cinfo;
{
  zclose(cinfo->input_file);
}

/*
 * The method selection routine for xloadimage internal format output.
 */

GLOBAL void
jselwxli (cinfo)
decompress_info_ptr cinfo;
{
	/* if grayscale or CMYK input, force similar output; */
	/* else leave the output colorspace as set by options. */
	if (cinfo->jpeg_color_space == CS_GRAYSCALE)
		cinfo->out_color_space = CS_GRAYSCALE;
	else if (cinfo->jpeg_color_space == CS_CMYK)
		cinfo->out_color_space = CS_CMYK;

	cinfo->methods->output_init = output_init;
	cinfo->methods->put_pixel_rows = put_pixel_rows;
	cinfo->methods->output_term = output_term;
}

/*****************************************/
/* Input Stuff                           */
/*****************************************/


/*
 * Reload the input buffer after it's been emptied, and return the next byte.
 * See the JGETC macro for calling conditions.
 *
 * This routine would need to be replaced if reading JPEG data from something
 * other than a stdio stream.
 */

METHODDEF int
read_jpeg_data (cinfo)
decompress_info_ptr cinfo;
{
  cinfo->bytes_in_buffer = zread(cinfo->input_file,
				 cinfo->input_buffer + MIN_UNGET,
				 JPEG_BUF_SIZE);

  cinfo->next_input_byte = cinfo->input_buffer + MIN_UNGET;
  
  if (cinfo->bytes_in_buffer <= 0)
    cinfo->eof_found = TRUE;

  return JGETC(cinfo);
}

/* ########################################################################## */
/*
 * jdmaster.c
 *
 */

METHODDEF void
d_per_scan_method_selection (cinfo)
decompress_info_ptr cinfo;
/* Central point for per-scan method selection */
{
  /* MCU disassembly */
  jseldmcu(cinfo);
  /* Un-subsampling of pixels */
  jselunsubsample(cinfo);
}


LOCAL void
d_initial_method_selection (cinfo)
decompress_info_ptr cinfo;
/* Central point for initial method selection (after reading file header) */
{
  /* JPEG file scanning method selection is already done. */
  /* So is output file format selection (both are done by user interface). */

  /* Entropy decoding: either Huffman or arithmetic coding. */
#ifdef ARITH_CODING_SUPPORTED
  jseldarithmetic(cinfo);
#else
  if (cinfo->arith_code) {
    ERREXIT(cinfo->emethods, "Arithmetic coding not supported");
  }
#endif
  jseldhuffman(cinfo);
  /* Cross-block smoothing */
#ifdef BLOCK_SMOOTHING_SUPPORTED
  jselbsmooth(cinfo);
#else
  cinfo->do_block_smoothing = FALSE;
#endif
  /* Gamma and color space conversion */
  jseldcolor(cinfo);

  /* Pipeline control */
  jseldpipeline(cinfo);
  /* Overall control (that's me!) */
  cinfo->methods->d_per_scan_method_selection = d_per_scan_method_selection;
}


LOCAL void
initial_setup (cinfo)
decompress_info_ptr cinfo;
/* Do computations that are needed before initial method selection */
{
  short ci;
  jpeg_component_info *compptr;

  /* Compute maximum sampling factors; check factor validity */
  cinfo->max_h_samp_factor = 1;
  cinfo->max_v_samp_factor = 1;
  for (ci = 0; ci < cinfo->num_components; ci++) {
    compptr = &cinfo->comp_info[ci];
    if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR ||
	compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR)
      ERREXIT(cinfo->emethods, "Bogus sampling factors");
    cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor,
				   compptr->h_samp_factor);
    cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor,
				   compptr->v_samp_factor);

  }

  /* Compute logical subsampled dimensions of components */
  for (ci = 0; ci < cinfo->num_components; ci++) {
    compptr = &cinfo->comp_info[ci];
    compptr->true_comp_width = (cinfo->image_width * compptr->h_samp_factor
				+ cinfo->max_h_samp_factor - 1)
				/ cinfo->max_h_samp_factor;
    compptr->true_comp_height = (cinfo->image_height * compptr->v_samp_factor
				 + cinfo->max_v_samp_factor - 1)
				 / cinfo->max_v_samp_factor;
  }
}


/*
 * This is the main entry point to the JPEG decompressor.
 * Set identify TRUE if you just want to check if this is a JPEG file.
 * This returns TRUE if we think it is a JPEG file.
 */


GLOBAL boolean
jpeg_decompress (cinfo, identify)
decompress_info_ptr cinfo;
boolean identify;
{
  short i;
  boolean retv = FALSE;

  /* Initialize pointers as needed to mark stuff unallocated. */
  cinfo->comp_info = NULL;
  for (i = 0; i < NUM_QUANT_TBLS; i++)
    cinfo->quant_tbl_ptrs[i] = NULL;
  for (i = 0; i < NUM_HUFF_TBLS; i++) {
    cinfo->dc_huff_tbl_ptrs[i] = NULL;
    cinfo->ac_huff_tbl_ptrs[i] = NULL;
  }

  /* Read the JPEG file header markers; everything up through the first SOS
   * marker is read now.  NOTE: the user interface must have initialized the
   * read_file_header method pointer (eg, by calling jselrjfif or jselrtiff).
   * The other file reading methods (read_scan_header etc.) were probably
   * set at the same time, but could be set up by read_file_header itself.
   */
  if (! ((*cinfo->methods->read_file_header) (cinfo)))
    goto rett;		/* image will be null */

  if (! ((*cinfo->methods->read_scan_header) (cinfo)))
    goto rett; /* Empty JPEG file - something went wrong */

  if (cinfo->verbose)
  {
    fprintf(stderr, "%s is a %dx%d JPEG Image, color space ",
	    cinfo->filename, (int) cinfo->image_width, (int) cinfo->image_height);
	switch (cinfo->jpeg_color_space)
    {
      case CS_UNKNOWN:
	fprintf(stderr, "Unknown");
	break;
      case CS_GRAYSCALE:
	fprintf(stderr, "Grayscale");
	break;
      case CS_RGB:
	fprintf(stderr, "RGB");
	break;
      case CS_YCbCr:
	fprintf(stderr, "YCbCr");
	break;
      case CS_YIQ:
	fprintf(stderr, "YIQ");
	break;
      case CS_CMYK:
	fprintf(stderr, "CMYK");
	break;
    }
    fprintf(stderr, ", %d comp%s,",cinfo->num_components, cinfo->num_components ? "s." : ".");
    if (cinfo->arith_code)
	fprintf(stderr, " Arithmetic coding\n");
    else
      fprintf(stderr, " Huffman coding\n");
  }

  if (identify)
  {
    retv = TRUE;
	goto rett;
  }

  /* Give UI a chance to adjust decompression parameters and select */
  /* output file format based on info from file header. */
  (*cinfo->methods->d_ui_method_selection) (cinfo);

  /* Now select methods for decompression steps. */
  initial_setup(cinfo);
  d_initial_method_selection(cinfo);

  /* Initialize the output file & other modules as needed */

  (*cinfo->methods->output_init) (cinfo);
  (*cinfo->methods->colorout_init) (cinfo);

  /* And let the pipeline controller do the rest. */
  (*cinfo->methods->d_pipeline_controller) (cinfo);

  /* Finish output file, release working storage, etc */
  (*cinfo->methods->colorout_term) (cinfo);
  (*cinfo->methods->output_term) (cinfo);
  (*cinfo->methods->read_file_trailer) (cinfo);

  /* Release allocated storage for tables */
#define FREE(ptr)  if ((ptr) != NULL) \
			(*cinfo->emethods->free_small) ((void *) ptr)

rett:
  FREE(cinfo->comp_info);
  for (i = 0; i < NUM_QUANT_TBLS; i++)
    FREE(cinfo->quant_tbl_ptrs[i]);
  for (i = 0; i < NUM_HUFF_TBLS; i++) {
    FREE(cinfo->dc_huff_tbl_ptrs[i]);
    FREE(cinfo->ac_huff_tbl_ptrs[i]);
  }
  if (cinfo->image != NULL)
    retv = TRUE;

  /* My, that was easy, wasn't it? */
  return retv;
}


/* ########################################################################## */
/*
 * jrdjfif.c
 *
 */

typedef enum {			/* JPEG marker codes */
  M_SOF0  = 0xc0,
  M_SOF1  = 0xc1,
  M_SOF2  = 0xc2,
  M_SOF3  = 0xc3,
  
  M_SOF5  = 0xc5,
  M_SOF6  = 0xc6,
  M_SOF7  = 0xc7,
  
  M_JPG   = 0xc8,
  M_SOF9  = 0xc9,
  M_SOF10 = 0xca,
  M_SOF11 = 0xcb,
  
  M_SOF13 = 0xcd,
  M_SOF14 = 0xce,
  M_SOF15 = 0xcf,
  
  M_DHT   = 0xc4,
  
  M_DAC   = 0xcc,
  
  M_RST0  = 0xd0,
  M_RST1  = 0xd1,
  M_RST2  = 0xd2,
  M_RST3  = 0xd3,
  M_RST4  = 0xd4,
  M_RST5  = 0xd5,
  M_RST6  = 0xd6,
  M_RST7  = 0xd7,
  
  M_SOI   = 0xd8,
  M_EOI   = 0xd9,
  M_SOS   = 0xda,
  M_DQT   = 0xdb,
  M_DNL   = 0xdc,
  M_DRI   = 0xdd,
  M_DHP   = 0xde,
  M_EXP   = 0xdf,
  
  M_APP0  = 0xe0,
  M_APP15 = 0xef,
  
  M_JPG0  = 0xf0,
  M_JPG13 = 0xfd,
  M_COM   = 0xfe,
  
  M_TEM   = 0x01,
  
  M_ERROR = 0x100
} JPEG_MARKER;


/*
 * Routines to parse JPEG markers & save away the useful info.
 */


LOCAL INT32
get_2bytes (cinfo)
decompress_info_ptr cinfo;
/* Get a 2-byte unsigned integer (e.g., a marker parameter length field) */
{
  INT32 a;
  
  a = JGETC(cinfo);
  return (a << 8) + JGETC(cinfo);
}


LOCAL void
skip_variable (cinfo, code)
decompress_info_ptr cinfo; int code;
/* Skip over an unknown or uninteresting variable-length marker */
{
  INT32 length;
  
  length = get_2bytes(cinfo);
  
  TRACEMS2(cinfo->emethods, 1,
	   "Skipping marker 0x%02x, length %d", code, length);
  
  for (length -= 2; length > 0; length--)
    (void) JGETC(cinfo);
}


LOCAL void
get_dht (cinfo)
decompress_info_ptr cinfo;
/* Process a DHT marker */
{
  INT32 length;
  UINT8 bits[17];
  UINT8 huffval[256];
  int i, index, count;
  HUFF_TBL **htblptr;
  
  length = get_2bytes(cinfo)-2;
  
  while (length > 0) {
    index = JGETC(cinfo);

    TRACEMS1(cinfo->emethods, 1, "Define Huffman Table 0x%02x", index);
      
    bits[0] = 0;
    count = 0;
    for (i = 1; i <= 16; i++) {
      bits[i] = JGETC(cinfo);
      count += bits[i];
    }

    TRACEMS8(cinfo->emethods, 2, "        %3d %3d %3d %3d %3d %3d %3d %3d",
	     bits[1], bits[2], bits[3], bits[4],
	     bits[5], bits[6], bits[7], bits[8]);
    TRACEMS8(cinfo->emethods, 2, "        %3d %3d %3d %3d %3d %3d %3d %3d",
	     bits[9], bits[10], bits[11], bits[12],
	     bits[13], bits[14], bits[15], bits[16]);

    if (count > 256)
      ERREXIT(cinfo->emethods, "Bogus DHT counts");

    for (i = 0; i < count; i++)
      huffval[i] = JGETC(cinfo);

    length -= 1 + 16 + count;

    if (index & 0x10) {		/* AC table definition */
      index -= 0x10;
      htblptr = &cinfo->ac_huff_tbl_ptrs[index];
    } else {			/* DC table definition */
      htblptr = &cinfo->dc_huff_tbl_ptrs[index];
    }

    if (index < 0 || index >= NUM_HUFF_TBLS)
      ERREXIT1(cinfo->emethods, "Bogus DHT index %d", index);

    if (*htblptr == NULL)
      *htblptr = (HUFF_TBL *)(*cinfo->emethods->alloc_small) (SIZEOF(HUFF_TBL));
  
    memcpy((void *) (*htblptr)->bits, (void *) bits, 
	    SIZEOF((*htblptr)->bits));
    memcpy((void *) (*htblptr)->huffval, (void *) huffval, 
	    SIZEOF((*htblptr)->huffval));
    }
}


LOCAL void
get_dac (cinfo)
decompress_info_ptr cinfo;
/* Process a DAC marker */
{
  INT32 length;
  int index, val;

  length = get_2bytes(cinfo)-2;
  
  while (length > 0) {
    index = JGETC(cinfo);
    val = JGETC(cinfo);

    TRACEMS2(cinfo->emethods, 1,
	     "Define Arithmetic Table 0x%02x: 0x%02x", index, val);

    if (index < 0 || index >= (2*NUM_ARITH_TBLS))
      ERREXIT1(cinfo->emethods, "Bogus DAC index %d", index);

    if (index >= NUM_ARITH_TBLS) { /* define AC table */
      cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = val;
    } else {			/* define DC table */
      cinfo->arith_dc_L[index] = val & 0x0F;
      cinfo->arith_dc_U[index] = val >> 4;
      if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index])
	ERREXIT1(cinfo->emethods, "Bogus DAC value 0x%x", val);
    }

    length -= 2;
  }
}


LOCAL void
get_dqt (cinfo)
decompress_info_ptr cinfo;
/* Process a DQT marker */
{
  INT32 length;
  int n, i, prec;
  UINT16 tmp;
  QUANT_TBL_PTR quant_ptr;
  
  length = get_2bytes(cinfo) - 2;
  
  while (length > 0) {
    n = JGETC(cinfo);
    prec = n >> 4;
    n &= 0x0F;

    TRACEMS2(cinfo->emethods, 1,
	     "Define Quantization Table %d  precision %d", n, prec);

    if (n >= NUM_QUANT_TBLS)
      ERREXIT1(cinfo->emethods, "Bogus table number %d", n);
      
    if (cinfo->quant_tbl_ptrs[n] == NULL)
      cinfo->quant_tbl_ptrs[n] = (QUANT_TBL_PTR)(*cinfo->emethods->alloc_small) (SIZEOF(QUANT_TBL));
    quant_ptr = cinfo->quant_tbl_ptrs[n];

    for (i = 0; i < DCTSIZE2; i++) {
      tmp = JGETC(cinfo);
      if (prec)
	tmp = (tmp<<8) + JGETC(cinfo);
      quant_ptr[i] = tmp;
    }

    for (i = 0; i < DCTSIZE2; i += 8) {
      TRACEMS8(cinfo->emethods, 2, "        %4d %4d %4d %4d %4d %4d %4d %4d",
	       quant_ptr[i  ], quant_ptr[i+1], quant_ptr[i+2], quant_ptr[i+3],
	       quant_ptr[i+4], quant_ptr[i+5], quant_ptr[i+6], quant_ptr[i+7]);
    }

    length -= DCTSIZE2+1;
    if (prec) length -= DCTSIZE2;
  }
}


LOCAL void
get_dri (cinfo)
decompress_info_ptr cinfo;
/* Process a DRI marker */
{
  if (get_2bytes(cinfo) != 4)
    ERREXIT(cinfo->emethods, "Bogus length in DRI");

  cinfo->restart_interval = get_2bytes(cinfo);

  TRACEMS1(cinfo->emethods, 1,
	   "Define Restart Interval %d", cinfo->restart_interval);
}


LOCAL void
get_app0 (cinfo)
decompress_info_ptr cinfo;
/* Process an APP0 marker */
{
#define JFIF_LEN 14
  INT32 length;
  UINT8 b[JFIF_LEN];
  int buffp;

  length = get_2bytes(cinfo) - 2;

  /* See if a JFIF APP0 marker is present */

  if (length >= JFIF_LEN) {
    for (buffp = 0; buffp < JFIF_LEN; buffp++)
      b[buffp] = JGETC(cinfo);
    length -= JFIF_LEN;

    if (b[0]=='J' && b[1]=='F' && b[2]=='I' && b[3]=='F' && b[4]==0) {
      /* Found JFIF APP0 marker: check version */
      /* Major version must be 1 */
      if (b[5] != 1)
	ERREXIT2(cinfo->emethods, "Unsupported JFIF revision number %d.%02d",
		 b[5], b[6]);
      /* Minor version should be 0 or 1, but try to process anyway if newer */
      if (b[6] != 0 && b[6] != 1)
	TRACEMS2(cinfo->emethods, 0, "Warning: unknown JFIF revision number %d.%02d",
		 b[5], b[6]);
      /* Save info */
      cinfo->density_unit = b[7];
      cinfo->X_density = (b[8] << 8) + b[9];
      cinfo->Y_density = (b[10] << 8) + b[11];
      /* Assume colorspace is YCbCr, unless UI has overridden me */
      if (cinfo->jpeg_color_space == CS_UNKNOWN)
	cinfo->jpeg_color_space = CS_YCbCr;
      TRACEMS3(cinfo->emethods, 1, "JFIF APP0 marker, density %dx%d  %d",
	       cinfo->X_density, cinfo->Y_density, cinfo->density_unit);
    } else {
      TRACEMS(cinfo->emethods, 1, "Unknown APP0 marker (not JFIF)");
    }
  } else {
    TRACEMS1(cinfo->emethods, 1,
	     "Short APP0 marker, length %d", (int) length);
  }

  while (length-- > 0)		/* skip any remaining data */
    (void) JGETC(cinfo);
}


LOCAL void
get_sof (cinfo, code)
decompress_info_ptr cinfo;
int code;
/* Process a SOFn marker */
{
  INT32 length;
  short ci;
  int c;
  jpeg_component_info * compptr;
  
  length = get_2bytes(cinfo);
  
  cinfo->data_precision = JGETC(cinfo);
  cinfo->image_height   = get_2bytes(cinfo);
  cinfo->image_width    = get_2bytes(cinfo);
  cinfo->num_components = JGETC(cinfo);

  TRACEMS4(cinfo->emethods, 1,
	   "Start Of Frame 0x%02x: width=%d, height=%d, components=%d",
	   code, cinfo->image_width, cinfo->image_height,
	   cinfo->num_components);

  /* We don't support files in which the image height is initially specified */
  /* as 0 and is later redefined by DNL.  As long as we have to check that,  */
  /* might as well have a general sanity check. */
  if (cinfo->image_height <= 0 || cinfo->image_width <= 0
      || cinfo->num_components <= 0)
    ERREXIT(cinfo->emethods, "Empty JPEG image (DNL not supported)");

#ifdef EIGHT_BIT_SAMPLES
  if (cinfo->data_precision != 8)
    ERREXIT(cinfo->emethods, "Unsupported JPEG data precision");
#endif
#ifdef TWELVE_BIT_SAMPLES
  if (cinfo->data_precision != 12) /* this needs more thought?? */
    ERREXIT(cinfo->emethods, "Unsupported JPEG data precision");
#endif
#ifdef SIXTEEN_BIT_SAMPLES
  if (cinfo->data_precision != 16) /* this needs more thought?? */
    ERREXIT(cinfo->emethods, "Unsupported JPEG data precision");
#endif

  if (length != (cinfo->num_components * 3 + 8))
    ERREXIT(cinfo->emethods, "Bogus SOF length");

  cinfo->comp_info = (jpeg_component_info *)((*cinfo->emethods->alloc_small)
			(cinfo->num_components * SIZEOF(jpeg_component_info)));
  
  for (ci = 0; ci < cinfo->num_components; ci++) {
    compptr = &cinfo->comp_info[ci];
    compptr->component_index = ci;
    compptr->component_id = JGETC(cinfo);
    c = JGETC(cinfo);
    compptr->h_samp_factor = (c >> 4) & 15;
    compptr->v_samp_factor = (c     ) & 15;
    compptr->quant_tbl_no  = JGETC(cinfo);
      
    TRACEMS4(cinfo->emethods, 1, "    Component %d: %dhx%dv q=%d",
	     compptr->component_id, compptr->h_samp_factor,
	     compptr->v_samp_factor, compptr->quant_tbl_no);
  }
}


LOCAL void
get_sos (cinfo)
decompress_info_ptr cinfo;
/* Process a SOS marker */
{
  INT32 length;
  int i, ci, n, c, cc;
  jpeg_component_info * compptr;
  
  length = get_2bytes(cinfo);
  
  n = JGETC(cinfo);  /* Number of components */
  cinfo->comps_in_scan = n;
  length -= 3;
  
  if (length != (n * 2 + 3) || n < 1 || n > MAX_COMPS_IN_SCAN)
    ERREXIT(cinfo->emethods, "Bogus SOS length");

  TRACEMS1(cinfo->emethods, 1, "Start Of Scan: %d components", n);
  
  for (i = 0; i < n; i++) {
    cc = JGETC(cinfo);
    c = JGETC(cinfo);
    length -= 2;
    
    for (ci = 0; ci < cinfo->num_components; ci++)
      if (cc == cinfo->comp_info[ci].component_id)
	break;
    
    if (ci >= cinfo->num_components)
      ERREXIT(cinfo->emethods, "Invalid component number in SOS");
    
    compptr = &cinfo->comp_info[ci];
    cinfo->cur_comp_info[i] = compptr;
    compptr->dc_tbl_no = (c >> 4) & 15;
    compptr->ac_tbl_no = (c     ) & 15;
    
    TRACEMS3(cinfo->emethods, 1, "    c%d: [dc=%d ac=%d]", cc,
	     compptr->dc_tbl_no, compptr->ac_tbl_no);
  }
  
  while (length > 0) {
    (void) JGETC(cinfo);
    length--;
  }
}


LOCAL void
get_soi (cinfo)
decompress_info_ptr cinfo;
/* Process an SOI marker */
{
  int i;
  
  TRACEMS(cinfo->emethods, 1, "Start of Image");

  /* Reset all parameters that are defined to be reset by SOI */

  for (i = 0; i < NUM_ARITH_TBLS; i++) {
    cinfo->arith_dc_L[i] = 0;
    cinfo->arith_dc_U[i] = 1;
    cinfo->arith_ac_K[i] = 5;
  }
  cinfo->restart_interval = 0;

  cinfo->density_unit = 0;	/* set default JFIF APP0 values */
  cinfo->X_density = 1;
  cinfo->Y_density = 1;

  cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling */
}


LOCAL int
next_marker (cinfo)
decompress_info_ptr cinfo;
/* Find the next JPEG marker */
/* Note that the output might not be a valid marker code, */
/* but it will never be 0 or FF */
{
  int c, nbytes;

  nbytes = 0;
  do {
    do {			/* skip any non-FF bytes */
      nbytes++;
      c = JGETC(cinfo);
    } while (c != 0xFF && !cinfo->eof_found);
    do {			/* skip any duplicate FFs */
      nbytes++;
      c = JGETC(cinfo);
    } while (c == 0xFF && !cinfo->eof_found);
  } while (c == 0 && !cinfo->eof_found);		/* repeat if it was a stuffed FF/00 */

  if (nbytes != 2)
    TRACEMS2(cinfo->emethods, 1, "Skipped %d bytes before marker 0x%02x",
	     nbytes-2, c);

  return c;
}


LOCAL JPEG_MARKER
process_tables (cinfo)
decompress_info_ptr cinfo;
/* Scan and process JPEG markers that can appear in any order */
/* Return when an SOI, EOI, SOFn, or SOS is found */
{
  int c;

  while (TRUE) {
    c = next_marker(cinfo);
    if (cinfo->eof_found)
      return 0;
      
    switch (c) {
    case M_SOF0:
    case M_SOF1:
    case M_SOF2:
    case M_SOF3:
    case M_SOF5:
    case M_SOF6:
    case M_SOF7:
    case M_JPG:
    case M_SOF9:
    case M_SOF10:
    case M_SOF11:
    case M_SOF13:
    case M_SOF14:
    case M_SOF15:
    case M_SOI:
    case M_EOI:
    case M_SOS:
      return c;
      
    case M_DHT:
      get_dht(cinfo);
      break;
      
    case M_DAC:
      get_dac(cinfo);
      break;
      
    case M_DQT:
      get_dqt(cinfo);
      break;
      
    case M_DRI:
      get_dri(cinfo);
      break;
      
    case M_APP0:
      get_app0(cinfo);
      break;

    case M_RST0:		/* these are all parameterless */
    case M_RST1:
    case M_RST2:
    case M_RST3:
    case M_RST4:
    case M_RST5:
    case M_RST6:
    case M_RST7:
    case M_TEM:
      TRACEMS1(cinfo->emethods, 1, "Unexpected marker 0x%02x", c);
      break;

    default:	/* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn */
      skip_variable(cinfo, c);
      break;
    }
  }
}



/*
 * Initialize and read the file header (everything through the SOF marker).
 * Return FALSE if we don't recognize the file.
 */

METHODDEF boolean
read_file_header (cinfo)
decompress_info_ptr cinfo;
{
  int c;

  /* Expect an SOI marker first */
  if (next_marker(cinfo) == M_SOI)
    get_soi(cinfo);
  else
    return FALSE;

  /* Process markers until SOF */
  c = process_tables(cinfo);

  switch (c) {
  case M_SOF0:
  case M_SOF1:
    get_sof(cinfo, c);
    cinfo->arith_code = FALSE;
    break;
      
  case M_SOF9:
    get_sof(cinfo, c);
    cinfo->arith_code = TRUE;
    break;

  default:
    /* Unsupported SOF marker type */
    fprintf(stderr, "unsupported SOF marker type 0x%02x\n", c);
    return FALSE;
  }

  /* Figure out what colorspace we have */
  /* (too bad the JPEG committee didn't provide a real way to specify this) */

  switch (cinfo->num_components) {
  case 1:
    cinfo->jpeg_color_space = CS_GRAYSCALE;
    break;

  case 3:
    /* if we saw a JFIF marker, leave it set to YCbCr; */
    /* also leave it alone if UI has provided a value */
    if (cinfo->jpeg_color_space == CS_UNKNOWN) {
      short cid0 = cinfo->comp_info[0].component_id;
      short cid1 = cinfo->comp_info[1].component_id;
      short cid2 = cinfo->comp_info[2].component_id;

      if (cid0 == 1 && cid1 == 2 && cid2 == 3)
	cinfo->jpeg_color_space = CS_YCbCr; /* assume it's JFIF w/out marker */
      else if (cid0 == 1 && cid1 == 4 && cid2 == 5)
	cinfo->jpeg_color_space = CS_YIQ; /* prototype's YIQ matrix */
      else {
	TRACEMS3(cinfo->emethods, 0,
		 "Unrecognized component IDs %d %d %d, assuming YCbCr",
		 cid0, cid1, cid2);
	cinfo->jpeg_color_space = CS_YCbCr;
      }
    }
    break;

  case 4:
    cinfo->jpeg_color_space = CS_CMYK;
    break;

  default:
    cinfo->jpeg_color_space = CS_UNKNOWN;
    break;
  }
  return TRUE;
}


/*
 * Read the start of a scan (everything through the SOS marker).
 * Return TRUE if find SOS, FALSE if find EOI.
 */

METHODDEF boolean
read_scan_header (cinfo)
decompress_info_ptr cinfo;
{
  int c;
  
  /* Process markers until SOS or EOI */
  c = process_tables(cinfo);
  
  switch (c) {
  case M_SOS:
    get_sos(cinfo);
    return TRUE;
    
  case M_EOI:
    TRACEMS(cinfo->emethods, 1, "End Of Image");
    return FALSE;

  default:
	if (cinfo->eof_found)
		return FALSE;
    ERREXIT1(cinfo->emethods, "Unexpected marker 0x%02x", c);
    break;
  }
  return FALSE;			/* keeps lint happy */
}


/*
 * Finish up after a compressed scan (series of read_jpeg_data calls);
 * prepare for another read_scan_header call.
 */

METHODDEF void
read_scan_trailer (cinfo)
decompress_info_ptr cinfo;
{
  /* no work needed */
}


/*
 * Finish up at the end of the file.
 */

METHODDEF void
read_file_trailer (cinfo)
decompress_info_ptr cinfo;
{
  /* no work needed */
}


/*
 * The method selection routine for standard JPEG header reading.
 * Note that this must be called by the user interface before calling
 * jpeg_decompress.  When a non-JFIF file is to be decompressed (TIFF,
 * perhaps), the user interface must discover the file type and call
 * the appropriate method selection routine.
 */

GLOBAL void
jselrjfif (cinfo)
decompress_info_ptr cinfo;
{
  cinfo->methods->read_file_header = read_file_header;
  cinfo->methods->read_scan_header = read_scan_header;
  /* For JFIF/raw-JPEG format, the user interface supplies read_jpeg_data. */
#if 0
  cinfo->methods->read_jpeg_data = read_jpeg_data;
#endif
  cinfo->methods->read_scan_trailer = read_scan_trailer;
  cinfo->methods->read_file_trailer = read_file_trailer;
}

/* ########################################################################## */
/*
 * jdpipe.c
 *
 */

/*
 * About the data structures:
 *
 * The processing chunk size for unsubsampling is referred to in this file as
 * a "row group": a row group is defined as Vk (v_samp_factor) sample rows of
 * any component while subsampled, or Vmax (max_v_samp_factor) unsubsampled
 * rows.  In an interleaved scan each MCU row contains exactly DCTSIZE row
 * groups of each component in the scan.  In a noninterleaved scan an MCU row
 * is one row of blocks, which might not be an integral number of row groups;
 * therefore, we read in Vk MCU rows to obtain the same amount of data as we'd
 * have in an interleaved scan.
 * To provide context for the unsubsampling step, we have to retain the last
 * two row groups of the previous MCU row while reading in the next MCU row
 * (or set of Vk MCU rows).  To do this without copying data about, we create
 * a rather strange data structure.  Exactly DCTSIZE+2 row groups of samples
 * are allocated, but we create two different sets of pointers to this array.
 * The second set swaps the last two pairs of row groups.  By working
 * alternately with the two sets of pointers, we can access the data in the
 * desired order.
 *
 * Cross-block smoothing also needs context above and below the "current" row.
 * Since this is an optional feature, I've implemented it in a way that is
 * much simpler but requires more than the minimum amount of memory.  We
 * simply allocate three extra MCU rows worth of coefficient blocks and use
 * them to "read ahead" one MCU row in the file.  For a typical 1000-pixel-wide
 * image with 2x2,1x1,1x1 sampling, each MCU row is about 50Kb; an 80x86
 * machine may be unable to apply cross-block smoothing to wider images.
 */


/*
 * These variables are logically local to the pipeline controller,
 * but we make them static so that scan_big_image can use them
 * without having to pass them through the quantization routines.
 * If you don't support 2-pass quantization, you could make them locals.
 */

static int rows_in_mem;		/* # of sample rows in full-size buffers */
/* Full-size image array holding desubsampled, color-converted data. */


/*
 * Utility routines: common code for pipeline controllers
 */

LOCAL void
interleaved_scan_setup (cinfo)
decompress_info_ptr cinfo;
/* Compute all derived info for an interleaved (multi-component) scan */
/* On entry, cinfo->comps_in_scan and cinfo->cur_comp_info[] are set up */
{
  short ci, mcublks;
  jpeg_component_info *compptr;

  if (cinfo->comps_in_scan > MAX_COMPS_IN_SCAN)
    ERREXIT(cinfo->emethods, "Too many components for interleaved scan");

  cinfo->MCUs_per_row = (cinfo->image_width
			 + cinfo->max_h_samp_factor*DCTSIZE - 1)
			/ (cinfo->max_h_samp_factor*DCTSIZE);

  cinfo->MCU_rows_in_scan = (cinfo->image_height
			     + cinfo->max_v_samp_factor*DCTSIZE - 1)
			    / (cinfo->max_v_samp_factor*DCTSIZE);
  
  cinfo->blocks_in_MCU = 0;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    compptr = cinfo->cur_comp_info[ci];
    /* for interleaved scan, sampling factors give # of blocks per component */
    compptr->MCU_width = compptr->h_samp_factor;
    compptr->MCU_height = compptr->v_samp_factor;
    compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
    /* compute physical dimensions of component */
    compptr->subsampled_width = jround_up(compptr->true_comp_width,
					  (long) (compptr->MCU_width*DCTSIZE));
    compptr->subsampled_height = jround_up(compptr->true_comp_height,
					   (long) (compptr->MCU_height*DCTSIZE));
    /* Sanity check */
    if (compptr->subsampled_width !=
	(cinfo->MCUs_per_row * (compptr->MCU_width*DCTSIZE)))
      ERREXIT(cinfo->emethods, "I'm confused about the image width");
    /* Prepare array describing MCU composition */
    mcublks = compptr->MCU_blocks;
    if (cinfo->blocks_in_MCU + mcublks > MAX_BLOCKS_IN_MCU)
      ERREXIT(cinfo->emethods, "Sampling factors too large for interleaved scan");
    while (mcublks-- > 0) {
      cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
    }
  }

  (*cinfo->methods->d_per_scan_method_selection) (cinfo);
}


LOCAL void
noninterleaved_scan_setup (cinfo)
decompress_info_ptr cinfo;
/* Compute all derived info for a noninterleaved (single-component) scan */
/* On entry, cinfo->comps_in_scan = 1 and cinfo->cur_comp_info[0] is set up */
{
  jpeg_component_info *compptr = cinfo->cur_comp_info[0];

  /* for noninterleaved scan, always one block per MCU */
  compptr->MCU_width = 1;
  compptr->MCU_height = 1;
  compptr->MCU_blocks = 1;
  /* compute physical dimensions of component */
  compptr->subsampled_width = jround_up(compptr->true_comp_width,
					(long) DCTSIZE);
  compptr->subsampled_height = jround_up(compptr->true_comp_height,
					 (long) DCTSIZE);

  cinfo->MCUs_per_row = compptr->subsampled_width / DCTSIZE;
  cinfo->MCU_rows_in_scan = compptr->subsampled_height / DCTSIZE;

  /* Prepare array describing MCU composition */
  cinfo->blocks_in_MCU = 1;
  cinfo->MCU_membership[0] = 0;

  (*cinfo->methods->d_per_scan_method_selection) (cinfo);
}


LOCAL void
reverse_DCT (cinfo, coeff_data, output_data, start_row)
decompress_info_ptr cinfo;
JBLOCKIMAGE coeff_data; JSAMPIMAGE output_data;
int start_row;
/* Perform inverse DCT on each block in an MCU row's worth of data; */
/* output the results into a sample array starting at row start_row. */
/* NB: start_row can only be nonzero when dealing with a single-component */
/* scan; otherwise we'd have to provide for different offsets for different */
/* components, since the heights of interleaved MCU rows can vary. */
{
  DCTBLOCK block;
  JBLOCKROW browptr;
  JSAMPARRAY srowptr;
  long blocksperrow, bi;
  short numrows, ri;
  short ci;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    /* calc size of an MCU row in this component */
    blocksperrow = cinfo->cur_comp_info[ci]->subsampled_width / DCTSIZE;
    numrows = cinfo->cur_comp_info[ci]->MCU_height;
    /* iterate through all blocks in MCU row */
    for (ri = 0; ri < numrows; ri++) {
      browptr = coeff_data[ci][ri];
      srowptr = output_data[ci] + (ri * DCTSIZE + start_row);
      for (bi = 0; bi < blocksperrow; bi++) {
	/* copy the data into a local DCTBLOCK.  This allows for change of
	 * representation (if DCTELEM != JCOEF).  On 80x86 machines it also
	 * brings the data back from FAR storage to NEAR storage.
	 */
	{ register JCOEFPTR elemptr = browptr[bi];
	  register DCTELEM *localblkptr = block;
	  register short elem = DCTSIZE2;

	  while (--elem >= 0)
	    *localblkptr++ = (DCTELEM) *elemptr++;
	}

	j_rev_dct(block);	/* perform inverse DCT */

	/* output the data into the sample array.
	 * Note change from signed to unsigned representation:
	 * DCT calculation works with values +-CENTERJSAMPLE,
	 * but sample arrays always hold 0..MAXJSAMPLE.
	 * Have to do explicit range-limiting because of quantization errors
	 * and so forth in the DCT/IDCT phase.
	 */
	{ register JSAMPROW elemptr;
	  register DCTELEM *localblkptr = block;
	  register short elemr, elemc;
	  register DCTELEM temp;

	  for (elemr = 0; elemr < DCTSIZE; elemr++) {
	    elemptr = srowptr[elemr] + (bi * DCTSIZE);
	    for (elemc = 0; elemc < DCTSIZE; elemc++) {
	      temp = (*localblkptr++) + CENTERJSAMPLE;
	      if (temp < 0) temp = 0;
	      else if (temp > MAXJSAMPLE) temp = MAXJSAMPLE;
	      *elemptr++ = (JSAMPLE) temp;
	    }
	  }
	}
      }
    }
  }
}



LOCAL JSAMPIMAGE
alloc_sampimage (cinfo, num_comps, num_rows, num_cols)
decompress_info_ptr cinfo;
int num_comps;
long num_rows;
long num_cols;
/* Allocate an in-memory sample image (all components same size) */
{
  JSAMPIMAGE image;
  int ci;

  image = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
				(num_comps * SIZEOF(JSAMPARRAY));
  for (ci = 0; ci < num_comps; ci++) {
    image[ci] = (*cinfo->emethods->alloc_small_sarray) (num_cols, num_rows);
  }
  return image;
}


LOCAL void
free_sampimage (cinfo, image, num_comps, num_rows)
decompress_info_ptr cinfo;
JSAMPIMAGE image;
int num_comps;
long num_rows;
/* Release a sample image created by alloc_sampimage */
{
  int ci;

  for (ci = 0; ci < num_comps; ci++) {
      (*cinfo->emethods->free_small_sarray) (image[ci], num_rows);
  }
  (*cinfo->emethods->free_small) ((void *) image);
}


LOCAL JBLOCKIMAGE
alloc_MCU_row (cinfo)
decompress_info_ptr cinfo;
/* Allocate one MCU row's worth of coefficient blocks */
{
  JBLOCKIMAGE image;
  int ci;

  image = (JBLOCKIMAGE) (*cinfo->emethods->alloc_small)
				(cinfo->comps_in_scan * SIZEOF(JBLOCKARRAY));
  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    image[ci] = (*cinfo->emethods->alloc_small_barray)
			(cinfo->cur_comp_info[ci]->subsampled_width / DCTSIZE,
			 (long) cinfo->cur_comp_info[ci]->MCU_height);
  }
  return image;
}


LOCAL void
free_MCU_row (cinfo, image)
decompress_info_ptr cinfo;
JBLOCKIMAGE image;
/* Release a coefficient block array created by alloc_MCU_row */
{
  int ci;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    (*cinfo->emethods->free_small_barray)
		(image[ci], (long) cinfo->cur_comp_info[ci]->MCU_height);
  }
  (*cinfo->emethods->free_small) ((void *) image);
}


LOCAL void
alloc_sampling_buffer (cinfo, subsampled_data)
decompress_info_ptr cinfo;
JSAMPIMAGE subsampled_data[2];
/* Create a subsampled-data buffer having the desired structure */
/* (see comments at head of file) */
{
  short ci, vs, i;

  /* Get top-level space for array pointers */
  subsampled_data[0] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
				(cinfo->comps_in_scan * SIZEOF(JSAMPARRAY));
  subsampled_data[1] = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
				(cinfo->comps_in_scan * SIZEOF(JSAMPARRAY));

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    vs = cinfo->cur_comp_info[ci]->v_samp_factor; /* row group height */
    /* Allocate the real storage */
    subsampled_data[0][ci] = (*cinfo->emethods->alloc_small_sarray)
				(cinfo->cur_comp_info[ci]->subsampled_width,
				(long) (vs * (DCTSIZE+2)));
    /* Create space for the scrambled-order pointers */
    subsampled_data[1][ci] = (JSAMPARRAY) (*cinfo->emethods->alloc_small)
				(vs * (DCTSIZE+2) * SIZEOF(JSAMPROW));
    /* Duplicate the first DCTSIZE-2 row groups */
    for (i = 0; i < vs * (DCTSIZE-2); i++) {
      subsampled_data[1][ci][i] = subsampled_data[0][ci][i];
    }
    /* Copy the last four row groups in swapped order */
    for (i = 0; i < vs * 2; i++) {
      subsampled_data[1][ci][vs*DCTSIZE + i] = subsampled_data[0][ci][vs*(DCTSIZE-2) + i];
      subsampled_data[1][ci][vs*(DCTSIZE-2) + i] = subsampled_data[0][ci][vs*DCTSIZE + i];
    }
  }
}


LOCAL void
free_sampling_buffer (cinfo, subsampled_data)
decompress_info_ptr cinfo;
JSAMPIMAGE subsampled_data[2];
/* Release a sampling buffer created by alloc_sampling_buffer */
{
  short ci, vs;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    vs = cinfo->cur_comp_info[ci]->v_samp_factor; /* row group height */
    /* Free the real storage */
    (*cinfo->emethods->free_small_sarray)
		(subsampled_data[0][ci], (long) (vs * (DCTSIZE+2)));
    /* Free the scrambled-order pointers */
    (*cinfo->emethods->free_small) ((void *) subsampled_data[1][ci]);
  }

  /* Free the top-level space */
  (*cinfo->emethods->free_small) ((void *) subsampled_data[0]);
  (*cinfo->emethods->free_small) ((void *) subsampled_data[1]);
}


LOCAL void
duplicate_row (image_data, num_cols, source_row, num_rows)
JSAMPARRAY image_data;
long num_cols;
int source_row;
int num_rows;
/* Duplicate the source_row at source_row+1 .. source_row+num_rows */
/* This happens only at the bottom of the image, */
/* so it needn't be super-efficient */
{
  register int row;

  for (row = 1; row <= num_rows; row++) {
    jcopy_sample_rows(image_data, source_row, image_data, source_row + row,
		      1, num_cols);
  }
}


LOCAL void
jexpand (cinfo, subsampled_data, fullsize_data, fullsize_width, above, current, below, out)
decompress_info_ptr cinfo;
JSAMPIMAGE subsampled_data;
JSAMPIMAGE fullsize_data;
long fullsize_width;
short above;
short current;
short below;
short out;
/* Do unsubsampling expansion of a single row group (of each component).  */
/* above, current, below are indexes of row groups in subsampled_data;    */
/* out is the index of the target row group in fullsize_data.             */
/* Special case: above, below can be -1 to indicate top, bottom of image. */
{
  jpeg_component_info *compptr;
  JSAMPARRAY above_ptr, below_ptr;
  JSAMPROW dummy[MAX_SAMP_FACTOR]; /* for subsample expansion at top/bottom */
  short ci, vs, i;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    compptr = cinfo->cur_comp_info[ci];
    vs = compptr->v_samp_factor; /* row group height */

    if (above >= 0)
      above_ptr = subsampled_data[ci] + above * vs;
    else {
      /* Top of image: make a dummy above-context with copies of 1st row */
      /* We assume current=0 in this case */
      for (i = 0; i < vs; i++)
	dummy[i] = subsampled_data[ci][0];
      above_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */
    }

    if (below >= 0)
      below_ptr = subsampled_data[ci] + below * vs;
    else {
      /* Bot of image: make a dummy below-context with copies of last row */
      for (i = 0; i < vs; i++)
	dummy[i] = subsampled_data[ci][(current+1)*vs-1];
      below_ptr = (JSAMPARRAY) dummy; /* possible near->far pointer conv */
    }

    (*cinfo->methods->unsubsample[ci])
		(cinfo, (int) ci,
		 compptr->subsampled_width, (int) vs,
		 fullsize_width, (int) cinfo->max_v_samp_factor,
		 above_ptr,
		 subsampled_data[ci] + current * vs,
		 below_ptr,
		 fullsize_data[ci] + out * cinfo->max_v_samp_factor);
  }
}

LOCAL void
emit_1pass (cinfo, num_rows, fullsize_data, color_data)
decompress_info_ptr cinfo;
int num_rows;
JSAMPIMAGE fullsize_data;
JSAMPIMAGE color_data;
/* Do color conversion and output of num_rows full-size rows. */
/* This is not used for 2-pass color quantization. */
{
  (*cinfo->methods->color_convert) (cinfo, num_rows,
				    fullsize_data, color_data);

  (*cinfo->methods->put_pixel_rows) (cinfo, num_rows,
				       color_data);
}

/*
 * Support routines for cross-block smoothing.
 */

#ifdef BLOCK_SMOOTHING_SUPPORTED


LOCAL void
smooth_mcu_row (cinfo, above, input, below, output)
decompress_info_ptr cinfo;
JBLOCKIMAGE above;
JBLOCKIMAGE input;
JBLOCKIMAGE below;
JBLOCKIMAGE output;
/* Apply cross-block smoothing to one MCU row's worth of coefficient blocks. */
/* above,below are NULL if at top/bottom of image. */
{
  jpeg_component_info *compptr;
  short ci, ri, last;
  JBLOCKROW prev;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    compptr = cinfo->cur_comp_info[ci];
    last = compptr->MCU_height - 1;

    if (above == NULL)
      prev = NULL;
    else
      prev = above[ci][last];

    for (ri = 0; ri < last; ri++) {
      (*cinfo->methods->smooth_coefficients) (cinfo, compptr,
				prev, input[ci][ri], input[ci][ri+1],
				output[ci][ri]);
      prev = input[ci][ri];
    }

    if (below == NULL)
      (*cinfo->methods->smooth_coefficients) (cinfo, compptr,
				prev, input[ci][last], (JBLOCKROW) NULL,
				output[ci][last]);
    else
      (*cinfo->methods->smooth_coefficients) (cinfo, compptr,
				prev, input[ci][last], below[ci][0],
				output[ci][last]);
  }
}


LOCAL void
get_smoothed_row (cinfo, coeff_data, bsmooth, whichb, cur_mcu_row)
decompress_info_ptr cinfo;
JBLOCKIMAGE coeff_data;
JBLOCKIMAGE bsmooth[3];
int * whichb;
long cur_mcu_row;
/* Get an MCU row of coefficients, applying cross-block smoothing. */
/* The output row is placed in coeff_data.  bsmooth and whichb hold */
/* working state, and cur_row is needed to check for image top/bottom. */
/* This routine just takes care of the buffering logic. */
{
  int prev, cur, next;
  
  /* Special case for top of image: need to pre-fetch a row & init whichb */
  if (cur_mcu_row == 0) {
    (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[0]);
    if (cinfo->MCU_rows_in_scan > 1) {
      (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[1]);
      smooth_mcu_row(cinfo, (JBLOCKIMAGE) NULL, bsmooth[0], bsmooth[1],
		     coeff_data);
    } else {
      smooth_mcu_row(cinfo, (JBLOCKIMAGE) NULL, bsmooth[0], (JBLOCKIMAGE) NULL,
		     coeff_data);
    }
    *whichb = 1;		/* points to next bsmooth[] element to use */
    return;
  }
  
  cur = *whichb;		/* set up references */
  prev = (cur == 0 ? 2 : cur - 1);
  next = (cur == 2 ? 0 : cur + 1);
  *whichb = next;		/* advance whichb for next time */
  
  /* Special case for bottom of image: don't read another row */
  if (cur_mcu_row >= cinfo->MCU_rows_in_scan - 1) {
    smooth_mcu_row(cinfo, bsmooth[prev], bsmooth[cur], (JBLOCKIMAGE) NULL,
		   coeff_data);
    return;
  }
  
  /* Normal case: read ahead a new row, smooth the one I got before */
  (*cinfo->methods->disassemble_MCU) (cinfo, bsmooth[next]);
  smooth_mcu_row(cinfo, bsmooth[prev], bsmooth[cur], bsmooth[next],
		 coeff_data);
}


#endif /* BLOCK_SMOOTHING_SUPPORTED */

/*
 * Decompression pipeline controller used for single-scan files
 * without 2-pass color quantization.
 */

METHODDEF void
single_dcontroller (cinfo)
decompress_info_ptr cinfo;
{
  long fullsize_width;		/* # of samples per row in full-size buffers */
  long cur_mcu_row;		/* counts # of MCU rows processed */
  long pixel_rows_output;	/* # of pixel rows actually emitted */
  int mcu_rows_per_loop;	/* # of MCU rows processed per outer loop */
  /* Work buffer for dequantized coefficients (IDCT input) */
  JBLOCKIMAGE coeff_data;
  /* Work buffer for cross-block smoothing input */
#ifdef BLOCK_SMOOTHING_SUPPORTED
  JBLOCKIMAGE bsmooth[3];	/* this is optional */
  int whichb;
#endif
  /* Work buffer for subsampled image data (see comments at head of file) */
  JSAMPIMAGE subsampled_data[2];
  /* Work buffer for desubsampled data */
  JSAMPIMAGE fullsize_data;
  /* Work buffer for color conversion output (full size) */
  JSAMPIMAGE color_data;
  int whichss, ri;
  short i;

  /* Prepare for single scan containing all components */
  if (cinfo->comps_in_scan == 1) {
    noninterleaved_scan_setup(cinfo);
    /* Need to read Vk MCU rows to obtain Vk block rows */
    mcu_rows_per_loop = cinfo->cur_comp_info[0]->v_samp_factor;
  } else {
    interleaved_scan_setup(cinfo);
    /* in an interleaved scan, one MCU row provides Vk block rows */
    mcu_rows_per_loop = 1;
  }

  /* Compute dimensions of full-size pixel buffers */
  /* Note these are the same whether interleaved or not. */
  rows_in_mem = cinfo->max_v_samp_factor * DCTSIZE;
  fullsize_width = jround_up(cinfo->image_width,
			     (long) (cinfo->max_h_samp_factor * DCTSIZE));

  /* Allocate working memory: */
  /* coeff_data holds a single MCU row of coefficient blocks */
  coeff_data = alloc_MCU_row(cinfo);
  /* if doing cross-block smoothing, need extra space for its input */
#ifdef BLOCK_SMOOTHING_SUPPORTED
  if (cinfo->do_block_smoothing) {
    bsmooth[0] = alloc_MCU_row(cinfo);
    bsmooth[1] = alloc_MCU_row(cinfo);
    bsmooth[2] = alloc_MCU_row(cinfo);
  }
#endif
  /* subsampled_data is sample data before unsubsampling */
  alloc_sampling_buffer(cinfo, subsampled_data);
  /* fullsize_data is sample data after unsubsampling */
  fullsize_data = alloc_sampimage(cinfo, (int) cinfo->num_components,
				  (long) rows_in_mem, fullsize_width);
  /* color_data is the result of the colorspace conversion step */
  color_data = alloc_sampimage(cinfo, (int) cinfo->color_out_comps,
			       (long) rows_in_mem, fullsize_width);

  /* Tell the memory manager to instantiate big arrays.
   * We don't need any big arrays in this controller,
   * but some other module (like the output file writer) may need one.
   */
  (*cinfo->emethods->alloc_big_arrays)
	((long) 0,				/* no more small sarrays */
	 (long) 0,				/* no more small barrays */
	 (long) 0);				/* no more "medium" objects */

  /* Initialize to read scan data */

  (*cinfo->methods->entropy_decoder_init) (cinfo);
  (*cinfo->methods->unsubsample_init) (cinfo);
  (*cinfo->methods->disassemble_init) (cinfo);

  /* Loop over scan's data: rows_in_mem pixel rows are processed per loop */

  pixel_rows_output = 0;
  whichss = 1;			/* arrange to start with subsampled_data[0] */

  for (cur_mcu_row = 0; cur_mcu_row < cinfo->MCU_rows_in_scan;
       cur_mcu_row += mcu_rows_per_loop) {
    whichss ^= 1;		/* switch to other subsample buffer */

    /* Obtain v_samp_factor block rows of each component in the scan. */
    /* This is a single MCU row if interleaved, multiple MCU rows if not. */
    /* In the noninterleaved case there might be fewer than v_samp_factor */
    /* block rows remaining; if so, pad with copies of the last pixel row */
    /* so that unsubsampling doesn't have to treat it as a special case. */

    for (ri = 0; ri < mcu_rows_per_loop; ri++) {
      if (cur_mcu_row + ri < cinfo->MCU_rows_in_scan) {
	/* OK to actually read an MCU row. */
#ifdef BLOCK_SMOOTHING_SUPPORTED
	if (cinfo->do_block_smoothing)
	  get_smoothed_row(cinfo, coeff_data,
			   bsmooth, &whichb, cur_mcu_row + ri);
	else
#endif
	  (*cinfo->methods->disassemble_MCU) (cinfo, coeff_data);
      
	reverse_DCT(cinfo, coeff_data, subsampled_data[whichss],
		    ri * DCTSIZE);
      } else {
	/* Need to pad out with copies of the last subsampled row. */
	/* This can only happen if there is just one component. */
	duplicate_row(subsampled_data[whichss][0],
		      cinfo->cur_comp_info[0]->subsampled_width,
		      ri * DCTSIZE - 1, DCTSIZE);
      }
    }

    /* Unsubsample the data */
    /* First time through is a special case */

    if (cur_mcu_row) {
      /* Expand last row group of previous set */
      jexpand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width,
	     (short) DCTSIZE, (short) (DCTSIZE+1), (short) 0,
	     (short) (DCTSIZE-1));
      /* and dump the previous set's expanded data */
      emit_1pass (cinfo, rows_in_mem, fullsize_data, color_data);
      pixel_rows_output += rows_in_mem;
      /* Expand first row group of this set */
      jexpand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width,
	     (short) (DCTSIZE+1), (short) 0, (short) 1,
	     (short) 0);
    } else {
      /* Expand first row group with dummy above-context */
      jexpand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width,
	     (short) (-1), (short) 0, (short) 1,
	     (short) 0);
    }
    /* Expand second through next-to-last row groups of this set */
    for (i = 1; i <= DCTSIZE-2; i++) {
      jexpand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width,
	     (short) (i-1), (short) i, (short) (i+1),
	     (short) i);
    }
  } /* end of outer loop */

  /* Expand the last row group with dummy below-context */
  /* Note whichss points to last buffer side used */
  jexpand(cinfo, subsampled_data[whichss], fullsize_data, fullsize_width,
	 (short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1),
	 (short) (DCTSIZE-1));
  /* and dump the remaining data (may be less than full height) */
  emit_1pass (cinfo, (int) (cinfo->image_height - pixel_rows_output),
	      fullsize_data, color_data);

  /* Clean up after the scan */
  (*cinfo->methods->disassemble_term) (cinfo);
  (*cinfo->methods->unsubsample_term) (cinfo);
  (*cinfo->methods->entropy_decoder_term) (cinfo);
  (*cinfo->methods->read_scan_trailer) (cinfo);

  /* Verify that we've seen the whole input file */
  if ((*cinfo->methods->read_scan_header) (cinfo))
    ERREXIT(cinfo->emethods, "Didn't expect more than one scan");

  /* Release working memory */
  free_MCU_row(cinfo, coeff_data);
#ifdef BLOCK_SMOOTHING_SUPPORTED
  if (cinfo->do_block_smoothing) {
    free_MCU_row(cinfo, bsmooth[0]);
    free_MCU_row(cinfo, bsmooth[1]);
    free_MCU_row(cinfo, bsmooth[2]);
  }
#endif
  free_sampling_buffer(cinfo, subsampled_data);
  free_sampimage(cinfo, fullsize_data, (int) cinfo->num_components,
		 (long) rows_in_mem);
  free_sampimage(cinfo, color_data, (int) cinfo->color_out_comps,
		 (long) rows_in_mem);
}

/*
 * Decompression pipeline controller used for multiple-scan files
 * without 2-pass color quantization.
 *
 * The current implementation places the "big" buffer at the stage of
 * desubsampled data.  Buffering subsampled data instead would reduce the
 * size of temp files (by about a factor of 2 in typical cases).  However,
 * the unsubsampling logic is dependent on the assumption that unsubsampling
 * occurs during a scan, so it's much easier to do the enlargement as the
 * JPEG file is read.  This also simplifies life for the memory manager,
 * which would otherwise have to deal with overlapping access_big_sarray()
 * requests.
 *
 * At present it appears that most JPEG files will be single-scan, so
 * it doesn't seem worthwhile to try to make this implementation smarter.
 */

#ifdef MULTISCAN_FILES_SUPPORTED

METHODDEF void
multi_dcontroller (cinfo)
decompress_info_ptr cinfo;
{
  long fullsize_width;		/* # of samples per row in full-size buffers */
  long cur_mcu_row;		/* counts # of MCU rows processed */
  long pixel_rows_output;	/* # of pixel rows actually emitted */
  int mcu_rows_per_loop;	/* # of MCU rows processed per outer loop */
  /* Work buffer for dequantized coefficients (IDCT input) */
  JBLOCKIMAGE coeff_data;
  /* Work buffer for cross-block smoothing input */
#ifdef BLOCK_SMOOTHING_SUPPORTED
  JBLOCKIMAGE bsmooth[3];	/* this is optional */
  int whichb;
#endif
  /* Work buffer for subsampled image data (see comments at head of file) */
  JSAMPIMAGE subsampled_data[2];
  /* Full-image buffer holding desubsampled, but not color-converted, data */
  big_sarray_ptr *fullsize_image;
  JSAMPIMAGE fullsize_ptrs;	/* workspace for access_big_sarray() results */
  /* Work buffer for color conversion output (full size) */
  JSAMPIMAGE color_data;
  int whichss, ri;
  short ci, i;

  /* Compute dimensions of full-size pixel buffers */
  /* Note these are the same whether interleaved or not. */
  rows_in_mem = cinfo->max_v_samp_factor * DCTSIZE;
  fullsize_width = jround_up(cinfo->image_width,
			     (long) (cinfo->max_h_samp_factor * DCTSIZE));

  /* Allocate all working memory that doesn't depend on scan info */
  /* color_data is the result of the colorspace conversion step */
  color_data = alloc_sampimage(cinfo, (int) cinfo->color_out_comps,
			       (long) rows_in_mem, fullsize_width);

  /* Get a big image: fullsize_image is sample data after unsubsampling. */
  fullsize_image = (big_sarray_ptr *) (*cinfo->emethods->alloc_small)
			(cinfo->num_components * SIZEOF(big_sarray_ptr));
  for (ci = 0; ci < cinfo->num_components; ci++) {
    fullsize_image[ci] = (*cinfo->emethods->request_big_sarray)
			(fullsize_width,
			 jround_up(cinfo->image_height, (long) rows_in_mem),
			 (long) rows_in_mem);
  }
  /* Also get an area for pointers to currently accessible chunks */
  fullsize_ptrs = (JSAMPIMAGE) (*cinfo->emethods->alloc_small)
				(cinfo->num_components * SIZEOF(JSAMPARRAY));

  /* Tell the memory manager to instantiate big arrays */
  (*cinfo->emethods->alloc_big_arrays)
	 /* extra sarray space is for subsampled-data buffers: */
	((long) (fullsize_width			/* max width in samples */
	 * cinfo->max_v_samp_factor*(DCTSIZE+2)	/* max height */
	 * cinfo->num_components),		/* max components per scan */
	 /* extra barray space is for MCU-row buffers: */
	 (long) ((fullsize_width / DCTSIZE)	/* max width in blocks */
	 * cinfo->max_v_samp_factor		/* max height */
	 * cinfo->num_components		/* max components per scan */
	 * (cinfo->do_block_smoothing ? 4 : 1)),/* how many of these we need */
	 /* no extra "medium"-object space */
	 (long) 0);


  /* Loop over scans in file */

  do {
    
    /* Prepare for this scan */
    if (cinfo->comps_in_scan == 1) {
      noninterleaved_scan_setup(cinfo);
      /* Need to read Vk MCU rows to obtain Vk block rows */
      mcu_rows_per_loop = cinfo->cur_comp_info[0]->v_samp_factor;
    } else {
      interleaved_scan_setup(cinfo);
      /* in an interleaved scan, one MCU row provides Vk block rows */
      mcu_rows_per_loop = 1;
    }
    
    /* Allocate scan-local working memory */
    /* coeff_data holds a single MCU row of coefficient blocks */
    coeff_data = alloc_MCU_row(cinfo);
    /* if doing cross-block smoothing, need extra space for its input */
#ifdef BLOCK_SMOOTHING_SUPPORTED
    if (cinfo->do_block_smoothing) {
      bsmooth[0] = alloc_MCU_row(cinfo);
      bsmooth[1] = alloc_MCU_row(cinfo);
      bsmooth[2] = alloc_MCU_row(cinfo);
    }
#endif
    /* subsampled_data is sample data before unsubsampling */
    alloc_sampling_buffer(cinfo, subsampled_data);

    /* line up the big buffers */
    for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
      fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray)
	(fullsize_image[cinfo->cur_comp_info[ci]->component_index],
	 (long) 0, TRUE);
    }
    
    /* Initialize to read scan data */
    
    (*cinfo->methods->entropy_decoder_init) (cinfo);
    (*cinfo->methods->unsubsample_init) (cinfo);
    (*cinfo->methods->disassemble_init) (cinfo);
    
    /* Loop over scan's data: rows_in_mem pixel rows are processed per loop */
    
    pixel_rows_output = 0;
    whichss = 1;		/* arrange to start with subsampled_data[0] */
    
    for (cur_mcu_row = 0; cur_mcu_row < cinfo->MCU_rows_in_scan;
	 cur_mcu_row += mcu_rows_per_loop) {
      whichss ^= 1;		/* switch to other subsample buffer */

      /* Obtain v_samp_factor block rows of each component in the scan. */
      /* This is a single MCU row if interleaved, multiple MCU rows if not. */
      /* In the noninterleaved case there might be fewer than v_samp_factor */
      /* block rows remaining; if so, pad with copies of the last pixel row */
      /* so that unsubsampling doesn't have to treat it as a special case. */
      
      for (ri = 0; ri < mcu_rows_per_loop; ri++) {
	if (cur_mcu_row + ri < cinfo->MCU_rows_in_scan) {
	  /* OK to actually read an MCU row. */
#ifdef BLOCK_SMOOTHING_SUPPORTED
	  if (cinfo->do_block_smoothing)
	    get_smoothed_row(cinfo, coeff_data,
			     bsmooth, &whichb, cur_mcu_row + ri);
	  else
#endif
	    (*cinfo->methods->disassemble_MCU) (cinfo, coeff_data);
	  
	  reverse_DCT(cinfo, coeff_data, subsampled_data[whichss],
		      ri * DCTSIZE);
	} else {
	  /* Need to pad out with copies of the last subsampled row. */
	  /* This can only happen if there is just one component. */
	  duplicate_row(subsampled_data[whichss][0],
			cinfo->cur_comp_info[0]->subsampled_width,
			ri * DCTSIZE - 1, DCTSIZE);
	}
      }
      
      /* Unsubsample the data */
      /* First time through is a special case */
      
      if (cur_mcu_row) {
	/* Expand last row group of previous set */
	jexpand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width,
	       (short) DCTSIZE, (short) (DCTSIZE+1), (short) 0,
	       (short) (DCTSIZE-1));
	/* Realign the big buffers */
	pixel_rows_output += rows_in_mem;
	for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
	  fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray)
	    (fullsize_image[cinfo->cur_comp_info[ci]->component_index],
	     pixel_rows_output, TRUE);
	}
	/* Expand first row group of this set */
	jexpand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width,
	       (short) (DCTSIZE+1), (short) 0, (short) 1,
	       (short) 0);
      } else {
	/* Expand first row group with dummy above-context */
	jexpand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width,
	       (short) (-1), (short) 0, (short) 1,
	       (short) 0);
      }
      /* Expand second through next-to-last row groups of this set */
      for (i = 1; i <= DCTSIZE-2; i++) {
	jexpand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width,
	       (short) (i-1), (short) i, (short) (i+1),
	       (short) i);
      }
    } /* end of outer loop */
    
    /* Expand the last row group with dummy below-context */
    /* Note whichss points to last buffer side used */
    jexpand(cinfo, subsampled_data[whichss], fullsize_ptrs, fullsize_width,
	   (short) (DCTSIZE-2), (short) (DCTSIZE-1), (short) (-1),
	   (short) (DCTSIZE-1));
    
    /* Clean up after the scan */
    (*cinfo->methods->disassemble_term) (cinfo);
    (*cinfo->methods->unsubsample_term) (cinfo);
    (*cinfo->methods->entropy_decoder_term) (cinfo);
    (*cinfo->methods->read_scan_trailer) (cinfo);

    /* Release scan-local working memory */
    free_MCU_row(cinfo, coeff_data);
#ifdef BLOCK_SMOOTHING_SUPPORTED
    if (cinfo->do_block_smoothing) {
      free_MCU_row(cinfo, bsmooth[0]);
      free_MCU_row(cinfo, bsmooth[1]);
      free_MCU_row(cinfo, bsmooth[2]);
    }
#endif
    free_sampling_buffer(cinfo, subsampled_data);
    
    /* Repeat if there is another scan */
  } while ((*cinfo->methods->read_scan_header) (cinfo));

  /* Now that we've collected all the data, color convert & output it. */

  for (pixel_rows_output = 0; pixel_rows_output < cinfo->image_height;
       pixel_rows_output += rows_in_mem) {

    /* realign the big buffers */
    for (ci = 0; ci < cinfo->num_components; ci++) {
      fullsize_ptrs[ci] = (*cinfo->emethods->access_big_sarray)
	(fullsize_image[ci], pixel_rows_output, FALSE);
    }

    emit_1pass (cinfo,
		(int) MIN(rows_in_mem, cinfo->image_height-pixel_rows_output),
		fullsize_ptrs, color_data);
  }

  /* Release working memory */
  free_sampimage(cinfo, color_data, (int) cinfo->color_out_comps,
		 (long) rows_in_mem);
  for (ci = 0; ci < cinfo->num_components; ci++) {
    (*cinfo->emethods->free_big_sarray) (fullsize_image[ci]);
  }
  (*cinfo->emethods->free_small) ((void *) fullsize_image);
  (*cinfo->emethods->free_small) ((void *) fullsize_ptrs);
}

#endif /* MULTISCAN_FILES_SUPPORTED */


/*
 * Decompression pipeline controller used for multiple-scan files
 * with 2-pass color quantization.
 */

#ifdef MULTISCAN_FILES_SUPPORTED
#ifdef QUANT_2PASS_SUPPORTED

METHODDEF void
multi_2quant_dcontroller
cinfo)  decompress_info_ptr cinfo;
{
  ERREXIT(cinfo->emethods, "Not implemented yet");
}

#endif /* QUANT_2PASS_SUPPORTED */
#endif /* MULTISCAN_FILES_SUPPORTED */


/*
 * The method selection routine for decompression pipeline controllers.
 * Note that at this point we've already read the JPEG header and first SOS,
 * so we can tell whether the input is one scan or not.
 */

GLOBAL void
jseldpipeline (cinfo)
decompress_info_ptr cinfo;
{
  if (cinfo->comps_in_scan == cinfo->num_components) {
    /* It's a single-scan file */
      cinfo->methods->d_pipeline_controller = single_dcontroller;
  } else {
    /* It's a multiple-scan file */
#ifdef MULTISCAN_FILES_SUPPORTED
    cinfo->methods->d_pipeline_controller = multi_dcontroller;
#else
    ERREXIT(cinfo->emethods, "Multiple-scan support was not compiled");
#endif
  }
}
/* ########################################################################## */
/*
 * jbsmooth.c
 *
 */

#ifdef BLOCK_SMOOTHING_SUPPORTED

/*
 * Cross-block coefficient smoothing.
 */

METHODDEF void
smooth_coefficients (cinfo, compptr, above, currow, below, output)
decompress_info_ptr cinfo;
jpeg_component_info *compptr;
JBLOCKROW above;
JBLOCKROW currow;
JBLOCKROW below;
JBLOCKROW output;
{
  QUANT_TBL_PTR Qptr = cinfo->quant_tbl_ptrs[compptr->quant_tbl_no];
  long blocks_in_row = compptr->subsampled_width / DCTSIZE;
  long col;

  /* First, copy the block row as-is.
   * This takes care of the first & last blocks in the row, the top/bottom
   * special cases, and the higher-order coefficients in each block.
   */
  jcopy_block_row(currow, output, blocks_in_row);

  /* Now apply the smoothing calculation, but not to any blocks on the
   * edges of the image.
   */

  if (above != NULL && below != NULL) {
    for (col = 1; col < blocks_in_row-1; col++) {

      /* See section 13.10 of JPEG-8-R8, or K.8 of JPEG-9-R6.
       *
       * As I understand it, this produces approximations
       * for the low frequency AC components, based on the
       * DC values of the block and its eight neighboring blocks.
       * (Thus it can't be used for blocks on the image edges.)
       */

      /* The layout of these variables corresponds to
       * the text in 13.10
       */
      
      JCOEF DC1, DC2, DC3;
      JCOEF DC4, DC5, DC6;
      JCOEF DC7, DC8, DC9;
      
      long       AC01, AC02;
      long AC10, AC11;
      long AC20;
      
      DC1 = above [col-1][0];
      DC2 = above [col  ][0];
      DC3 = above [col+1][0];
      DC4 = currow[col-1][0];
      DC5 = currow[col  ][0];
      DC6 = currow[col+1][0];
      DC7 = below [col-1][0];
      DC8 = below [col  ][0];
      DC9 = below [col+1][0];
      
#define DIVIDE_256(x)	x = ( (x) < 0 ? -((128-(x))/256) : ((x)+128)/256 )
      
      AC01 = (36 * (DC4 - DC6));
      DIVIDE_256(AC01);
      AC10 = (36 * (DC2 - DC8));
      DIVIDE_256(AC10);
      AC20 = (9 * (DC2 + DC8 - 2*DC5));
      DIVIDE_256(AC20);
      AC11 = (5 * ((DC1 - DC3) - (DC7 - DC9)));
      DIVIDE_256(AC11);
      AC02 = (9 * (DC4 + DC6 - 2*DC5));
      DIVIDE_256(AC02);
      
      /* I think that this checks to see if the quantisation
       * on the transmitting side would have produced this
       * answer. If so, then we use our (hopefully better)
       * estimate.
       */

#define ABS(x)	((x) < 0 ? -(x) : (x))

#define COND_ASSIGN(_ac,_n,_z)   if ((ABS(output[col][_n] - (_ac))<<1) <= Qptr[_z]) output[col][_n] = (_ac)

      COND_ASSIGN(AC01,  1, 1);
      COND_ASSIGN(AC02,  2, 5);
      COND_ASSIGN(AC10,  8, 2);
      COND_ASSIGN(AC11,  9, 4);
      COND_ASSIGN(AC20, 16, 3);
    }
  }
}


/*
 * The method selection routine for cross-block smoothing.
 */

GLOBAL void
jselbsmooth (cinfo)
decompress_info_ptr cinfo;
{
  /* just one implementation for now */
  cinfo->methods->smooth_coefficients = smooth_coefficients;
}

#endif /* BLOCK_SMOOTHING_SUPPORTED */

/* ########################################################################## */
/*
 * jdarith.c
 *
 */

#ifdef ARITH_CODING_SUPPORTED


/*
 * The arithmetic coding option of the JPEG standard specifies Q-coding,
 * which is covered by patents held by IBM (and possibly AT&T and Mitsubishi).
 * At this time it does not appear to be legal for the Independent JPEG
 * Group to distribute software that implements arithmetic coding.
 * We have therefore removed arithmetic coding support from the
 * distributed source code.
 *
 * We're not happy about it either.
 */


/*
 * The method selection routine for arithmetic entropy decoding.
 */

GLOBAL void
jseldarithmetic (cinfo)
decompress_info_ptr cinfo;
{
  if (cinfo->arith_code) {
    ERREXIT(cinfo->emethods, "Sorry, there are legal restrictions on arithmetic coding");
  }
}

#endif /* ARITH_CODING_SUPPORTED */

/* ########################################################################## */


/*
 * jdhuff.c
 *
 */


/* Static variables to avoid passing 'round extra parameters */

static decompress_info_ptr dcinfo;

static unsigned int get_buffer; /* current bit-extraction buffer */
static int bits_left;		/* # of unused bits in it */


LOCAL void
fix_huff_tbl (htbl)
HUFF_TBL * htbl;
/* Compute derived values for a Huffman table */
{
  int p, i, l, lastp, si;
  char huffsize[257];
  UINT16 huffcode[257];
  UINT16 code;
  
  /* Figure 7.3.5.4.2.1: make table of Huffman code length for each symbol */
  /* Note that this is in code-length order. */

  p = 0;
  for (l = 1; l <= 16; l++) {
    for (i = 1; i <= htbl->bits[l]; i++)
      huffsize[p++] = l;
  }
  huffsize[p] = 0;
  lastp = p;
  
  /* Figure 7.3.5.4.2.2: generate the codes themselves */
  /* Note that this is in code-length order. */
  
  code = 0;
  si = huffsize[0];
  p = 0;
  while (huffsize[p]) {
    while (huffsize[p] == si) {
      huffcode[p++] = code;
      code++;
    }
    code <<= 1;
    si++;
  }
  
  /* Figure 7.3.5.4.2.3: generate encoding tables */
  /* These are code and size indexed by symbol value */

  for (p = 0; p < lastp; p++) {
    htbl->ehufco[htbl->huffval[p]] = huffcode[p];
    htbl->ehufsi[htbl->huffval[p]] = huffsize[p];
  }
  
  /* Figure 13.4.2.3.1: generate decoding tables */

  p = 0;
  for (l = 1; l <= 16; l++) {
    if (htbl->bits[l]) {
      htbl->valptr[l] = p;	/* huffval[] index of 1st sym of code len l */
      htbl->mincode[l] = huffcode[p]; /* minimum code of length l */
      p += htbl->bits[l];
      htbl->maxcode[l] = huffcode[p-1];	/* maximum code of length l */
    } else {
      htbl->maxcode[l] = -1;
    }
  }
}


/* Extract the next N bits from the input stream (N <= 8) */

LOCAL int
get_bits (nbits)
int nbits;
{
  int result;
  
  while (nbits > bits_left) {
    int c = JGETC(dcinfo);
    
    get_buffer = (get_buffer << 8) + c;
    bits_left += 8;
    /* If it's 0xFF, check and discard stuffed zero byte */
    if (c == 0xff) {
      c = JGETC(dcinfo);  /* Byte stuffing */
      if (c != 0)
	ERREXIT1(dcinfo->emethods,
		 "Unexpected marker 0x%02x in compressed data", c);
    }
  }
  
  bits_left -= nbits;
  result = (get_buffer >> bits_left) & ((1 << nbits) - 1);
  return result;
}

/* Macro to make things go at some speed! */

#define get_bit()	(bits_left ? \
			 ((get_buffer >> (--bits_left)) & 1) : \
			 get_bits(1))


/* Figure 13.4.2.3.2: extract next coded symbol from input stream */
  
LOCAL int
huff_DECODE (htbl)
HUFF_TBL * htbl;
{
  int l, p;
  INT32 code;
  
  code = get_bit();
  l = 1;
  while (code > htbl->maxcode[l]) {
    code = (code << 1) + get_bit();
    l++;
  }
  
  p = htbl->valptr[l] + (code - htbl->mincode[l]);
  
  return htbl->huffval[p];
}


/* Figure 13.4.2.1.1: extend sign bit */

#define huff_EXTEND(x, s)	((x) < (1 << ((s)-1)) ? \
				 (x) + (-1 << (s)) + 1 : \
				 (x))


/* Decode a single block's worth of coefficients */
/* Note that only the difference is returned for the DC coefficient */

LOCAL void
decode_one_block (block, dctbl, actbl)
JBLOCK block;
HUFF_TBL *dctbl;
HUFF_TBL *actbl;
{
  int s, k, r, n;

  /* zero out the coefficient block */

  memset((void *) block, 0, SIZEOF(JBLOCK));
  
  /* Section 13.4.2.1: decode the DC coefficient difference */

  s = huff_DECODE(dctbl);
  r = get_bits(s);

  /* bug fix: original code did not care if 's' was zero, causing
   * 1 << -1 in the huff_EXTEND macro, which is illegal. - jimf 11.25.91
   */
  if (s)
    block[0] = huff_EXTEND(r, s);
  else
    block[0] = r;
  
  /* Section 13.4.2.2: decode the AC coefficients */
  
  for (k = 1; k < DCTSIZE2; k++) {
    r = huff_DECODE(actbl);
    
    s = r & 15;
    n = r >> 4;
    
    if (s) {
      k = k + n;
      r = get_bits(s);
      block[k] = huff_EXTEND(r, s);
    } else {
      if (n != 15)
	break;
      k += 15;
    }
  }
}


/*
 * Initialize for a Huffman-compressed scan.
 * This is invoked after reading the SOS marker.
 */

METHODDEF void
huff_decoder_init (cinfo)
decompress_info_ptr cinfo;
{
  short ci;
  jpeg_component_info * compptr;

  /* Initialize static variables */
  dcinfo = cinfo;
  bits_left = 0;

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    compptr = cinfo->cur_comp_info[ci];
    /* Make sure requested tables are present */
    if (cinfo->dc_huff_tbl_ptrs[compptr->dc_tbl_no] == NULL ||
	cinfo->ac_huff_tbl_ptrs[compptr->ac_tbl_no] == NULL)
      ERREXIT(cinfo->emethods, "Use of undefined Huffman table");
    /* Compute derived values for Huffman tables */
    /* We may do this more than once for same table, but it's not a big deal */
    fix_huff_tbl(cinfo->dc_huff_tbl_ptrs[compptr->dc_tbl_no]);
    fix_huff_tbl(cinfo->ac_huff_tbl_ptrs[compptr->ac_tbl_no]);
    /* Initialize DC predictions to 0 */
    cinfo->last_dc_val[ci] = 0;
  }

  /* Initialize restart stuff */
  cinfo->restarts_to_go = cinfo->restart_interval;
  cinfo->next_restart_num = 0;
}


/*
 * Check for a restart marker & resynchronize decoder.
 */

LOCAL void
process_restart (cinfo)
decompress_info_ptr cinfo;
{
  int c, nbytes;
  short ci;

  /* Throw away any partial unread byte */
  bits_left = 0;

  /* Scan for next JPEG marker */
  nbytes = 0;
  do {
    do {			/* skip any non-FF bytes */
      nbytes++;
      c = JGETC(cinfo);
    } while (c != 0xFF);
    do {			/* skip any duplicate FFs */
      nbytes++;
      c = JGETC(cinfo);
    } while (c == 0xFF);
  } while (c == 0);		/* repeat if it was a stuffed FF/00 */

  if (c != (RST0 + cinfo->next_restart_num))
    ERREXIT2(cinfo->emethods, "Found 0x%02x marker instead of RST%d",
	     c, cinfo->next_restart_num);

  if (nbytes != 2)
    TRACEMS2(cinfo->emethods, 1, "Skipped %d bytes before RST%d",
	     nbytes-2, cinfo->next_restart_num);
  else
    TRACEMS1(cinfo->emethods, 2, "RST%d", cinfo->next_restart_num);

  /* Re-initialize DC predictions to 0 */
  for (ci = 0; ci < cinfo->comps_in_scan; ci++)
    cinfo->last_dc_val[ci] = 0;

  /* Update restart state */
  cinfo->restarts_to_go = cinfo->restart_interval;
  cinfo->next_restart_num++;
  cinfo->next_restart_num &= 7;
}


/*
 * Decode and return one MCU's worth of Huffman-compressed coefficients.
 */

METHODDEF void
huff_decode (cinfo, MCU_data)
decompress_info_ptr cinfo; JBLOCK *MCU_data;
{
  short blkn, ci;
  jpeg_component_info * compptr;

  /* Account for restart interval, process restart marker if needed */
  if (cinfo->restart_interval) {
    if (cinfo->restarts_to_go == 0)
      process_restart(cinfo);
    cinfo->restarts_to_go--;
  }

  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
    ci = cinfo->MCU_membership[blkn];
    compptr = cinfo->cur_comp_info[ci];
    decode_one_block(MCU_data[blkn],
		     cinfo->dc_huff_tbl_ptrs[compptr->dc_tbl_no],
		     cinfo->ac_huff_tbl_ptrs[compptr->ac_tbl_no]);
    /* Convert DC difference to actual value, update last_dc_val */
    MCU_data[blkn][0] += cinfo->last_dc_val[ci];
    cinfo->last_dc_val[ci] = MCU_data[blkn][0];
  }
}


/*
 * Finish up at the end of a Huffman-compressed scan.
 */

METHODDEF void
huff_decoder_term (cinfo)
decompress_info_ptr cinfo;
{
  /* No work needed */
}


/*
 * The method selection routine for Huffman entropy decoding.
 */

GLOBAL void
jseldhuffman (cinfo)
decompress_info_ptr cinfo;
{
  if (! cinfo->arith_code) {
    cinfo->methods->entropy_decoder_init = huff_decoder_init;
    cinfo->methods->entropy_decode = huff_decode;
    cinfo->methods->entropy_decoder_term = huff_decoder_term;
  }
}

/* ########################################################################## */


/*
 * jdcolor.c
 *
 */

/*
 * Initialize for colorspace conversion.
 */

METHODDEF void
colorout_init (cinfo)
decompress_info_ptr cinfo;
{
  /* no work needed */
}


/*
 * Convert some rows of samples to the output colorspace.
 * This version handles YCbCr -> RGB conversion.
 * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
 * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
 */

METHODDEF void
ycc_rgb_convert (cinfo, num_rows, input_data, output_data)
decompress_info_ptr cinfo; int num_rows;
JSAMPIMAGE input_data; JSAMPIMAGE output_data;
{
  register INT32 y, u, v, x;
  register JSAMPROW inptr0, inptr1, inptr2;
  register JSAMPROW outptr0, outptr1, outptr2;
  register long col;
  register long width = cinfo->image_width;
  register int row;
  
  for (row = 0; row < num_rows; row++) {
    inptr0 = input_data[0][row];
    inptr1 = input_data[1][row];
    inptr2 = input_data[2][row];
    outptr0 = output_data[0][row];
    outptr1 = output_data[1][row];
    outptr2 = output_data[2][row];
    for (col = width; col > 0; col--) {
      y = GETJSAMPLE(*inptr0++);
      u = (int) GETJSAMPLE(*inptr1++) - CENTERJSAMPLE;
      v = (int) GETJSAMPLE(*inptr2++) - CENTERJSAMPLE;
      /* Note: if the inputs were computed directly from RGB values,
       * range-limiting would be unnecessary here; but due to possible
       * noise in the DCT/IDCT phase, we do need to apply range limits.
       */
      y *= 1024;	/* in case compiler can't spot common subexpression */
      x = y          + 1436*v + 512; /* red */
      if (x < 0) x = 0;
      if (x > ((INT32) MAXJSAMPLE*1024)) x = (INT32) MAXJSAMPLE*1024;
      *outptr0++ = x >> 10;
      x = y -  352*u -  731*v + 512; /* green */
      if (x < 0) x = 0;
      if (x > ((INT32) MAXJSAMPLE*1024)) x = (INT32) MAXJSAMPLE*1024;
      *outptr1++ = x >> 10;
      x = y + 1815*u          + 512; /* blue */
      if (x < 0) x = 0;
      if (x > ((INT32) MAXJSAMPLE*1024)) x = (INT32) MAXJSAMPLE*1024;
      *outptr2++ = x >> 10;
    }
  }
}


/*
 * Color conversion for no colorspace change: just copy the data.
 */

METHODDEF void
null_convert (cinfo, num_rows, input_data, output_data)
decompress_info_ptr cinfo; int num_rows;
JSAMPIMAGE input_data; JSAMPIMAGE output_data;
{
  short ci;

  for (ci = 0; ci < cinfo->num_components; ci++) {
    jcopy_sample_rows(input_data[ci], 0, output_data[ci], 0,
		      num_rows, cinfo->image_width);
  }
}


/*
 * Color conversion for grayscale: just copy the data.
 * This also works for YCbCr/YIQ -> grayscale conversion, in which
 * we just copy the Y (luminance) component and ignore chrominance.
 */

METHODDEF void
grayscale_convert (cinfo, num_rows, input_data, output_data)
decompress_info_ptr cinfo; int num_rows;
JSAMPIMAGE input_data; JSAMPIMAGE output_data;
{
  jcopy_sample_rows(input_data[0], 0, output_data[0], 0,
		    num_rows, cinfo->image_width);
}


/*
 * Finish up at the end of the file.
 */

METHODDEF void
colorout_term (cinfo)
decompress_info_ptr cinfo;
{
  /* no work needed */
}


/*
 * The method selection routine for output colorspace conversion.
 */

GLOBAL void
jseldcolor (cinfo)
decompress_info_ptr cinfo;
{
  /* Make sure num_components agrees with jpeg_color_space */
  switch (cinfo->jpeg_color_space) {
  case CS_GRAYSCALE:
    if (cinfo->num_components != 1)
      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
    break;

  case CS_RGB:
  case CS_YIQ:
  case CS_YCbCr:
    if (cinfo->num_components != 3)
      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
    break;

  case CS_CMYK:
    if (cinfo->num_components != 4)
      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
    break;

  default:
    ERREXIT(cinfo->emethods, "Unsupported JPEG colorspace");
    break;
  }

  /* Set color_out_comps and conversion method based on requested space */
  switch (cinfo->out_color_space) {
  case CS_GRAYSCALE:
    cinfo->color_out_comps = 1;
    if (cinfo->jpeg_color_space == CS_GRAYSCALE ||
	cinfo->jpeg_color_space == CS_YCbCr ||
	cinfo->jpeg_color_space == CS_YIQ)
      cinfo->methods->color_convert = grayscale_convert;
    else
      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
    break;

  case CS_RGB:
    cinfo->color_out_comps = 3;
    if (cinfo->jpeg_color_space == CS_YCbCr)
      cinfo->methods->color_convert = ycc_rgb_convert;
    else if (cinfo->jpeg_color_space == CS_RGB)
      cinfo->methods->color_convert = null_convert;
    else
      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
    break;

  case CS_CMYK:
    cinfo->color_out_comps = 4;
    if (cinfo->jpeg_color_space == CS_CMYK)
      cinfo->methods->color_convert = null_convert;
    else
      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
    break;

  default:
    ERREXIT(cinfo->emethods, "Unsupported output colorspace");
    break;
  }

  cinfo->final_out_comps = cinfo->color_out_comps;
  cinfo->methods->colorout_init = colorout_init;
  cinfo->methods->colorout_term = colorout_term;
}

/* ########################################################################## */


/*
 * jdmcu.c
 *
 */

/*
 * Quantization descaling and zigzag reordering
 */


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

static 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
qdescale_zig (input, outputptr, quanttbl)
JBLOCK input;
JBLOCKROW outputptr;
QUANT_TBL_PTR quanttbl;
{
  short i;

  for (i = 0; i < DCTSIZE2; i++) {
    (*outputptr)[ZAG[i]] = (*input++) * (*quanttbl++);
  }
}



/*
 * Fetch one MCU row from entropy_decode, build coefficient array.
 * This version is used for noninterleaved (single-component) scans.
 */

METHODDEF void
disassemble_noninterleaved_MCU (cinfo, image_data)
decompress_info_ptr cinfo;
JBLOCKIMAGE image_data;
{
  JBLOCK MCU_data[1];
  long mcuindex;
  jpeg_component_info * compptr;
  QUANT_TBL_PTR quant_ptr;

  /* this is pretty easy since there is one component and one block per MCU */
  compptr = cinfo->cur_comp_info[0];
  quant_ptr = cinfo->quant_tbl_ptrs[compptr->quant_tbl_no];
  for (mcuindex = 0; mcuindex < cinfo->MCUs_per_row; mcuindex++) {
    /* Fetch the coefficient data */
    (*cinfo->methods->entropy_decode) (cinfo, MCU_data);
    /* Descale, reorder, and distribute it into the image array */
    qdescale_zig(MCU_data[0], image_data[0][0] + mcuindex, quant_ptr);
  }
}


/*
 * Fetch one MCU row from entropy_decode, build coefficient array.
 * This version is used for interleaved (multi-component) scans.
 */

METHODDEF void
disassemble_interleaved_MCU (cinfo, image_data)
decompress_info_ptr cinfo;
JBLOCKIMAGE image_data;
{
  JBLOCK MCU_data[MAX_BLOCKS_IN_MCU];
  long mcuindex;
  short blkn, ci, xpos, ypos;
  jpeg_component_info * compptr;
  QUANT_TBL_PTR quant_ptr;
  JBLOCKROW image_ptr;

  for (mcuindex = 0; mcuindex < cinfo->MCUs_per_row; mcuindex++) {
    /* Fetch the coefficient data */
    (*cinfo->methods->entropy_decode) (cinfo, MCU_data);
    /* Descale, reorder, and distribute it into the image array */
    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++) {
	image_ptr = image_data[ci][ypos] + (mcuindex * compptr->MCU_width);
	for (xpos = 0; xpos < compptr->MCU_width; xpos++) {
	  qdescale_zig(MCU_data[blkn], image_ptr, quant_ptr);
	  image_ptr++;
	  blkn++;
	}
      }
    }
  }
}


/*
 * Initialize for processing a scan.
 */

METHODDEF void
disassemble_init (cinfo)
decompress_info_ptr cinfo;
{
  /* no work for now */
}


/*
 * Clean up after a scan.
 */

METHODDEF void
disassemble_term (cinfo)
decompress_info_ptr cinfo;
{
  /* no work for now */
}



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

GLOBAL void
jseldmcu (cinfo)
decompress_info_ptr cinfo;
{
  if (cinfo->comps_in_scan == 1)
    cinfo->methods->disassemble_MCU = disassemble_noninterleaved_MCU;
  else
    cinfo->methods->disassemble_MCU = disassemble_interleaved_MCU;
  cinfo->methods->disassemble_init = disassemble_init;
  cinfo->methods->disassemble_term = disassemble_term;
}

/* ########################################################################## */


/*
 * jdsample.c
 *
 */

/*
 * Initialize for un-subsampling a scan.
 */

METHODDEF void
unsubsample_init (cinfo)
decompress_info_ptr cinfo;
{
  /* no work for now */
}


/*
 * Un-subsample pixel values of a single component.
 * This version only handles integral sampling ratios.
 */

METHODDEF void
unsubsample (cinfo, which_component, input_cols, input_rows, output_cols, output_rows, above, input_data, below, output_data)
decompress_info_ptr cinfo; int which_component;
long input_cols; int input_rows;
long output_cols; int output_rows;
JSAMPARRAY above; JSAMPARRAY input_data; JSAMPARRAY below;
JSAMPARRAY output_data;
{
  jpeg_component_info * compptr = cinfo->cur_comp_info[which_component];
  short h_expand, v_expand, h, v;
  int inrow, outrow;
  long incol;
  JSAMPROW inptr, outptr;
  JSAMPLE invalue;

  /* TEMP FOR DEBUGGING PIPELINE CONTROLLER */
  if (input_rows != compptr->v_samp_factor ||
      output_rows != cinfo->max_v_samp_factor ||
      (input_cols % compptr->h_samp_factor) != 0 ||
      (output_cols % cinfo->max_h_samp_factor) != 0 ||
      output_cols*compptr->h_samp_factor != input_cols*cinfo->max_h_samp_factor)
    ERREXIT(cinfo->emethods, "Bogus unsubsample parameters");

  h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor;
  v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor;

  outrow = 0;
  for (inrow = 0; inrow < input_rows; inrow++) {
    for (v = 0; v < v_expand; v++) {
      inptr = input_data[inrow];
      outptr = output_data[outrow++];
      for (incol = 0; incol < input_cols; incol++) {
	invalue = GETJSAMPLE(*inptr++);
	for (h = 0; h < h_expand; h++) {
	  *outptr++ = invalue;
	}
      }
    }
  }
}


/*
 * Un-subsample pixel values of a single component.
 * This version handles the special case of a full-size component.
 */

METHODDEF void
fullsize_unsubsample (cinfo, which_component, input_cols, input_rows, output_cols, output_rows, above, input_data, below, output_data)
decompress_info_ptr cinfo; int which_component;
long input_cols; int input_rows;
long output_cols; int output_rows;
JSAMPARRAY above; JSAMPARRAY input_data; JSAMPARRAY below;
JSAMPARRAY output_data;
{
  if (input_cols != output_cols || input_rows != output_rows) /* DEBUG */
    ERREXIT(cinfo->emethods, "Pipeline controller messed up");

  jcopy_sample_rows(input_data, 0, output_data, 0, output_rows, output_cols);
}



/*
 * Clean up after a scan.
 */

METHODDEF void
unsubsample_term (cinfo)
decompress_info_ptr cinfo;
{
  /* no work for now */
}



/*
 * The method selection routine for unsubsampling.
 * Note that we must select a routine for each component.
 */

GLOBAL void
jselunsubsample (cinfo)
decompress_info_ptr cinfo;
{
  short ci;
  jpeg_component_info * compptr;

  if (cinfo->CCIR601_sampling)
    ERREXIT(cinfo->emethods, "CCIR601 subsampling not implemented yet");

  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
    compptr = cinfo->cur_comp_info[ci];
    if (compptr->h_samp_factor == cinfo->max_h_samp_factor &&
	compptr->v_samp_factor == cinfo->max_v_samp_factor)
      cinfo->methods->unsubsample[ci] = fullsize_unsubsample;
    else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 &&
	     (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0)
      cinfo->methods->unsubsample[ci] = unsubsample;
    else
      ERREXIT(cinfo->emethods, "Fractional subsampling not implemented yet");
  }

  cinfo->methods->unsubsample_init = unsubsample_init;
  cinfo->methods->unsubsample_term = unsubsample_term;
}

/* ########################################################################## */


/*
 * jrevdct.c
 *
 */

/* The poop on this scaling stuff is as follows:
 *
 * Most of the numbers (after multiplication by the constants) are
 * (logically) shifted left by LG2_DCT_SCALE. This is undone by UNFIXH
 * before assignment to the output array. Note that we want an additional
 * division by 2 on the output (required by the equations).
 *
 * If right shifts are unsigned, then there is a potential problem.
 * However, shifting right by 16 and then assigning to a short
 * (assuming short = 16 bits) will keep the sign right!!
 *
 * For other shifts,
 *
 *     ((x + (1 << 30)) >> shft) - (1 << (30 - shft))
 *
 * gives a nice right shift with sign (assuming no overflow). However, all the
 * scaling is such that this isn't a problem. (Is this true?)
 */


#define ONE 1L			/* remove L if long > 32 bits */

#ifdef RIGHT_SHIFT_IS_UNSIGNED
#define LG2_DCT_SCALE 15
#define RIGHT_SHIFT(_x,_shft)   ((((_x) + (ONE << 30)) >> (_shft)) - (ONE << (30 - (_shft))))
#else
#define LG2_DCT_SCALE 16
#define RIGHT_SHIFT(_x,_shft)   ((_x) >> (_shft))
#endif

#define DCT_SCALE (ONE << LG2_DCT_SCALE)

#define LG2_OVERSCALE 2
#define OVERSCALE (ONE << LG2_OVERSCALE)

#define FIX(x)  ((INT32) ((x) * DCT_SCALE + 0.5))
#define FIXO(x)  ((INT32) ((x) * DCT_SCALE / OVERSCALE + 0.5))
#define UNFIX(x)   RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1)), LG2_DCT_SCALE)
#define UNFIXH(x)  RIGHT_SHIFT((x) + (ONE << LG2_DCT_SCALE), LG2_DCT_SCALE+1)
#define UNFIXO(x)  RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1-LG2_OVERSCALE)), LG2_DCT_SCALE-LG2_OVERSCALE)
#define OVERSH(x)   ((x) << LG2_OVERSCALE)

#define SIN_1_4 FIX(0.7071067811856476)
#define COS_1_4 SIN_1_4

#define SIN_1_8 FIX(0.3826834323650898)
#define COS_1_8 FIX(0.9238795325112870)
#define SIN_3_8 COS_1_8
#define COS_3_8 SIN_1_8

#define SIN_1_16 FIX(0.1950903220161282)
#define COS_1_16 FIX(0.9807852804032300)
#define SIN_7_16 COS_1_16
#define COS_7_16 SIN_1_16

#define SIN_3_16 FIX(0.5555702330196022)
#define COS_3_16 FIX(0.8314696123025450)
#define SIN_5_16 COS_3_16
#define COS_5_16 SIN_3_16

#define OSIN_1_4 FIXO(0.707106781185647)
#define OCOS_1_4 OSIN_1_4

#define OSIN_1_8 FIXO(0.3826834323650898)
#define OCOS_1_8 FIXO(0.9238795325112870)
#define OSIN_3_8 OCOS_1_8
#define OCOS_3_8 OSIN_1_8

#define OSIN_1_16 FIXO(0.1950903220161282)
#define OCOS_1_16 FIXO(0.9807852804032300)
#define OSIN_7_16 OCOS_1_16
#define OCOS_7_16 OSIN_1_16

#define OSIN_3_16 FIXO(0.5555702330196022)
#define OCOS_3_16 FIXO(0.8314696123025450)
#define OSIN_5_16 OCOS_3_16
#define OCOS_5_16 OSIN_3_16


/* INLINE SYSVR3 + gcc -traditional trips on this */
LOCAL void
fast_idct_8 (in, stride)
DCTELEM *in;
int stride;
{
  /* tmp1x are new values of tmpx -- flashy register colourers
   * should be able to do this lot very well
   */
  INT32 tmp10, tmp11, tmp12, tmp13;
  INT32 tmp20, tmp21, tmp22, tmp23;
  INT32 tmp30, tmp31;
  INT32 tmp40, tmp41, tmp42, tmp43;
  INT32 tmp50, tmp51, tmp52, tmp53;
  INT32 in0, in1, in2, in3, in4, in5, in6, in7;
  
  in0 = in[       0];
  in1 = in[stride  ];
  in2 = in[stride*2];
  in3 = in[stride*3];
  in4 = in[stride*4];
  in5 = in[stride*5];
  in6 = in[stride*6];
  in7 = in[stride*7];
  
  tmp10 = (in0 + in4) * COS_1_4;
  tmp11 = (in0 - in4) * COS_1_4;
  tmp12 = in2 * SIN_1_8 - in6 * COS_1_8;
  tmp13 = in6 * SIN_1_8 + in2 * COS_1_8;
  
  tmp20 = tmp10 + tmp13;
  tmp21 = tmp11 + tmp12;
  tmp22 = tmp11 - tmp12;
  tmp23 = tmp10 - tmp13;
  
  tmp30 = UNFIXO((in3 + in5) * COS_1_4);
  tmp31 = UNFIXO((in3 - in5) * COS_1_4);
  
  tmp40 = OVERSH(in1) + tmp30;
  tmp41 = OVERSH(in7) + tmp31;
  tmp42 = OVERSH(in1) - tmp30;
  tmp43 = OVERSH(in7) - tmp31;
  
  tmp50 = tmp40 * OCOS_1_16 + tmp41 * OSIN_1_16;
  tmp51 = tmp40 * OSIN_1_16 - tmp41 * OCOS_1_16;
  tmp52 = tmp42 * OCOS_5_16 + tmp43 * OSIN_5_16;
  tmp53 = tmp42 * OSIN_5_16 - tmp43 * OCOS_5_16;
  
  in[       0] = UNFIXH(tmp20 + tmp50);
  in[stride  ] = UNFIXH(tmp21 + tmp53);
  in[stride*2] = UNFIXH(tmp22 + tmp52);
  in[stride*3] = UNFIXH(tmp23 + tmp51);
  in[stride*4] = UNFIXH(tmp23 - tmp51);
  in[stride*5] = UNFIXH(tmp22 - tmp52);
  in[stride*6] = UNFIXH(tmp21 - tmp53);
  in[stride*7] = UNFIXH(tmp20 - tmp50);
}


/*
 * Perform the inverse DCT on one block of coefficients.
 *
 * Note that this code is specialized to the case DCTSIZE = 8.
 */

GLOBAL void
j_rev_dct (data)
DCTBLOCK data;
{
  int i;
  
  for (i = 0; i < DCTSIZE; i++)
    fast_idct_8(data+i*DCTSIZE, 1);
  
  for (i = 0; i < DCTSIZE; i++)
    fast_idct_8(data+i, DCTSIZE);
}

/* ########################################################################## */


/*
 * jutils.c
 */

GLOBAL void
jcopy_sample_rows (input_array, source_row, output_array, dest_row, num_rows, num_cols)
JSAMPARRAY input_array;
int source_row;
JSAMPARRAY output_array; int dest_row;
int num_rows; long num_cols;
/* Copy some rows of samples from one place to another.
 * num_rows rows are copied from input_array[source_row++]
 * to output_array[dest_row++]; these areas should not overlap.
 * The source and destination arrays must be at least as wide as num_cols.
 */
{
  register JSAMPROW inptr, outptr;
  register size_t count = num_cols * SIZEOF(JSAMPLE);
  register int row;

  input_array += source_row;
  output_array += dest_row;

  for (row = num_rows; row > 0; row--) {
    inptr = *input_array++;
    outptr = *output_array++;
    memcpy((void *)outptr, (void *)inptr, count);
  }
}

/* ########################################################################## */

/*
 * jvirtmem.c
 *
 */

/*
 * Some important notes:
 *   The array alloc/dealloc routines are not merely a convenience;
 *   on 80x86 machines the bottom-level pointers in an array are FAR
 *   and thus may not be allocatable by alloc_small.
 *
 *   Also, it's not a good idea to try to merge the sarray and barray
 *   routines, even though they are textually almost the same, because
 *   samples are usually stored as bytes while coefficients are shorts.
 *   Thus, in machines where byte pointers have a different representation
 *   from word pointers, the resulting machine code could not be the same.
 */


static external_methods_ptr methods; /* saved for access to error_exit */


LOCAL void
out_of_memory (which)
int which;
/* Report an out-of-memory error and stop execution */
{
  ERREXIT1(methods, "Insufficient memory (case %d)", which);
}



METHODDEF void *
alloc_small (sizeofobject)
size_t sizeofobject;
/* Allocate a "small" (all-in-memory) object */
{
  void * result;

  result = lmalloc(sizeofobject);
  if (result == NULL)
    out_of_memory(1);
  return result;
}


METHODDEF void
free_small (ptr)
void *ptr;
/* Free a "small" (all-in-memory) object */
{
  lfree(ptr);

}

METHODDEF JSAMPARRAY
alloc_small_sarray (samplesperrow, numrows)
long samplesperrow; long numrows;
/* Allocate a "small" (all-in-memory) 2-D sample array */
{
  JSAMPARRAY result;
  long i;

  /* Get space for row pointers; this is always "near" on 80x86 */
  result = (JSAMPARRAY) alloc_small((size_t) (numrows * SIZEOF(JSAMPROW)));

  /* Get the rows themselves; on 80x86 these are "far" */
  for (i = 0; i < numrows; i++) {
    result[i] = (JSAMPROW) lmalloc((size_t) (samplesperrow * SIZEOF(JSAMPLE)));
    if (result[i] == NULL)
      out_of_memory(3);
  }

  return result;
}


METHODDEF void
free_small_sarray (ptr, numrows)
JSAMPARRAY ptr; long numrows;
/* Free a "small" (all-in-memory) 2-D sample array */
{
  long i;

  /* Free the rows themselves; on 80x86 these are "far" */
  for (i = 0; i < numrows; i++) {
    lfree((void *) ptr[i]);
  }

  /* Free space for row pointers; this is always "near" on 80x86 */
  free_small((void *) ptr);
}


METHODDEF JBLOCKARRAY
alloc_small_barray (blocksperrow, numrows)
long blocksperrow; long numrows;
/* Allocate a "small" (all-in-memory) 2-D coefficient-block array */
{
  JBLOCKARRAY result;
  long i;

  /* Get space for row pointers; this is always "near" on 80x86 */
  result = (JBLOCKARRAY) alloc_small((size_t) (numrows * SIZEOF(JBLOCKROW)));

  /* Get the rows themselves; on 80x86 these are "far" */
  for (i = 0; i < numrows; i++) {
    result[i] = (JBLOCKROW) lmalloc((size_t) (blocksperrow * SIZEOF(JBLOCK)));
    if (result[i] == NULL)
      out_of_memory(4);
  }

  return result;
}


METHODDEF void
free_small_barray (ptr, numrows)
JBLOCKARRAY ptr; long numrows;
/* Free a "small" (all-in-memory) 2-D coefficient-block array */
{
  long i;

  /* Free the rows themselves; on 80x86 these are "far" */
  for (i = 0; i < numrows; i++) {
    lfree((void *) ptr[i]);
  }

  /* Free space for row pointers; this is always "near" on 80x86 */
  free_small((void *) ptr);

}



/* The control blocks for virtual arrays.
 * These are pretty minimal in this implementation.
 * Note: in this implementation we could realize big arrays
 * at request time and make alloc_big_arrays a no-op;
 * however, doing it separately keeps callers honest.
 */

struct big_sarray_control {
	JSAMPARRAY mem_buffer;	/* memory buffer (the whole thing, here) */
	long rows_in_mem;	/* Height of memory buffer */
	long samplesperrow;	/* Width of memory buffer */
	long unitheight;	/* # of rows accessed by access_big_sarray() */
	big_sarray_ptr next;	/* list link for unrealized arrays */
};

struct big_barray_control {
	JBLOCKARRAY mem_buffer;	/* memory buffer (the whole thing, here) */
	long rows_in_mem;	/* Height of memory buffer */
	long blocksperrow;	/* Width of memory buffer */
	long unitheight;	/* # of rows accessed by access_big_barray() */
	big_barray_ptr next;	/* list link for unrealized arrays */
};


/* Headers of lists of control blocks for unrealized big arrays */
static big_sarray_ptr unalloced_sarrays;
static big_barray_ptr unalloced_barrays;


METHODDEF big_sarray_ptr
request_big_sarray (samplesperrow, numrows, unitheight)
long samplesperrow;
long numrows;
 long unitheight;
/* Request a "big" (virtual-memory) 2-D sample array */
{
  big_sarray_ptr result;

  /* get control block */
  result = (big_sarray_ptr) alloc_small(SIZEOF(struct big_sarray_control));

  result->mem_buffer = NULL;	/* lets access routine spot premature access */
  result->rows_in_mem = numrows;
  result->samplesperrow = samplesperrow;
  result->unitheight = unitheight;
  result->next = unalloced_sarrays; /* add to list of unallocated arrays */
  unalloced_sarrays = result;

  return result;
}


METHODDEF big_barray_ptr
request_big_barray (blocksperrow, numrows, unitheight)
long blocksperrow;
long numrows;
long unitheight;
/* Request a "big" (virtual-memory) 2-D coefficient-block array */
{
  big_barray_ptr result;

  /* get control block */
  result = (big_barray_ptr) alloc_small(SIZEOF(struct big_barray_control));

  result->mem_buffer = NULL;	/* lets access routine spot premature access */
  result->rows_in_mem = numrows;
  result->blocksperrow = blocksperrow;
  result->unitheight = unitheight;
  result->next = unalloced_barrays; /* add to list of unallocated arrays */
  unalloced_barrays = result;

  return result;
}


METHODDEF void
alloc_big_arrays (extra_small_samples, extra_small_blocks, extra_medium_space)
long extra_small_samples;
long extra_small_blocks;
long extra_medium_space;
/* Allocate the in-memory buffers for any unrealized "big" arrays */
/* 'extra' values are upper bounds for total future small-array requests */
/* and far-heap requests */
{
  /* In this implementation we just malloc the whole arrays */
  /* and expect the system's virtual memory to worry about swapping them */
  big_sarray_ptr sptr;
  big_barray_ptr bptr;

  for (sptr = unalloced_sarrays; sptr != NULL; sptr = sptr->next) {
    sptr->mem_buffer = alloc_small_sarray(sptr->samplesperrow,
					  sptr->rows_in_mem);
  }

  for (bptr = unalloced_barrays; bptr != NULL; bptr = bptr->next) {
    bptr->mem_buffer = alloc_small_barray(bptr->blocksperrow,
					  bptr->rows_in_mem);
  }

  unalloced_sarrays = NULL;	/* reset for possible future cycles */
  unalloced_barrays = NULL;
}


METHODDEF JSAMPARRAY
access_big_sarray (ptr, start_row, writable)
big_sarray_ptr ptr;
long start_row;
boolean writable;
/* Access the part of a "big" sample array starting at start_row */
/* and extending for ptr->unitheight rows.  writable is true if  */
/* caller intends to modify the accessed area. */
{
  /* debugging check */
  if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_mem ||
      ptr->mem_buffer == NULL)
    ERREXIT(methods, "Bogus access_big_sarray request");

  return ptr->mem_buffer + start_row;
}


METHODDEF JBLOCKARRAY
access_big_barray (ptr, start_row, writable)
big_barray_ptr ptr;
long start_row;
boolean writable;
/* Access the part of a "big" coefficient-block array starting at start_row */
/* and extending for ptr->unitheight rows.  writable is true if  */
/* caller intends to modify the accessed area. */
{
  /* debugging check */
  if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_mem ||
      ptr->mem_buffer == NULL)
    ERREXIT(methods, "Bogus access_big_barray request");

  return ptr->mem_buffer + start_row;
}


METHODDEF void
free_big_sarray (ptr)
big_sarray_ptr ptr;
/* Free a "big" (virtual-memory) 2-D sample array */
{
  free_small_sarray(ptr->mem_buffer, ptr->rows_in_mem);
  free_small((void *) ptr);	/* free the control block too */
}


METHODDEF void
free_big_barray (ptr)
big_barray_ptr ptr;
/* Free a "big" (virtual-memory) 2-D coefficient-block array */
{
  free_small_barray(ptr->mem_buffer, ptr->rows_in_mem);
  free_small((void *) ptr);	/* free the control block too */
}



/*
 * The method selection routine for virtual memory systems.
 * The system-dependent setup routine should call this routine
 * to install the necessary method pointers in the supplied struct.
 */

GLOBAL void
jselvirtmem (emethods)
external_methods_ptr emethods;
{
  methods = emethods;		/* save struct addr for error exit access */

  emethods->alloc_small = alloc_small;
  emethods->free_small = free_small;
  emethods->alloc_small_sarray = alloc_small_sarray;
  emethods->free_small_sarray = free_small_sarray;
  emethods->alloc_small_barray = alloc_small_barray;
  emethods->free_small_barray = free_small_barray;
  emethods->request_big_sarray = request_big_sarray;
  emethods->request_big_barray = request_big_barray;
  emethods->alloc_big_arrays = alloc_big_arrays;
  emethods->access_big_sarray = access_big_sarray;
  emethods->access_big_barray = access_big_barray;
  emethods->free_big_sarray = free_big_sarray;
  emethods->free_big_barray = free_big_barray;

  unalloced_sarrays = NULL;	/* make sure list headers are empty */
  unalloced_barrays = NULL;
}

/* ########################################################################## */

/*
 * jerror.c
 *
 */

METHODDEF void
trace_message (msgtext)
char *msgtext;
{
  fprintf(stderr, msgtext,
	  methods->message_parm[0], methods->message_parm[1],
	  methods->message_parm[2], methods->message_parm[3],
	  methods->message_parm[4], methods->message_parm[5],
	  methods->message_parm[6], methods->message_parm[7]);
  fprintf(stderr, "\n");
}


METHODDEF void
error_exit (msgtext)
char *msgtext;
{
  trace_message(msgtext);
  cleanup(-1);
}


/*
 * The method selection routine for simple error handling.
 * The system-dependent setup routine should call this routine
 * to install the necessary method pointers in the supplied struct.
 */

GLOBAL void
jselerror (emethods)
external_methods_ptr emethods;
{
  methods = emethods;		/* save struct addr for msg parm access */

  emethods->error_exit = error_exit;
  emethods->trace_message = trace_message;

  emethods->trace_level = 0;	/* default = no tracing */
}

/* ########################################################################## */
