#ifndef lint
static char SccsId[] = "@(#)dispdither.c	1.2  8/12/89";
#endif

/* Module:	dispdthr.c (Display Dither)
 * Purpose:	Map 16 bit data to a single plane bitmap using dithering
 * Subroutine:	dither_sample()			returns: void
 * Subroutine:	dither_replicate()		returns: void
 * Xlib calls:	none
 * Method:	Image dithering based on 16x16 square of values from 0 to 255.
 * Note:	Matrix is short to allow compare with 256 (not a uchar val)
 * Note:	Lookup is used to rescale input data to 0-255 range
 * Note:	Most loops check by pointer val, saving indexing overhead
 * Note:	Seperate code used for forward and reverse video rather than
 *		switching foreground and background val, because non-image
 *		area of display must not change
 * Copyright:	1989, 1995 Smithsonian Astrophysical Observatory
 *		You may do anything you like with this file except remove
 *		this copyright.  The Smithsonian Astrophysical Observatory
 *		makes no representations about the suitability of this
 *		software for any purpose.  It is provided "as is" without
 *		express or implied warranty.
 * Modified:	{0} Michael VanHilst	initial version	     7 July 1989
 *		{1} MVH added ULedge partial replicate	     3  Feb 1990
 *		{2} MVH replaced zoom rep code		    21 June 1991
 *		{3} Doug Mink  cast lookup table for comp     4 May 1995
 *		{n} <who> -- <does what> -- <when>
 */

static unsigned char bit_mask[8] = {
  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

/*
 * Subroutine:	zoom_dither_sample
 * Purpose:	Perform subsampling and dithering to produce a bitmap image
 *		from 16 bit signed data through a scaling lookup table.
 *		Used when bitmap is same size or smaller than data image.
 * Method:	Sample every zoom'th data element in each direction
 * Note:	x, y, width, and height define a subsection within output.
 *		Bits outside of subsection are unchanged.
 */
void dither_sample ( short_data, data_width, zoom, bitmap, bytes_per_line,
		     x, y, width, height, lookup, dither_matrix, inverse )
     short *short_data;			/* i: image of signed short data */
     int data_width;			/* i: row width of short image */
     int zoom;				/* i: zoom factor for subsampling */
     unsigned char *bitmap;		/* o: output bitmap buffer */
     int bytes_per_line;		/* i: bytes per line of bitmap */
     int x, y, width, height;		/* i: dimensions to fill */
     register unsigned char *lookup;	/* i: short to uchar lookup table */
     short *dither_matrix;		/* i: 16x16 matrix for dithering */
     int inverse;			/* i: make 0 on 1, else 1 on 0 */
{
  register short *data;			/* l: ptr to current data input */
  register short *matrix;		/* l: ptr to current dither value */
  register unsigned char *bitmap_byte;	/* l: ptr to current output byte */
  register unsigned char *bitmap_bit;	/* l: current bit in output byte */
  short *data_row;			/* l: beginning of current input row */
  short *matrix_row;			/* l: 1st entry in matrix row */
  short *matrix_row_end;		/* l: last short in matrix row */
  short *matrix_end;			/* l: last short in matrix */
  short *data_row_end;			/* l: last short in input data row */
  unsigned char *bitmap_row;		/* l: first byte of output row */
  unsigned char *bitmap_first_bit;	/* l: first byte to actually write */
  unsigned char *bitmap_last_bit;	/* l: last byte to be affected */
  short x_step;				/* l: sampling step along row */
  short y_step;				/* l: pointer offset to advance rows */
  short i;

  /* point register pointer to data buffer */
  data_row = short_data;
  bitmap_row = bitmap + (y * bytes_per_line) + (x / 8);
  bitmap_first_bit = bit_mask + (x & 7);	/* same as %8 */
  bitmap_last_bit = &(bit_mask[7]);
  /* initialize matrix pointers for use and loop tests */
  matrix_row = dither_matrix;
  matrix_row_end = matrix_row + 16;
  matrix_end = matrix_row + 256;
  /* initial sampling step size for both directions */
  x_step = zoom;
  y_step = data_width * x_step;
  /* width becomes count of data traversed each row */
  width *= zoom;
  /* if high vals are to be white */
  if( inverse ) {
    /* process one row at a time */
    for( i = 0; i < height; i++ ) {
      matrix = matrix_row;
      data = data_row;
      data_row_end = data + width;
      bitmap_byte = bitmap_row;
      bitmap_bit = bitmap_first_bit;
      /* process through the row */
      while( data < data_row_end ) {
	/* set bit by comparing val to matrix entry */
	if( (short)lookup[*data] <= *matrix )
	  *bitmap_byte |= *bitmap_bit;
	data += x_step;
	++matrix;
	/* check for next byte */
	if( ++bitmap_bit > bitmap_last_bit ) {
	  bitmap_bit = bit_mask;
	  ++bitmap_byte;
	  /* matrix row wrap-around happens every other byte edge */
	  if( matrix >= matrix_row_end )
	    matrix = matrix_row;
	}
      }
      /* advance to next row of everything */
      bitmap_row += bytes_per_line;
      data_row += y_step;
      matrix_row = matrix_row_end;
      matrix_row_end += 16;
      /* check for matrix column wrap-around */
      if( matrix_row >= matrix_end ) {
	matrix_row = dither_matrix;
	matrix_row_end = matrix_row + 16;
      }
    }
    /* else foreground is to be black */
  } else {
    /* process one row at a time */
    for( i = 0; i < height; i++ ) {
      matrix = matrix_row;
      data = data_row;
      data_row_end = data + width;
      bitmap_byte = bitmap_row;
      bitmap_bit = bitmap_first_bit;
      /* process through the row */
      while( data < data_row_end ) {
	/* set bit by comparing val to matrix entry */
	if( (short)lookup[*data] > *matrix )
	  *bitmap_byte |= *bitmap_bit;
	data += x_step;
	++matrix;
	/* check for next byte */
	if( ++bitmap_bit > bitmap_last_bit ) {
	  bitmap_bit = bit_mask;
	  ++bitmap_byte;
	  /* matrix row wrap-around happens every other byte edge */
	  if( matrix >= matrix_row_end )
	    matrix = matrix_row;
	}
      }
      /* advance to next row of everything */
      bitmap_row += bytes_per_line;
      data_row += y_step;
      matrix_row = matrix_row_end;
      matrix_row_end += 16;
      /* check for matrix column wrap-around */
      if( matrix_row >= matrix_end ) {
	matrix_row = dither_matrix;
	matrix_row_end = matrix_row + 16;
      }
    }
  }
}

/*
 * Subroutine:	zoom_dither_replicate
 * Purpose:	Perform replication and dithering to produce a bitmap image
 *		from 16 bit signed data through a scaling lookup table.
 *		Used when bitmap is larger than data image.
 * Method:	Repeat data element zoom times in each direction
 */
void dither_replicate ( short_data, data_width, zoom, bitmap, bytes_per_line,
		        first_x_rep, first_y_rep, x, y, width, height,
		        lookup, dither_matrix, inverse )
     short *short_data;			/* i: image of signed short data */
     int data_width;			/* i: row width of short image */
     int zoom;				/* i: zoom factor for replicating */
     unsigned char *bitmap;		/* o: output bitmap buffer */
     int bytes_per_line;		/* i: bytes per line of bitmap */
     int first_x_rep;			/* i: number of reps in 1st column */
     int first_y_rep;			/* i: num of reps in 1st row */
     int x, y, width, height;		/* i: area of output to fill */
     register unsigned char *lookup;	/* i: short to uchar lookup table */
     short *dither_matrix;		/* i: 16x16 matrix for dithering */
     int inverse;			/* i: make 0 on 1, else 1 on 0 */
{
  register short *data;			/* l: ptr to current data input */
  register short *matrix;		/* l: ptr to current dither value */
  register unsigned char *bitmap_byte;	/* l: ptr to current output byte */
  register int bitmap_bit;		/* l: current bit in output byte */
  short *data_row;			/* l: beginning of current input row */
  short *matrix_row;			/* l: 1st entry in matrix row */
  short *matrix_row_end;		/* l: last short in matrix row */
  short *matrix_end;			/* l: last short in matrix */
  short *data_row_end;			/* l: last short in input data row */
  unsigned char *bitmap_row;		/* l: first byte of output row */
  unsigned char *bitmap_row_end;	/* l: first byte in bitmap */
  int bitmap_first_bit;			/* l: 1st bit of first byte affected */
  int bitmap_last_bit;			/* l: last bit of last byte affected */
  int bitmap_bytes_per_row;		/* l: length of output line in bytes */
  int x_step;				/* l: repetition step along row */
  int y_step;				/* l: pointer offset to repeat rows */
  int i;

  /* point register pointer to data buffer */
  data_row = short_data;
  bitmap_row = bitmap + (y * bytes_per_line) + (x / 8);
  /* which bit does this x fall on? ((x & 7) is same as (x % 8)) */
  bitmap_first_bit = x & 7;
  /* which bit will last x fall on */
/*
  bitmap_last_bit = (width + bitmap_first_bit) & 7;
  bitmap_bytes_per_row = (width + bitmap_first_bit + 7) / 8;
*/
  bitmap_last_bit = (width + bitmap_first_bit - 1) & 7;
  bitmap_bytes_per_row = (x + width + 7) / 8;
  if( bitmap_bytes_per_row >= bytes_per_line )
    bitmap_bytes_per_row = bytes_per_line - ((x / 8) + 1);
  else
    bitmap_bytes_per_row -= ((x / 8) + 1);

  /* get matrix pointer and loop references ready */
  matrix_row = dither_matrix;
  matrix_row_end = matrix_row + 16;
  matrix_end = matrix_row + 256;
  /* convert first rep to first step toward zoom reps */
  y_step = 1 + zoom - first_y_rep;
  first_x_rep = 1 + zoom - first_x_rep;
  if( inverse ) {
    /* do one row at a time */
    for( i = 0; i < height; i++ ) {
      /* beginning of row initialization */
      data = data_row;
      bitmap_byte = bitmap_row;
      bitmap_row_end = bitmap_byte + bitmap_bytes_per_row;
      bitmap_bit = bitmap_first_bit;
      /* align matrix with output bytes */
      matrix = matrix_row + bitmap_bit;
      x_step = first_x_rep;
      /* go through the row */
      while( (bitmap_byte < bitmap_row_end) ||
	     ((bitmap_byte == bitmap_row_end) &&
	      (bitmap_bit <= bitmap_last_bit)) ) {
	/* set bit by comparing val to matrix entry */
	if( (short)lookup[*data] <= *matrix )
	  *bitmap_byte |= 1 << bitmap_bit;
	++matrix;
	/* check for next bitmap byte and end of matrix row */
	if( ++bitmap_bit == 8 ) {
	  bitmap_bit = 0;
	  ++bitmap_byte;
	  /* wrap matrix row around on output byte edges */
	  if( matrix >= matrix_row_end )
	    matrix = matrix_row;
	}
	/* move to next val when done with replication */
	if( ++x_step > zoom ) {
	  ++data;
	  x_step = 1;
	}
      }
      bitmap_row += bytes_per_line;
      matrix_row = matrix_row_end;
      matrix_row_end += 16;
      /* check for matrix column wrap-around */
      if( matrix_row >= matrix_end ) {
	matrix_row = dither_matrix;
	matrix_row_end = matrix_row + 16;
      }
      /* advance data row only after zoom repetitions */
      if( ++y_step > zoom ) {
	data_row += data_width;
	y_step = 1;
      }
    }
  } else {
    /* do one row at a time */
    for( i = 0; i < height; i++ ) {
      data = data_row;
      bitmap_byte = bitmap_row;
      bitmap_row_end = bitmap_byte + bitmap_bytes_per_row;
      bitmap_bit = bitmap_first_bit;
      /* align matrix with output bytes */
      matrix = matrix_row + bitmap_bit;
      x_step = first_x_rep;
      /* go through the row */
      while( (bitmap_byte < bitmap_row_end) ||
	     ((bitmap_byte == bitmap_row_end) &&
	      (bitmap_bit <= bitmap_last_bit)) ) {
	/* set bit by comparing val to matrix entry */
	if( (short)lookup[*data] > *matrix )
	  *bitmap_byte |= 1 << bitmap_bit;
	++matrix;
	/* check for next bitmap byte and end of matrix row */
	if( ++bitmap_bit == 8 ) {
	  bitmap_bit = 0;
	  ++bitmap_byte;
	  /* wrap matrix row around on output byte edges */
	  if( matrix >= matrix_row_end )
	    matrix = matrix_row;
	}
	/* move to next val when done with replication */
	if( ++x_step > zoom ) {
	  ++data;
	  x_step = 1;
	}
      }
      bitmap_row += bytes_per_line;
      matrix_row = matrix_row_end;
      matrix_row_end += 16;
      /* check for matrix column wrap-around */
      if( matrix_row >= matrix_end ) {
	matrix_row = dither_matrix;
	matrix_row_end = matrix_row + 16;
      }
      /* advance data row only after zoom repetitions */
      if( ++y_step > zoom ) {
	data_row += data_width;
	y_step = 1;
      }
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1