#ifndef lint
static char SccsId[] = "%W%  %G%";
#endif

/* Module:	sclmap.c (Scale Map)
 * Purpose:	Map image values to display screen values using various
 *		functions
 * Subroutine:	make_scalemap()			returns: void
 * Copyright:	1993 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		  26 May 1989
 *              {2} Doug Mink    find range from histogram   10 November 1993
 *		{n} <who> -- <does what> -- <when>
 */

#include <stdio.h>		/* get stderr, NULL, etc. */
#include <math.h>		/* get the math definitions */
#include <X11/Xlib.h>		/* X window stuff */
#include <X11/Xutil.h>		/* X window manager stuff */
#include "hfiles/constant.h"	/* define codes  */
#include "hfiles/struct.h"	/* declare structure types */
#include "hfiles/extern.h"	/* extern main parameter structures */
#include "hfiles/scale.h"	/* define scaling constants */

#ifndef SUN
#define expm1(a)  (exp(a)-1)
#endif

/*
 * Subroutine:	make_scalemap
 * Purpose:	Make scale map according to type selected
 * Note:	Map goes from image value to hardware value (through ideal
 *		map value using pixels array from XAllocColors)
 */

static void linear_scale(), wrap_scale(), sqrt_scale(), log_scale();

void make_scalemap ( image_min, image_max )
     int image_min, image_max;	/* i: range of mapping for image data input */
{
  void histogram_equalize();

  /* note the range of data for which mapping is to be calculated */
  if( image_max == image_min )
    ++image_max;
  if( image_max < image_min ) {
    int temp;
    temp = image_max;
    image_max = image_min;
    image_min = temp;
  }
  {
    /* fill in map below min with mincolor */
    register int imageval;
    register int pixval;
    register unsigned char *lookup;	/* l: scalemap base (signed index) */
    lookup = buffer.scalemap + SCALEOFF;
    pixval = color.pixvalmap[0];
    for( imageval = -32768; imageval < image_min; imageval++ )
      lookup[imageval] = pixval;
  }
  /* create the image map straight or with some form of scaling */
  switch( color.scale.mode ) {
  case SOP_HistEq:
    /* if there are levels to distribute, call the routine, else do the next */
    if( color.ncolors < (image_max - image_min) ) {
      histogram_equalize(buffer.scalemap + SCALEOFF,
			 buffer.histogram + SCALEOFF, buffer.hist_area,
			 image_min, image_max,
			 color.ncolors, color.pixvalmap);
      break;
    }
    /* fall through to linear if there is nothing to distribute */
  case SOP_Linear:
    linear_scale(image_min, image_max);
    break;
  case SOP_Wrap:
    wrap_scale(image_min, image_max);
    /* don't fill in the top of map */
    return;
  case SOP_Sqrt:
    sqrt_scale(image_min, image_max);
    break;
  case SOP_Log:
    log_scale(image_min, image_max);
    break;
  default:
    (void)fprintf(stderr,"Unknown scaling type request!\n");
  }
  {
    /* fill in top of map with maxcolor */
    register int imageval;
    register int pixval;
    register unsigned char *lookup;	/* l: scalemap base (signed index) */
    lookup = buffer.scalemap + SCALEOFF;
    pixval = color.pixvalmap[color.ncolors - 1];
    for( imageval = image_max; imageval < 32768; imageval++ )
      lookup[imageval] = pixval;
  }
}

/*
 * Subroutine:	linear_scale
 * Purpose:	Distribute color levels in the map evenly
 */

static void get_histogram_range();

static void linear_scale ( image_min, image_max )
  int image_min, image_max;		/* i: limits of values in display */
{
  double scale;
  double upper_bound;
  int maxcolor;
  int level;
  int range_min, range_max;
  unsigned long *pixels;		/* l: ideal byte to hardare byte map */
  register unsigned char *lookup;	/* l: scalemap base (signed offsets) */
  register int imageval;
  register int pixval;
  register int imagelim;

  range_min = image_min;
  range_max = image_max;
  if (buffer.cmdMax <= buffer.cmdMin && !img.imtool_200) {
    get_histogram_range (buffer.histogram, &range_min, &range_max);
    }
  lookup = buffer.scalemap + SCALEOFF;
  maxcolor = color.ncolors - 1;
  pixels = color.pixvalmap;
  /* input range / output range yields input cells per ouput cell */
  scale = (double)(range_max - range_min + 1) / (double)color.ncolors;
  imageval = image_min;
  pixval = pixels[0];

  /* fill in at bottom if range_min > image_min */
  while( imageval < range_min )
    lookup[imageval++] = pixval;

  /* upper bound is ideal edge between colors (offset for rounding) */
  upper_bound = range_min + 0.5;
  level = 0;
  while( level++ < maxcolor ) {
    upper_bound += scale;
    imagelim = (int)upper_bound;
    while( imageval < imagelim )
      lookup[imageval++] = pixval;
    /* level was inc'd after loop test, make pixval for next round */
    pixval = pixels[level];
  }

  /* fill in at top if range_max  < image_max */
  while( imageval <= image_max )
    lookup[imageval++] = pixval;
}

/*
 * Subroutine:	wrap_scale
 * Purpose:	Create the image map with a repeating linear scale
 * Note:	Levels below image_min are not mapped
 */
static void wrap_scale ( image_min, image_max )
  int image_min, image_max;	/* i: limits of values in display */
{
  double scale;
  double range;
  int maxcolor;
  int level;
  unsigned long *pixels;		/* l: ideal byte to hardare byte map */
  register unsigned char *lookup;	/* l: scalemap base (signed offsets) */
  register int imageval;
  register int pixval;
  register int imagelim;

  lookup = buffer.scalemap + SCALEOFF;
  maxcolor = color.ncolors - 1;
  pixels = color.pixvalmap;
  /* input range / output range yields input cells per ouput cell */
  imagelim = (image_max - image_min + 1) / color.scale.wrap_cnt;
  scale = (double)imagelim / (double)color.ncolors;
  /* start at image_min with color level = 0 */
  imageval = image_min;
  level = 0;
  /* use range as floating point boundary between levels */
  range = (double)imageval;
  while( imageval < 32768 ) {
    do {
      /* level was inc'd after loop test, make pixval for next round */
      pixval = pixels[level];
      range += scale;
      imagelim = (int)(range + 0.5);
      if( imagelim >= 32768 ) 
	imagelim = 32767;
      do {
	lookup[imageval] = pixval;
      } while( ++imageval <= imagelim );
      if( imageval >= 32768 )
	return;
    } while( ++level <= maxcolor );
    /* pixval at level 0 lowest color again */
    level = 0;
  }
}

/*
 * Subroutine:	sqrt_scale
 * Purpose:	Distribute color levels in the map by a root or power function.
 * Method:	(level / maxlevel) ranges from 0 to 1.  Raise to a power.
 *		Result curves from 0 to 1.  Map result directly back to
 *		pixel value.
 */
static void sqrt_scale ( image_min, image_max )
  int image_min, image_max;		/* i: limits of values in display */
{
  double range;
  double power;
  double ncolors;
  int maxcolor;
  int level;
  unsigned long *pixels;		/* l: ideal byte to hardare byte map */
  register unsigned char *lookup;	/* l: scalemap base (signed offsets) */
  register int imageval;
  register int pixval;
  register int imagelim;

  lookup = buffer.scalemap + SCALEOFF;
  maxcolor = color.ncolors - 1;
  pixels = color.pixvalmap;
  power = color.scale.root_power;
  ncolors = (double)color.ncolors;
  range = image_max - image_min + 1;
  imageval = image_min;
  level = 0;
  /* pixval at level 0 is pixoffset (lshifted 0 is still 0) */
  pixval = pixels[0];
  while( level++ < maxcolor ) {
    imagelim = image_min + (int)
      ((pow(((double)level / ncolors), power) * range) + 0.5);
    /* limit map range to image values */
    if( imagelim > image_max )
      imagelim = image_max;
    while( imageval < imagelim )
      lookup[imageval++] = pixval;
    /* level was inc'd after loop test, make pixval for next round */
    pixval = pixels[level];
  }
  /* fill in at top if short of image_max */
  while( imageval <= image_max )
    lookup[imageval++] = pixval;
}

/*
 * Subroutine:	log_scale
 * Purpose:	Distribute color levels in the map by a logorithmic or
 *		exponential curve (powers of e).
 */
static void log_scale ( image_min, image_max )
  int image_min, image_max;		/* i: limits of values in display */
{
  double scale;
  double expo;
  double ncolors;
  int maxcolor;
  int level;
  unsigned long *pixels;		/* l: ideal byte to hardare byte map */
  register unsigned char *lookup;	/* l: scalemap base (signed offsets) */
  register int imageval;
  register int pixval;
  register int imagelim;

  lookup = buffer.scalemap + SCALEOFF;
  maxcolor = color.ncolors - 1;
  pixels = color.pixvalmap;
  expo = color.scale.log_expo;
  ncolors = (double)color.ncolors;
  /* base distribution on e**n as n goes from 0 to expo (expm1 is exp()-1) */
  if( color.scale.log_expo >= 0 ) {
    scale = (double)(image_max - image_min + 1) / expm1(expo);
  } else {
    /* negative exponents allocate more levels toward the high values */
    scale = (double)(image_max - image_min + 1) / (1.0 - exp(expo));
  }
  imageval = image_min;
  level = 0;
  /* pixval at level 0 is pixoffset (lshifted 0 is still 0) */
  pixval = pixels[0];
  while( level++ < maxcolor ) {
    if( expo > 0 ) {
      imagelim = image_min + (int)
	((expm1(((double)level / ncolors) * expo) * scale) + 0.5);
    } else {
      imagelim = image_min + (int)
	((1.0-exp(((double)level / ncolors) * expo) * scale) + 0.5);
    }
    /* limit map range to image values */
    if( imagelim > image_max )
      imagelim = image_max;
    while( imageval < imagelim )
      lookup[imageval++] = pixval;
    /* level was inc'd after loop test, make pixval for next round */
    pixval = pixels[level];
  }
  /* fill in at top if short of image_max */
  while( imageval <= image_max )
    lookup[imageval++] = pixval;
}

/*
 * Subroutine:	get_histogram_range
 * Purpose:	Find a range from the peak of the pixel value distribution
 */
static void get_histogram_range ( histogram, imin, imax)
     int *histogram;
     int *imin;		/* minimum pixel value */
     int *imax;		/* maximum pixel value */
{
  register int *hist;
  register int vmin;
  register int i;
  register int minhist, maxhist;
  float lfrac;
  int vpeak;
  int ipeak, ppeak;
  int pmin, pmax;
  int hwidth,hoff;

  if (buffer.lfrac <= 0 || buffer.lfrac >= 1.)
    return;

  /* offset hist to zero (+32768 or 0x8000)  */
  hist = histogram + SCALEOFF;

  /* find histogram peak */
  maxhist = SCALEMAX;
  minhist = SCALEMIN;
  vpeak = 0;
  ipeak = 0;
  for( i = minhist; (i <= maxhist); i++ ) {
    if (hist[i] > vpeak) {
      ipeak = i;
      vpeak = hist[i];
      }
    }

  /* Convert peak from histogram bin to pixel value */
  if ( img.fiscaled )
    ppeak = ((float) ipeak * img.fiscale) + img.fibias;
  else
    ppeak = ipeak;

  /* Find first pixel value at peak of histogram - lfrac */
  i = ipeak;
  if ( buffer.lfrac > 0 )
    lfrac = buffer.lfrac;
  else
    lfrac = 0.0;
  vmin = hist[ipeak] * lfrac;
  *imin = minhist;
  for (i = ipeak; i > minhist; i--) {
    if (hist[i] > 0 && hist[i] < vmin) {
      *imin = i+1;
      break;
      }
    }

  /* Convert range minimum from histogram bin to pixel value */
  if ( img.fiscaled )
    pmin = ((float) *imin * img.fiscale) + img.fibias;
  else
    pmin = *imin;

  /* Find last pixel value at peak of histogram - lfrac */
  *imax = maxhist;
  for (i = ipeak; i < maxhist; i++) {
    if (hist[i] > 0 && hist[i] < vmin) {
      *imax = i - 1;
      break;
      }
    }

  /* Convert range maximum from histogram bin to pixel value */
  if ( img.fiscaled )
    pmax = ((float) *imax * img.fiscale) + img.fibias;
  else
    pmax = *imax;

  if (control.verbose) {
    (void)printf("Linear display autoscaled from %d to %d\n", pmin, pmax);
    (void)printf("Limits at %5.3f of most common value = %d\n", lfrac, ppeak);
    }

  return;
}


syntax highlighted by Code2HTML, v. 0.9.1