[comp.graphics] Color Quantization sources needed

schear@ttidca.TTI.COM (Steve Schear) (01/31/89)

Some engineers in my group have been assigned the task of creating a demo to demonstrate the effect of decreasing color space resolution on display quality.  Has anyone out there written routines using some of the more popular algorithms (e.g., media slice, or quadtree) which permit a 24-bit/pixel color image to be reduced to less color resolution.  One bit increments would be ideal.  We at least plan to show 16-bit and 12-bit versions in addition to the obvious 8-bit/pixel images.

BTW C sources code would be best.

Thanks in advance.

Steve Schear
Product Planner
Citicorp/TTI
3100 Ocean Park Blvd.
Santa Monica, CA 90405

(213) 452-9191

quinn@killington.dartmouth.edu (Jerry Quinn) (02/01/89)

me too!

jerry quinn
quinn@sunapee.dartmouth.edu

falk@sun.uucp (Ed Falk) (02/02/89)

In article <3781@ttidca.TTI.COM>, schear@ttidca.TTI.COM (Steve Schear) writes:
> Some engineers in my group have been assigned the task of creating a
> demo to demonstrate the effect of decreasing color space resolution
> on display quality.  Has anyone out there written routines using some
> of the more popular algorithms (e.g., media slice, or quadtree) which
> permit a 24-bit/pixel color image to be reduced to less color
> resolution.  One bit increments would be ideal.  We at least plan to
> show 16-bit and 12-bit versions in addition to the obvious
> 8-bit/pixel images.
> 

OK, but only if you promise to put newlines in your postings from now
on.  Remember: this isn't MACWRITE.

This has got to be the most commonly asked question in this newsgroup.
In fact, I've posted all this stuff recently.  Is there an
archive service we should be using for this stuff?

Here it is once again:  "dither24to8" reduces a 24-bit sun rasterfile
to 8 bits by defining a color cube in the color table and mapping
pixels directly to color table entries.

"median" is Heckbert's median-cut algorithm.

Now, NOBODY ELSE IS ALLOWED TO ASK THIS QUESTION FOR THREE MONTHS.


#! /bin/sh
# this is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh to create the files:
#	dither24to8.c
#	median.c
#	pr_stream.c
# This archive created: Wed Feb  1 18:15:56 PST 1989 by falk
#
#
export PATH; PATH=/bin:$PATH
#
if test -f dither24to8.c
then
echo shar: will not over-write existing file dither24to8.c
else
echo shar: extracting 'dither24to8.c',    19177 characters
sed 's/^X//' > dither24to8.c <<'SHAR_EOF'
X/* quantize stuff:
X *
X * dither24to8 [-ctrim] [-od] [-fsd] [-size n n n] [-news] [-ofs n ]
X *				[ifile [ofile]]
X *
X */
X/*
X
XI should point out that the actual fractions we used were, assuming
Xyou are at X, moving left to right:
X
X		X     7/16
X         3/16   5/16  1/16    
X
XNote that the error goes to four neighbors, not three.  I think this
Xwill probably do better (at least for black and white) than the
X3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
Xseen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
Xbut I have no idea who the credit really belongs to.
X
XAlso, I should add that if you do zig-zag scanning (see my immediately
Xprevious message), it is sufficient (but not quite as good) to send
Xhalf the error one pixel ahead (e.g. to the right on lines you scan
Xleft to right), and half one pixel straight down.  Again, this is for
Xblack and white;  I've not tried it with color.
X-- 
X					Lou Steinberg
X
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <pixrect/pixrect_hs.h>
X
X#define	MAX_CMAP_SIZE	256
X
X#define	MAX_REDS	5
X#define	MAX_GREENS	9
X#define	MAX_BLUES	5
X#define	CUBE_OFFSET	30
X#define	RSCALE	0
X#define	GSCALE	(MAX_BLUES*MAX_REDS)
X#define	BSCALE	MAX_REDS
X
X
X
X
Xstatic	struct rasterfile	rh;
Xstatic	colormap_t		colormap, ocmap ;
Xstatic	unsigned char		rm[MAX_CMAP_SIZE],
X				gm[MAX_CMAP_SIZE],
X				bm[MAX_CMAP_SIZE] ;
Xstatic	int			bytes_per_pixel ;
Xstatic	int			linebytes, olinebytes ;
Xstatic	int			num_r, num_g, num_b ;
Xstatic	int			special_case ;
Xstatic	int			trim ;
Xstatic	int			NeWS_offset ;
Xstatic	int			min_r, max_r, min_g, max_g, min_b, max_b ;
X
Xstatic	char	*usage =
X"usage: dither24to8 [-ctrim] [-od] [-fsd] [-size n n n] [ifile [ofile]]\n" ;
X
X
X
Xmain(argc, argv)
X    int argc;
X    char **argv;
X{
Xstatic	struct rasterfile	outheader ;
X	int			i, j ;
X	int			dither = 0 ;
X	int			odither = 0 ;
X	char			*ifile_name = NULL, *ofile_name = NULL ;
X
X	trim = 0 ;
X	num_r = 8;  num_g = 8;  num_b = 4 ;
X	special_case = 1 ;
X	NeWS_offset = 0 ;
X	min_r = min_g = min_b = 0 ;
X	max_r = max_g = max_b = 255 ;
X
X	while(--argc)
X	{
X	  ++argv ;
X	  if(strcmp(*argv,"-od") == 0)
X	    odither = 1 ;
X
X	  else if(strcmp(*argv,"-fsd") == 0)
X	    dither = 1 ;
X
X	  else if(strcmp(*argv,"-size") == 0)
X	  {
X	    if(argc < 4)
X	    {
X	      fprintf(stderr,"-size requires 3 arguments\n%s",usage) ;
X	      exit(1) ;
X	    }
X	    else
X	    {
X	      argc -= 3 ;
X	      num_r = atoi(*++argv) ;
X	      num_g = atoi(*++argv) ;
X	      num_b = atoi(*++argv) ;
X	      special_case = 0 ;
X	      if(num_r * num_g * num_b + NeWS_offset > MAX_CMAP_SIZE)
X	      {
X		fprintf(stderr,"-size specifies colormap greater than %d\n%s",
X			MAX_CMAP_SIZE,usage) ;
X		exit(1) ;
X	      }
X	    }
X	  }
X
X	  else if(strcmp(*argv,"-ctrim") == 0)
X	  {
X	    trim = 1 ;
X	    special_case = 0 ;
X	  }
X
X	  else if(strcmp(*argv,"-news") == 0)
X	  {
X	    trim = 0 ;
X	    special_case = 0 ;
X	    num_r = 5 ; num_g = 9 ; num_b = 5 ;
X	    NeWS_offset = CUBE_OFFSET ;
X	  }
X
X	  else if(strcmp(*argv,"-ofs") == 0)
X	  {
X	    if( argc < 2 )
X	    {
X	      fprintf(stderr,"-ofs requires an argument\n%s",usage) ;
X	      exit(1) ;
X	    }
X	    else
X	    {
X	      special_case = 0 ;
X	      NeWS_offset = atoi(*++argv) ; --argc ;
X	      if(num_r * num_g * num_b + NeWS_offset > MAX_CMAP_SIZE)
X	      {
X		fprintf(stderr,"-ofs specifies colormap greater than %d\n%s",
X			MAX_CMAP_SIZE,usage) ;
X		exit(1) ;
X	      }
X	    }
X	  }
X
X	  else if(**argv == '-')
X	  {
X	    fprintf(stderr,"unknown argument '%s', %s",*argv, usage) ;
X	    exit(1) ;
X	  }
X
X	  else if(!ifile_name)
X	  {
X	    ifile_name = *argv ;
X	  }
X
X	  else if(!ofile_name)
X	  {
X	    ofile_name = *argv ;
X	  }
X
X	  else
X	  {
X	    fprintf(stderr, "Too many file names, %s",usage) ;
X	    exit(1) ;
X	  }
X	}
X
X	if(ifile_name)
X	  if (freopen(ifile_name, "r", stdin) == NULL)
X	  {
X	    fprintf(stderr,"Cannot open input file %s\n%s", ifile_name,usage);
X	    exit(1);
X	  }
X
X	if(ofile_name)
X	  if (freopen(ofile_name, "w", stdout) == NULL)
X	  {
X	    fprintf(stderr,"Cannot open output file %s\n%s", ofile_name,usage);
X	    exit(1);
X	  }
X
X
X	colormap.type = RMT_NONE ;
X
X
X	if( pr_load_header(stdin, &rh) != 0  ||
X	    pr_load_colormap(stdin, &rh, &colormap) != 0 )
X	{
X	  fprintf(stderr,"Error reading rasterfile header.\n");
X	  exit(1);
X	}
X
X
X	switch( rh.ras_depth )
X	{
X	  case 24: bytes_per_pixel = 3 ; break ;
X	  case 32: bytes_per_pixel = 4 ; break ;
X	  default:
X	    fprintf(stderr,
X	      "Image is %d bits deep, dither24to8 only works with 24 or 32\n");
X	    exit(1) ;
X	}
X
X	if(trim)
X	{
X	  if(fseek(stdin, 0L, 0) == 0)
X	  {
X	    trim_ctab() ;
X	    rewind(stdin) ;
X	    pr_load_header(stdin, &rh) ;
X	    pr_load_colormap(stdin, &rh, NULL) ;
X	  }
X	  else
X	  {
X	    fprintf(stderr, "-t option only works if input from file\n") ;
X	  }
X	}
X
X
X	ocmap.type = RMT_EQUAL_RGB ;
X	ocmap.length = num_r * num_g * num_b + NeWS_offset ;
X	ocmap.map[0] = rm ;
X	ocmap.map[1] = gm ;
X	ocmap.map[2] = bm ;
X	{
X	  register int	ir,ig,ib,ptr ;
X	  int		lr = max_r-min_r, lg=max_g-min_g, lb=max_b-min_b ;
X	  int		nr = num_r-1, ng=num_g-1, nb=num_b-1 ;
X	  ptr = 0 ;
X#ifdef	COMMENT
X	  for(ir = 0; ir < num_r; ++ir)
X	    for(ig = 0; ig < num_g; ++ig)
X	      for(ib = 0; ib < num_b; ++ib)
X	      {
X		rm[ptr] = min_r + ir * lr / nr ;
X		gm[ptr] = min_g + ig * lg / ng ;
X		bm[ptr] = min_b + ib * lb / nb ;
X		++ptr ;
X	      }
X#endif	COMMENT
X	  for(ig = 0; ig < num_g; ++ig)
X	    for(ib = 0; ib < num_b; ++ib)
X	      for(ir = 0; ir < num_r; ++ir)
X	      {
X		rm[ptr+NeWS_offset] = min_r + ir * lr / nr ;
X		gm[ptr+NeWS_offset] = min_g + ig * lg / ng ;
X		bm[ptr+NeWS_offset] = min_b + ib * lb / nb ;
X		++ptr ;
X	      }
X	}
X
X
X	pr_read_init(&rh) ;
X
X	linebytes = rh.ras_width * bytes_per_pixel ;
X	linebytes += linebytes%2 ;
X	olinebytes = rh.ras_width ;
X	olinebytes += olinebytes%2 ;
X
X	outheader.ras_magic = RAS_MAGIC ;
X	outheader.ras_width = rh.ras_width ;
X	outheader.ras_height = rh.ras_height ;
X	outheader.ras_depth = 8 ;
X	outheader.ras_length = olinebytes * outheader.ras_height ;
X	outheader.ras_type = RT_STANDARD ;
X	outheader.ras_maptype = RMT_EQUAL_RGB ;
X	outheader.ras_maplength = ocmap.length*3 ;
X	pr_dump_header(stdout, &outheader, &ocmap) ;
X
X	if(dither)
X	  quant_fsdither() ;
X	else if(odither)
X	  quant_odither() ;
X	else
X	  quant() ;
X
X	fclose(stdout) ;
X}
X
X
X
X
X
X#define	GETPIX(inptr,red,green,blue)		\
X	if(rh.ras_depth == 32)			\
X	  ++inptr ;				\
X	blue = *inptr++ ;			\
X	green = *inptr++ ;			\
X	red = *inptr++ ;			\
X						\
X	if (rh.ras_maplength > 0)		\
X	{					\
X	  red = *(colormap.map[0]+red) ;	\
X	  green = *(colormap.map[1]+green) ;	\
X	  blue = *(colormap.map[2]+blue) ;	\
X	}
X
X
X
X
X/*
X * straight quantization.  Each pixel is mapped to the colors
X * closest to it.  Color values are rounded to the nearest color
X * table entry.
X */
X
Xquant()
X{
X	unsigned char	*outline ;
X	unsigned char	*inline ;
Xregister unsigned char	*outptr ;
Xregister unsigned char	*inptr ;
Xregister int		i, j ;
X	int		nr = num_r-1, ng = num_g-1, nb = num_b-1 ;
X	int		lr = max_r-min_r, lg = max_g-min_g, lb = max_b-min_b ;
X	int		or = lr/nr/2 - min_r,
X			og = lg/ng/2 - min_g,
X			ob = lb/nb/2 - min_b ;
X
X
X
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X
X	outline = (unsigned char *) malloc( olinebytes ) ;
X
X	for(i=0; i < rh.ras_height; ++i)
X	{
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  inptr = inline ;
X	  outptr = outline;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    register int	tmp,red,green,blue ;
X
X	    GETPIX(inptr,red,green,blue) ;
X
X#ifdef	COMMENT
X	    if(special_case)
X	      tmp = (red & 0xe0) + ((green & 0xe0) >> 3) + (blue >> 6) ;
X	    else
X	    {
X	      if(trim)
X	      {
X		tmp = (red+or)*nr / lr ;
X		green = (green+og)*ng / lg ;
X		blue = (blue+ob)*nb / lb ;
X	      }
X	      else
X	      {
X		tmp = (red+or)*nr / 256 ;
X		green = (green+og)*ng / 256 ;
X		blue = (blue+ob)*nb / 256 ;
X	      }
X#ifdef	DEBUG
X	      if(tmp > nr  ||  green > ng  ||  blue > nb)
X		fprintf(stderr,"ERROR: rgb = %d,%d,%d\n",tmp,green,blue) ;
X#endif	DEBUG
X	      tmp = (((tmp * num_g) + green) * num_b) + blue ;
X#endif	COMMENT
X	    if(special_case)
X	      tmp = (red >> 5) + (green & 0xe0) + ((blue & 0xc0) >> 3) ;
X	    else
X	    {
X	      if(trim)
X	      {
X		red = (red+or)*nr / lr ;
X		tmp = (green+og)*ng / lg ;
X		blue = (blue+ob)*nb / lb ;
X	      }
X	      else
X	      {
X		red = (red+or)*nr / 256 ;
X		tmp = (green+og)*ng / 256 ;
X		blue = (blue+ob)*nb / 256 ;
X	      }
X#ifdef	DEBUG
X	      if(red > nr  ||  tmp > ng  ||  blue > nb)
X		fprintf(stderr,"ERROR: rgb = %d,%d,%d\n",red,tmp,blue) ;
X#endif	DEBUG
X	      tmp = (((tmp * num_b) + blue) * num_r) + red + NeWS_offset ;
X	    }
X	    *outptr++ = tmp ;
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X	free(inline) ;
X	free(outline) ;
X}
X
X
X/*
X * quantization with dither.  Each color value is rounded up or down
X * depending on the comparison of the error and the corresponding entry
X * in the dither matrix
X */
X
Xquant_odither()
X{
X	unsigned char	*outline ;
X	unsigned char	*inline ;
Xregister unsigned char	*outptr ;
Xregister unsigned char	*inptr ;
Xregister int		i, j ;
X	int		nr = num_r-1, ng = num_g-1, nb = num_b-1 ;
X	int		lr = max_r-min_r, lg = max_g-min_g, lb = max_b-min_b ;
X
Xstatic	int	d16[16][16] = {
X	{  0,192, 48,240, 12,204, 60,252,  3,195, 51,243, 15,207, 63,255},
X	{128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127},
X	{ 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223},
X	{160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95},
X	{  8,200, 56,248,  4,196, 52,244, 11,203, 59,251,  7,199, 55,247},
X	{136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119},
X	{ 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215},
X	{168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87},
X	{  2,194, 50,242, 14,206, 62,254,  1,193, 49,241, 13,205, 61,253},
X	{130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125},
X	{ 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221},
X	{162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93},
X	{ 10,202, 58,250,  6,198, 54,246,  9,201, 57,249,  5,197, 53,245},
X	{138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117},
X	{ 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213},
X	{170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85}} ;
X
X	int	rdither[16][16], gdither[16][16], bdither[16][16] ;
X
X	for(i=0; i<16; ++i)
X	  for(j=0; j<16; ++j)
X	  {
X	    rdither[i][j] = d16[i][j]*lr / nr / 256 ;
X	    gdither[i][j] = d16[i][j]*lg / ng / 256 ;
X	    bdither[i][j] = d16[i][j]*lb / nb / 256 ;
X	  }
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X
X	outline = (unsigned char *) malloc( olinebytes ) ;
X
X	for(i=0; i < rh.ras_height; ++i)
X	{
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  inptr = inline ;
X	  outptr = outline;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    GETPIX(inptr,red,green,blue) ;
X
X	    if(special_case)
X	    {
X#ifdef	COMMENT
X	      tmp = (red & 0xe0) + ((green >> 3) & 0x1c) + (blue >> 6) ;
X	      red -= rm[tmp] ;
X	      green -= gm[tmp] ;
X	      blue -= bm[tmp] ;
X	      if(red > rdither[i%16][j%16])
X		tmp += 0x20 ;
X	      if(green > gdither[i%16][j%16])
X		tmp += 0x4 ;
X	      if(blue > bdither[i%16][j%16])
X		++tmp ;
X#endif	COMMENT
X	      tmp = (green & 0xe0) + ((blue >> 3) & 0x18) + (red >> 5) ;
X	      red -= rm[tmp] ;
X	      green -= gm[tmp] ;
X	      blue -= bm[tmp] ;
X	      if(red > rdither[i%16][j%16])
X		++tmp ;
X	      if(green > gdither[i%16][j%16])
X		tmp += 0x20 ;
X	      if(blue > bdither[i%16][j%16])
X		tmp += 8 ;
X	    }
X	    else
X	    {
X	      if(trim)
X	      {
X		r2 = (red-min_r)*nr / lr ;
X		g2 = (green-min_g)*ng / lg ;
X		b2 = (blue-min_b)*nb / lb ;
X	      }
X	      else
X	      {
X		r2 = red*nr / 255 ;
X		g2 = green*ng / 255 ;
X		b2 = blue*nb / 255 ;
X	      }
X#ifdef	COMMENT
X	      tmp = (((r2 * num_g) + g2) * num_b) + b2 ;
X#endif	COMMENT
X	      tmp = (((g2 * num_b) + b2) * num_r) + r2 + NeWS_offset ;
X	      red -= rm[tmp] ;
X	      green -= gm[tmp] ;
X	      blue -= bm[tmp] ;
X	      if(red > rdither[i%16][j%16])
X		++r2 ;
X	      if(green > gdither[i%16][j%16])
X		++g2 ;
X	      if(blue > bdither[i%16][j%16])
X		++b2 ;
X#ifdef	DEBUG
X	      if(r2 > nr  ||  g2 > ng  ||  b2 > nb)
X		fprintf(stderr,"ERROR: %d,%d,%d => %d,%d,%d\n",
X			red,green,blue,r2,g2,b2) ;
X#endif	DEBUG
X#ifdef	COMMENT
X	      tmp = (((r2 * num_g) + g2) * num_b) + b2 ;
X#endif	COMMENT
X	      tmp = (((g2 * num_b) + b2) * num_r) + r2 + NeWS_offset ;
X	    }
X	    *outptr++ = tmp ;
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X	free(inline) ;
X	free(outline) ;
X}
X
X
X#ifdef	COMMENT
X#define	FS_GETPIX						\
X	    blue = *thisptr++ ;					\
X	    green = *thisptr++ ;				\
X	    red = *thisptr++ ;					\
X								\
X	    if(special_case)					\
X	      tmp = (red & 0xe0) + ((green & 0xe0) >> 3) + (blue >> 6) ; \
X	    else						\
X	    {							\
X	      if(trim)						\
X	      {							\
X		r2 = (red-min_r)*nr / lr ;			\
X		g2 = (green-min_g)*ng / lg ;			\
X		b2 = (blue-min_b)*nb / lb ;			\
X	      }							\
X	      else						\
X	      {							\
X		r2 = red*nr / 255 ;				\
X		g2 = green*ng / 255 ;				\
X		b2 = blue*nb / 255 ;				\
X	      }							\
X	      if(r2 > nr) r2 = nr ;				\
X	      if(g2 > ng) g2 = ng ;				\
X	      if(b2 > nb) b2 = nb ;				\
X	      tmp = (((r2 * num_g) + g2) * num_b) + b2 ;	\
X	    }							\
X	    *outptr++ = tmp ;					\
X								\
X	    red -= rm[tmp] ;					\
X	    green -= gm[tmp] ;					\
X	    blue -= bm[tmp] ;
X#endif	COMMENT
X#define	FS_GETPIX						\
X	    blue = *thisptr++ ;					\
X	    green = *thisptr++ ;				\
X	    red = *thisptr++ ;					\
X								\
X	    if(special_case)					\
X	      tmp = (green & 0xe0) + ((blue & 0xc0) >> 3) + (red >> 5) ; \
X	    else						\
X	    {							\
X	      if(trim)						\
X	      {							\
X		r2 = (red-min_r)*nr / lr ;			\
X		g2 = (green-min_g)*ng / lg ;			\
X		b2 = (blue-min_b)*nb / lb ;			\
X	      }							\
X	      else						\
X	      {							\
X		r2 = red*nr / 255 ;				\
X		g2 = green*ng / 255 ;				\
X		b2 = blue*nb / 255 ;				\
X	      }							\
X	      if(r2 > nr) r2 = nr ;				\
X	      if(g2 > ng) g2 = ng ;				\
X	      if(b2 > nb) b2 = nb ;				\
X	      tmp = (((g2 * num_b) + b2) * num_r) + r2 + NeWS_offset ;	\
X	    }							\
X	    *outptr++ = tmp ;					\
X								\
X	    red -= rm[tmp] ;					\
X	    green -= gm[tmp] ;					\
X	    blue -= bm[tmp] ;
X
X
X
X
X
Xquant_fsdither()
X{
X	unsigned char	*outline, *inline ;
X	short		*thisline, *nextline, *tmpptr ;
X	int		nr = num_r-1, ng = num_g-1, nb = num_b-1 ;
X	int		lr = max_r-min_r, lg = max_g-min_g, lb = max_b-min_b ;
X	unsigned char	*inptr ;
Xregister unsigned char	*outptr ;
Xregister short		*thisptr, *nextptr ;
Xregister int		i, j ;
X
X
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X	thisline = (short *) malloc( rh.ras_width * 3 * sizeof(short)) ;
X	nextline = (short *) malloc( rh.ras_width * 3 * sizeof(short)) ;
X
X	outline = (unsigned char *) malloc( olinebytes ) ;
X
X	/*
X	 * get first line
X	 */
X	pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	{
X	  register int tmp ;
X	  inptr = inline ;
X	  nextptr = nextline ;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    if(rh.ras_depth == 32)
X	      ++inptr ;
X	    if( rh.ras_maplength > 0)
X	    {
X	      *nextptr++ = *(colormap.map[0]+*inptr++) ;
X	      *nextptr++ = *(colormap.map[1]+*inptr++) ;
X	      *nextptr++ = *(colormap.map[2]+*inptr++) ;
X	    }
X	    else
X	    {
X	      *nextptr++ = *inptr++ ;
X	      *nextptr++ = *inptr++ ;
X	      *nextptr++ = *inptr++ ;
X	    }
X	  }
X	}
X
X	for(i=0; i < rh.ras_height-1; ++i)
X	{
X	  tmpptr = thisline ;
X	  thisline = nextline ;
X	  nextline = tmpptr ;
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  {
X	    register int tmp ;
X	    inptr = inline ;
X	    nextptr = nextline ;
X	    for(j=0; j < rh.ras_width; ++j)
X	    {
X	      if(rh.ras_depth == 32)
X		++inptr ;
X	      if( rh.ras_maplength > 0)
X	      {
X		*nextptr++ = *(colormap.map[0]+*inptr++) ;
X		*nextptr++ = *(colormap.map[1]+*inptr++) ;
X		*nextptr++ = *(colormap.map[2]+*inptr++) ;
X	      }
X	      else
X	      {
X		*nextptr++ = *inptr++ ;
X		*nextptr++ = *inptr++ ;
X		*nextptr++ = *inptr++ ;
X	      }
X	    }
X	  }
X
X	  thisptr = thisline ;
X	  nextptr = nextline ;
X	  outptr = outline;
X
X	  /*
X	   * special case: first pixel on line
X	   */
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    FS_GETPIX
X
X	    thisptr[0] += blue * 7 / 16 ;
X	    thisptr[1] += green * 7 / 16 ;
X	    thisptr[2] += red * 7 / 16 ;
X	    nextptr[0] += blue * 5 / 16 ;
X	    nextptr[1] += green * 5 / 16 ;
X	    nextptr[2] += red * 5 / 16 ;
X	    nextptr[3] += blue / 16 ;
X	    nextptr[4] += green / 16 ;
X	    nextptr[5] += red / 16 ;
X	    nextptr += 3 ;
X	  }
X
X
X
X
X	  /*
X	   * next case: most of the rest of the line
X	   */
X	  for(j=1; j < rh.ras_width-1 ; ++j)
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    FS_GETPIX
X	    thisptr[0] += blue * 7 / 16 ;
X	    thisptr[1] += green * 7 / 16 ;
X	    thisptr[2] += red * 7 / 16 ;
X	    nextptr[-3] += blue * 3 / 16 ;
X	    nextptr[-2] += green * 3 / 16 ;
X	    nextptr[-1] += red * 3 / 16 ;
X	    nextptr[0] += blue * 5 / 16 ;
X	    nextptr[1] += green * 5 / 16 ;
X	    nextptr[2] += red * 5 / 16 ;
X	    nextptr[3] += blue / 16 ;
X	    nextptr[4] += green / 16 ;
X	    nextptr[5] += red / 16 ;
X	    nextptr += 3 ;
X	  }
X
X	  /*
X	   * last case: last pixel on line
X	   */
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    FS_GETPIX
X	    nextptr[-3] += blue * 3 / 16 ;
X	    nextptr[-2] += green * 3 / 16 ;
X	    nextptr[-1] += red * 3 / 16 ;
X	    nextptr[0] += blue * 5 / 16 ;
X	    nextptr[1] += green * 5 / 16 ;
X	    nextptr[2] += red * 5 / 16 ;
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X
X
X
X	/*
X	 * special case: last line:
X	 */
X	{
X	  thisline = nextline ;
X
X	  thisptr = thisline ;
X	  outptr = outline;
X
X
X
X	  /*
X	   * first case: most of the line
X	   */
X	  for(j=0; j < rh.ras_width-1 ; ++j)
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    FS_GETPIX
X	    thisptr[0] += blue * 7 / 16 ;
X	    thisptr[1] += green * 7 / 16 ;
X	    thisptr[2] += red * 7 / 16 ;
X	  }
X
X	  /*
X	   * last case: last pixel on line
X	   */
X	  {
X	    register int	tmp,red,green,blue,r2,g2,b2 ;
X
X	    FS_GETPIX
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X	free(inline) ;
X	free(thisline) ;
X	free(nextline) ;
X	free(outline) ;
X}
X
X
X
X
Xtrim_ctab()
X{
X	unsigned char	*inline ;
Xregister unsigned char	*inptr ;
Xregister int		i, j ;
X
X	pr_load_header(stdin, &rh) ;
X	pr_load_colormap(stdin, &rh, NULL) ;
X
X	pr_read_init(&rh) ;
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X
X	min_r = min_g = min_b = 999 ;
X	max_r = max_g = max_b = -1 ;
X
X	for(i=0; i < rh.ras_height; ++i)
X	{
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  inptr = inline ;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    register int	red,green,blue ;
X
X	    if(rh.ras_depth == 32)
X	      ++inptr ;
X	    blue = *inptr++ ;
X	    green = *inptr++ ;
X	    red = *inptr++ ;
X
X	    if (rh.ras_maplength > 0)
X	    {
X	      red = *(colormap.map[0]+red) ;
X	      green = *(colormap.map[1]+green) ;
X	      blue = *(colormap.map[2]+blue) ;
X	    }
X
X	    if(red < min_r) min_r = red ;
X	    if(red > max_r) max_r = red ;
X	    if(green < min_g) min_g = green ;
X	    if(green > max_g) max_g = green ;
X	    if(blue < min_b) min_b = blue ;
X	    if(blue > max_b) max_b = blue ;
X	  }
X	}
X
X	fprintf(stderr,"%d<r<%d, %d<g<%d, %d<b<%d\n",
X		min_r,max_r, min_g,max_g, min_b,max_b) ;
X	free(inline) ;
X}
SHAR_EOF
len=`wc -c < dither24to8.c`
if test $len !=    19177 ; then
echo error: dither24to8.c was $len bytes long, should have been    19177
fi
fi # end of overwriting check
if test -f median.c
then
echo shar: will not over-write existing file median.c
else
echo shar: extracting 'median.c',    27988 characters
sed 's/^X//' > median.c <<'SHAR_EOF'
Xstatic	char	sccsid[] = "@(#)median.c 1.11 89/01/19 SMI";
X
X/* quantize stuff:
X *
X * median [-csize n] [-fsd] [ifile [ofile]]
X *
X * -csize n	- set colortable size.  Default is 256.
X * -fsd		- use Floyd-Steinberg dithering.
X * -ccube	- add a 5x5x5 color cube; may reduce FSD artifacts
X */
X
X
X/* Notes:
X *
X * [1] Floyd-Steinberg dither:
X *
X *
X *  I should point out that the actual fractions we used were, assuming
X *  you are at X, moving left to right:
X *
X *		    X     7/16
X *	     3/16   5/16  1/16    
X *
X *  Note that the error goes to four neighbors, not three.  I think this
X *  will probably do better (at least for black and white) than the
X *  3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
X *  seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
X *  but I have no idea who the credit really belongs to.
X
X *  Also, I should add that if you do zig-zag scanning (see my immediately
X *  previous message), it is sufficient (but not quite as good) to send
X *  half the error one pixel ahead (e.g. to the right on lines you scan
X *  left to right), and half one pixel straight down.  Again, this is for
X *  black and white;  I've not tried it with color.
X *  -- 
X *					    Lou Steinberg
X *
X *
X * [2] Color Image Quantization for Frame Buffer Display, Paul Heckbert,
X *	Siggraph '82 proceedings, pp. 297-307
X *
X *
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <pixrect/pixrect_hs.h>
X
X#define	MAX_CMAP_SIZE	256
X
X#define	COLOR_DEPTH	8
X#define	MAX_COLOR	256
X
X#define	B_DEPTH		5		/* # bits/pixel to use */
X#define	B_LEN		(1<<B_DEPTH)
X
X#define	C_DEPTH		2
X#define	C_LEN		(1<<C_DEPTH)	/* # cells/color to use */
X
X
X
Xtypedef	struct colorbox {
X	  struct colorbox	*next, *prev ;
X	  int			rmin,rmax, gmin,gmax, bmin,bmax ;
X	  int			total ;
X	} Colorbox ;
X
Xtypedef struct {
X	  int	num_ents ;
X	  int	entries[MAX_CMAP_SIZE][2] ;
X	} C_cell ;
X
Xstatic	struct rasterfile	rh;
Xstatic	colormap_t		colormap, ocmap ;
Xstatic	unsigned char		rm[MAX_CMAP_SIZE],
X				gm[MAX_CMAP_SIZE],
X				bm[MAX_CMAP_SIZE] ;
Xstatic	int			bytes_per_pixel ;
Xstatic	int			linebytes, olinebytes ;
Xstatic	int			num_colors ;
X
Xstatic	int			histogram[B_LEN][B_LEN][B_LEN] ;
X
Xstatic	Colorbox		*freeboxes ;
Xstatic	Colorbox		*usedboxes ;
Xstatic	Colorbox		*largest_box() ;
X
Xstatic	C_cell			**ColorCells ;
X
X
Xstatic	char	*usage =
X"usage: median [-csize n] [-fsd] [ifile [ofile]]\n" ;
X
X
Xmain(argc, argv)
X    int argc;
X    char **argv;
X{
Xstatic	struct rasterfile	outheader ;
X	int			i, j ;
X	int			dither = 0 ;
X	char			*ifile_name = NULL, *ofile_name = NULL ;
X	Colorbox		*box_list, *ptr ;
X	int			ccube = 0 ;
X
X	num_colors = MAX_CMAP_SIZE ;
X
X	while(--argc)
X	{
X	  ++argv ;
X	  if(strcmp(*argv,"-fsd") == 0)
X	    dither = 1 ;
X
X	  else if(strcmp(*argv,"-csize") == 0)
X	  {
X	    if(argc < 2)
X	    {
X	      fprintf(stderr,"-csize requires 2 arguments, %s",usage) ;
X	      exit(1) ;
X	    }
X	    else
X	    {
X	      argc -= 1 ;
X	      num_colors = atoi(*++argv) ;
X	      if(num_colors > MAX_CMAP_SIZE)
X	      {
X		fprintf(stderr,"-csize specifies colormap greater than %d\n%s",
X			MAX_CMAP_SIZE,usage) ;
X		exit(1) ;
X	      }
X	    }
X	  }
X
X	  else if(strcmp(*argv,"-ccube") == 0)
X	    ++ccube ;
X
X	  else if(**argv == '-')
X	  {
X	    fprintf(stderr,"unknown argument '%s'\n%s",*argv,usage) ;
X	    exit(1) ;
X	  }
X
X	  else if(!ifile_name)
X	  {
X	    ifile_name = *argv ;
X	  }
X
X	  else if(!ofile_name)
X	  {
X	    ofile_name = *argv ;
X	  }
X
X	  else
X	  {
X	    fprintf(stderr, "Too many file names\n%s",usage) ;
X	    exit(1) ;
X	  }
X	}
X
X	if(ifile_name)
X	  if (freopen(ifile_name, "r", stdin) == NULL)
X	  {
X	    fprintf(stderr,"Cannot open input file %s\n%s", ifile_name,usage);
X	    exit(1);
X	  }
X
X	if(ofile_name)
X	  if (freopen(ofile_name, "w", stdout) == NULL)
X	  {
X	    fprintf(stderr,"Cannot open output file %s\n%s", ofile_name,usage);
X	    exit(1);
X	  }
X
X
X	if (pr_load_header(stdin, &rh) != 0  ||
X	    pr_load_colormap(stdin, &rh, &colormap) != 0 )
X	{
X	  fprintf(stderr,"Error reading rasterfile header.\n");
X	  exit(1);
X	}
X
X
X
X	switch( rh.ras_depth )
X	{
X	  case 8: bytes_per_pixel = 1 ; break ;
X	  case 24: bytes_per_pixel = 3 ; break ;
X	  case 32: bytes_per_pixel = 4 ; break ;
X	  default:
X	    fprintf(stderr, "median: unknown image depth: %d\n", rh.ras_depth);
X	    exit(1) ;
X	}
X
X	linebytes = rh.ras_width * bytes_per_pixel ;
X	linebytes += linebytes%2 ;
X
X/*
X * PART I - Assign color table
X */
X
X	/*
X	 * STEP 1:  create empty boxes
X	 */
X
X	usedboxes = NULL ;
X	box_list = freeboxes =
X		(Colorbox *) malloc( num_colors * sizeof(Colorbox) ) ;
X
X	freeboxes[0].next = &freeboxes[1] ;
X	freeboxes[0].prev = NULL ;
X	for(i=1; i<num_colors-1; ++i)
X	{
X	  freeboxes[i].next = &freeboxes[i+1] ;
X	  freeboxes[i].prev = &freeboxes[i-1] ;
X	}
X	freeboxes[num_colors-1].next = NULL ;
X	freeboxes[num_colors-1].prev = &freeboxes[num_colors-2] ;
X
X
X
X	/*
X	 * STEP 2: get histogram, initialize first box
X	 */
X
X	ptr = freeboxes ;
X	freeboxes = ptr->next ;
X	if(freeboxes) freeboxes->prev = NULL ;
X
X	ptr->next = usedboxes ;
X	usedboxes = ptr ;
X	if(ptr->next) ptr->next->prev = ptr ;
X
X	get_histogram(ptr) ;
X
X	if( fseek(stdin, 0L, 0) != 0)
X	{
X	  fprintf(stderr, "median: input must be from file\n") ;
X	  exit(1) ;
X	}
X
X
X
X	/*
X	 * STEP 3 (optional): assign color boxes from a 5x5x5 color
X	 * cube.
X	 */
X
X	if( ccube  &&  num_colors > 125 )
X	{
X	  register int r,g,b ;
X	  register Colorbox *tmp ;
X	  for( r=0; r<=4; ++r )
X	    for( g=0; g<=4; ++g )
X	      for( b=0; b<=4; ++b )
X	      {
X		tmp = freeboxes ;
X		freeboxes = tmp->next ;
X		if(freeboxes) freeboxes->prev = NULL ;
X		tmp->next = usedboxes ;
X		usedboxes = tmp ;
X		if(tmp->next) tmp->next->prev = tmp ;
X		tmp->rmin = tmp->rmax = r*B_LEN/4 ;
X		tmp->gmin = tmp->gmax = g*B_LEN/4 ;
X		tmp->bmin = tmp->bmax = b*B_LEN/4 ;
X		tmp->total = 0 ;
X	      }
X	}
X
X
X
X	/*
X	 * STEP 4: continually subdivide boxes until no more free
X	 * boxes remain or until all colors assigned.
X	 */
X
X	while(freeboxes != NULL)
X	{
X	  ptr = largest_box() ;
X	  if( ptr != NULL )
X	    splitbox(ptr) ;
X	  else
X	    freeboxes = NULL ;
X	}
X
X
X
X
X
X
X	/*
X	 * STEP 5: assign colors to all boxes
X	 */
X
X	/* extra feature: find dark box and put first */
X	for(ptr=usedboxes->next; ptr != NULL; )
X	  if( ptr->rmax < B_LEN/4  &&  ptr->gmax < B_LEN/4  &&
X	      ptr->bmax < B_LEN/4 )
X	  {
X	    ptr->prev->next = ptr->next ;
X	    if( ptr->next != NULL )
X	      ptr->next->prev = ptr->prev ;
X	    ptr->next = usedboxes ;
X	    ptr->prev = NULL ;
X	    ptr->next->prev = ptr ;
X	    usedboxes = ptr ;
X	    ptr = NULL ;
X	  }
X	  else
X	    ptr = ptr->next ;
X
X	for(i=0, ptr=usedboxes; ptr != NULL; ++i, ptr=ptr->next)
X	  assign_color(ptr,&rm[i],&gm[i],&bm[i]) ;
X
X	/* We're done with the boxes now */
X
X	free(box_list) ;
X	box_list = freeboxes = usedboxes = NULL ;
X
X
X
X
X	/*
X	 * STEP 6: scan histogram and map all values to closest color
X	 */
X
X	/* 6a: create cell list as described in Heckbert[2] */
X
X	{
X	  register C_cell	**ptr ;
X	  register int		n = C_LEN*C_LEN*C_LEN ;
X
X	  ptr = ColorCells =
X		(C_cell **) malloc(C_LEN*C_LEN*C_LEN * sizeof(C_cell *));
X
X	  while( n-- > 0 )
X	    *ptr++ = NULL ;
X	}
X
X
X
X
X	/* 6b: create mapping from truncated pixel space to color
X	   table entries */
X
X	map_colortable() ;
X
X
X
X/*
X * PART II - scan image and map rgb values to color table, dithering
X *	if requested.
X */
X
X
X	/*
X	 * STEP 7: scan image, match input values to table entries
X	 */
X
X	pr_load_header(stdin, &rh) ;
X	pr_load_colormap(stdin, &rh, &colormap) ;
X	pr_read_init(&rh) ;
X
X	olinebytes = rh.ras_width ;
X	olinebytes += olinebytes%2 ;
X
X	outheader.ras_magic = RAS_MAGIC ;
X	outheader.ras_width = rh.ras_width ;
X	outheader.ras_height = rh.ras_height ;
X	outheader.ras_depth = COLOR_DEPTH ;
X	outheader.ras_length = olinebytes * outheader.ras_height ;
X	outheader.ras_type = RT_STANDARD ;
X	outheader.ras_maptype = RMT_EQUAL_RGB ;
X	outheader.ras_maplength = num_colors*3 ;
X
X	ocmap.type = RMT_EQUAL_RGB ;
X	ocmap.length = num_colors ;
X	ocmap.map[0] = rm ;
X	ocmap.map[1] = gm ;
X	ocmap.map[2] = bm ;
X
X	pr_dump_header(stdout, &outheader, &ocmap) ;
X
X	if(dither)
X	  quant_fsdither() ;
X	else
X	  quant() ;
X
X	fclose(stdout) ;
X}
X
X
X
X
X
X/*
X * This function reads the input, truncates the three colors to
X * "B_DEPTH" bits of precision, updates the limits of the colorbox it
X * is filling, and calculates a histogram
X */
X
X
Xstatic
Xget_histogram(box)
Xregister Colorbox	*box ;
X{
X	unsigned char	*inline ;
Xregister unsigned char	*inptr ;
X	int		i ;
Xregister int		j ;
X
X
X	pr_read_init(&rh) ;
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X
X	box->rmin = box->gmin = box->bmin = 999 ;
X	box->rmax = box->gmax = box->bmax = -1 ;
X	box->total = rh.ras_height*rh.ras_width ;
X
X	bzero( histogram, sizeof(histogram) ) ;
X
X
X	for(i=rh.ras_height; --i >= 0;)
X	{
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  inptr = inline ;
X	  for(j=rh.ras_width; --j >= 0;)
X	  {
X	    register int	red,green,blue ;
X
X	    switch( rh.ras_depth )
X	    {
X	      case 8: blue = green = red = *inptr++ ; break ;
X	      case 32: ++inptr ;
X	      case 24: blue = *inptr++ ; green = *inptr++ ; red = *inptr++ ;
X	    }
X
X	    if (rh.ras_maplength > 0)
X	    {
X	      red = colormap.map[0][red] ;
X	      green = colormap.map[1][green] ;
X	      blue = colormap.map[2][blue] ;
X	    }
X
X	    red >>= (COLOR_DEPTH-B_DEPTH) ;
X	    green >>= (COLOR_DEPTH-B_DEPTH) ;
X	    blue >>= (COLOR_DEPTH-B_DEPTH) ;
X
X	    if(red < box->rmin) box->rmin = red ;
X	    if(red > box->rmax) box->rmax = red ;
X	    if(green < box->gmin) box->gmin = green ;
X	    if(green > box->gmax) box->gmax = green ;
X	    if(blue < box->bmin) box->bmin = blue ;
X	    if(blue > box->bmax) box->bmax = blue ;
X
X	    ++histogram[red][green][blue] ;
X	  }
X	}
X	free(inline) ;
X}
X
X
X
X
X
X/*
X * This function searches all of the color boxes in the list and returns
X * the largest one, where "largest" is defined as the box with the most
X * pixels.
X *
X * It is possible that defining "largest" as the box enclosing
X * the most color space might provide better results.  I'll have to
X * try that sometime.
X */
X
Xstatic Colorbox *
Xlargest_box()
X{
Xregister Colorbox	*tmp = usedboxes, *ptr = NULL ;
Xregister int 		size = -1 ;
X
X	while(tmp != NULL)
X	{
X	  if( (tmp->rmax > tmp->rmin  ||
X	       tmp->gmax > tmp->gmin  ||
X	       tmp->bmax > tmp->bmin)  &&  tmp->total > size )
X	  {
X	    ptr = tmp ;
X	    size = tmp->total ;
X	  }
X	  tmp = tmp->next ;
X	}
X	return(ptr) ;
X}
X
X
X
X
X/* This function takes a color box and splits it down the middle of its
X * longest (r/g/b) axis.  "longest" is defined as the axis with the
X * largest domain.  "middle" is defined as the median (point with
X * the same number of pixels on each side).
X *
X * Both sub-boxes are then compressed until they just barely enclose
X * their points.
X *
X * It is possible that defining "middle" in some other way might
X * provide better results.  I'll have to try that sometime.
X */
X
Xstatic
Xsplitbox(ptr)
Xregister Colorbox	*ptr ;
X{
X	int		hist2[B_LEN] ;
X	int		first, last ;
Xregister Colorbox	*new ;
Xregister int		*iptr, *histp ;
Xregister int		i, j ;
X	enum		{RED,GREEN,BLUE} which ;
X
X	/*
X	 * see which axis is the largest, do a histogram along that
X	 * axis.  Split at median point.  Contract both new boxes to
X	 * fit points and return
X	 */
X
X	if( (i = ptr->rmax - ptr->rmin) >= (ptr->gmax - ptr->gmin)  &&
X	    i >= (ptr->bmax - ptr->bmin) )
X	  which = RED ;
X	else if( (ptr->gmax - ptr->gmin) >= (ptr->bmax - ptr->bmin) )
X	  which = GREEN ;
X	else
X	  which = BLUE ;
X
X
X
X	/* get histogram along longest axis */
X	{
X	  register int	ir,ig,ib ;
X	  switch(which)
X	  {
X	    case RED:
X	      histp = &hist2[ptr->rmin] ;
X	      for(ir = ptr->rmin; ir <= ptr->rmax; ++ir)
X	      {
X		*histp = 0 ;
X		for(ig = ptr->gmin; ig <= ptr->gmax; ++ig)
X		{
X		  iptr = &histogram[ir][ig][ptr->bmin] ;
X		  for(ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X		  {
X		    *histp += *iptr ;
X		    ++iptr ;
X		  }
X		}
X		++histp ;
X	      }
X	      first = ptr->rmin;  last = ptr->rmax ;
X	      break ;
X
X	    case GREEN:
X	      histp = &hist2[ptr->gmin] ;
X	      for(ig = ptr->gmin; ig <= ptr->gmax; ++ig)
X	      {
X		*histp = 0 ;
X		for(ir = ptr->rmin; ir <= ptr->rmax; ++ir)
X		{
X		  iptr = &histogram[ir][ig][ptr->bmin] ;
X		  for(ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X		  {
X		    *histp += *iptr ;
X		    ++iptr ;
X		  }
X		}
X		++histp ;
X	      }
X	      first = ptr->gmin;  last = ptr->gmax ;
X	      break ;
X
X	    case BLUE:
X	      histp = &hist2[ptr->bmin] ;
X	      for(ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X	      {
X		*histp = 0 ;
X		for(ir = ptr->rmin; ir <= ptr->rmax; ++ir)
X		{
X		  iptr = &histogram[ir][ptr->gmin][ib] ;
X		  for(ig = ptr->gmin; ig <= ptr->gmax; ++ig)
X		  {
X		    *histp += *iptr ;
X		    iptr += B_LEN ;
X		  }
X		}
X		++histp ;
X	      }
X	      first = ptr->bmin;  last = ptr->bmax ;
X	      break ;
X	  }
X	}
X
X	/* find median point */
X	{
X	  register int sum, sum2 ;
X	  histp = &hist2[first] ;
X
X	  sum2 = ptr->total/2 ;
X	  histp = &hist2[first] ;
X	  sum = 0 ;
X	  for( i=first; i<=last && (sum += *histp++) < sum2; ++i) ;
X	  if( i == first ) ++i ;
X	}
X
X	/* Create new box, re-allocate points */
X
X	new = freeboxes ;
X	freeboxes = new->next ;
X	if(freeboxes) freeboxes->prev = NULL ;
X
X	if(usedboxes) usedboxes->prev = new ;
X	new->next = usedboxes ;
X	usedboxes = new ;
X
X	{
X	  register int	sum1,sum2,j ;
X	  int		total ;
X	  total = ptr->total ;
X	  histp = &hist2[first] ;
X	  sum1 = 0 ;
X	  for( j = first; j < i; ++j)
X	    sum1 += *histp++ ;
X	  sum2 = 0 ;
X	  for( j = i; j <= last; ++j)
X	    sum2 += *histp++ ;
X#ifdef	DEBUG
X	  if( sum1+sum2 != total  ||  sum1 == 0  ||  sum2 == 0 )
X	    fprintf(stderr, "??? splitbox: sum1=%d, sum2=%d, total=%d\n",
X		sum1,sum2,total) ;
X#endif	DEBUG
X	  new->total = sum1 ;
X	  ptr->total = sum2 ;
X	}
X
X
X	new->rmin = ptr->rmin ;
X	new->rmax = ptr->rmax ;
X	new->gmin = ptr->gmin ;
X	new->gmax = ptr->gmax ;
X	new->bmin = ptr->bmin ;
X	new->bmax = ptr->bmax ;
X	switch(which)
X	{
X	  case RED:
X	    new->rmax = i-1 ;
X	    ptr->rmin = i ;
X	    break ;
X
X	  case GREEN:
X	    new->gmax = i-1 ;
X	    ptr->gmin = i ;
X	  break ;
X
X	  case BLUE:
X	    new->bmax = i-1 ;
X	    ptr->bmin = i ;
X	  break ;
X	}
X	shrinkbox(new) ;
X	shrinkbox(ptr) ;
X}
X
X
X
X
Xstatic
Xshrinkbox(box)
Xregister Colorbox	*box ;
X{
Xregister int		*histp ;
Xregister int		ir,ig,ib ;
X
X
X	if(box->rmax > box->rmin)
X	{
X	  for(ir = box->rmin; ir <= box->rmax; ++ir)
X	    for(ig = box->gmin; ig <= box->gmax; ++ig)
X	    {
X	      histp = &histogram[ir][ig][box->bmin] ;
X	      for(ib = box->bmin; ib <= box->bmax; ++ib)
X		if( *histp++ != 0 )
X		{
X		  box->rmin = ir ;
X		  goto have_rmin ;
X		}
X	    }
Xhave_rmin:
X
X	  if(box->rmax > box->rmin)
X	    for(ir = box->rmax; ir >= box->rmin; --ir)
X	      for(ig = box->gmin; ig <= box->gmax; ++ig)
X	      {
X		histp = &histogram[ir][ig][box->bmin] ;
X		for(ib = box->bmin; ib <= box->bmax; ++ib)
X		  if( *histp++ != 0 )
X		  {
X		    box->rmax = ir ;
X		    goto have_rmax ;
X		  }
X	      }
X	}
Xhave_rmax:
X
X	if( box->gmax > box->gmin )
X	{
X	  for(ig = box->gmin; ig <= box->gmax; ++ig)
X	    for(ir = box->rmin; ir <= box->rmax; ++ir)
X	    {
X	      histp = &histogram[ir][ig][box->bmin] ;
X	      for(ib = box->bmin; ib <= box->bmax; ++ib)
X		if( *histp++ != 0 )
X		{
X		  box->gmin = ig ;
X		  goto have_gmin ;
X		}
X	    }
Xhave_gmin:
X
X	  if( box->gmax > box->gmin )
X	    for(ig = box->gmax; ig >= box->gmin; --ig)
X	      for(ir = box->rmin; ir <= box->rmax; ++ir)
X	      {
X		histp = &histogram[ir][ig][box->bmin] ;
X		for(ib = box->bmin; ib <= box->bmax; ++ib)
X		  if( *histp++ != 0 )
X		  {
X		    box->gmax = ig ;
X		    goto have_gmax ;
X		  }
X	      }
X	}
Xhave_gmax:
X
X	if( box->bmax > box->bmin )
X	{
X	  for(ib = box->bmin; ib <= box->bmax; ++ib)
X	    for(ir = box->rmin; ir <= box->rmax; ++ir)
X	    {
X	      histp = &histogram[ir][box->gmin][ib] ;
X	      for(ig = box->gmin; ig <= box->gmax; ++ig)
X	      {
X		if( *histp != 0 )
X		{
X		  box->bmin = ib ;
X		  goto have_bmin ;
X		}
X		histp += B_LEN ;
X	      }
X	    }
Xhave_bmin:
X
X	  if( box->bmax > box->bmin )
X	    for(ib = box->bmax; ib >= box->bmin; --ib)
X	      for(ir = box->rmin; ir <= box->rmax; ++ir)
X	      {
X		histp = &histogram[ir][box->gmin][ib] ;
X		for(ig = box->gmin; ig <= box->gmax; ++ig)
X		{
X		  if( *histp != 0 )
X		  {
X		    box->bmax = ib ;
X		    goto have_bmax ;
X		  }
X		  histp += B_LEN ;
X		}
X	      }
X	}
Xhave_bmax: return ;
X}
X
X
X
X
X/*
X * This function assigns a color to a color box.  Currently, the resulting
X * color is the center of the box.
X *
X * It is possible that a histogram-weighted center would provide better
X * results, but I seem to have tried that and not liked the results.
X */
X
X
Xstatic
Xassign_color(ptr,rp,gp,bp)
Xregister Colorbox	*ptr ;
X	unsigned char	*rp,*gp,*bp ;
X{
Xregister int		*histp ;
Xregister int		ir,ig,ib ;
Xregister long		red = 0, green = 0, blue = 0 ;
X
X	*rp = ((ptr->rmin + ptr->rmax) << (COLOR_DEPTH - B_DEPTH)) / 2 ;
X	*gp = ((ptr->gmin + ptr->gmax) << (COLOR_DEPTH - B_DEPTH)) / 2 ;
X	*bp = ((ptr->bmin + ptr->bmax) << (COLOR_DEPTH - B_DEPTH)) / 2 ;
X
X#ifdef	COMMENT
X#ifdef	DEBUG
X	if( ptr->total <= 0 )
X	{
X	  fprintf(stderr,"??? assign_color: total = %d\n",ptr->total) ;
X	  return ;
X	}
X#endif	DEBUG
X
X	for( ir = ptr->rmin; ir <= ptr->rmax; ++ir)
X	  for( ig = ptr->gmin; ig <= ptr->gmax; ++ig)
X	  {
X	    histp = &histogram[ir][ig][ptr->bmin] ;
X	    for( ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X	    {
X	      if( *histp != 0 )
X	      {
X		red += ir * *histp ;
X		green += ig * *histp ;
X		blue += ib * *histp ;
X	      }
X	      ++histp ;
X	    }
X	  }
X
X	*rp = (red << (COLOR_DEPTH - B_DEPTH)) / ptr->total ;
X	*gp = (green << (COLOR_DEPTH - B_DEPTH)) / ptr->total ;
X	*bp = (blue << (COLOR_DEPTH - B_DEPTH)) / ptr->total ;
X#endif	COMMENT
X}
X
X
X
Xstatic	C_cell *
Xcreate_colorcell(red,green,blue)
X	int		red,green,blue ;
X{
Xregister int		ir,ig,ib, i ;
X	int		mindist ;
Xregister C_cell		*ptr ;
X
X	ir = red >> (COLOR_DEPTH-C_DEPTH) ;
X	ig = green >> (COLOR_DEPTH-C_DEPTH) ;
X	ib = blue >> (COLOR_DEPTH-C_DEPTH) ;
X
X#ifdef	COMMENT
X	red &= ~0 << (COLOR_DEPTH-C_DEPTH) ;
X	green &= ~0 << (COLOR_DEPTH-C_DEPTH) ;
X	blue &= ~0 << (COLOR_DEPTH-C_DEPTH) ;
X#endif	COMMENT
X
X	ptr = (C_cell *) malloc( sizeof(C_cell) ) ;
X	*( ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib ) = ptr ;
X
X	ptr->num_ents = 0 ;
X
X
X	/* step 1: find all colors inside this cell, while we're at
X		  it, find distance of centermost point to furthest
X		  corner */
X
X	mindist = 99999999 ;
X	for(i=0; i<num_colors; ++i)
X	  if( rm[i]>>(COLOR_DEPTH-C_DEPTH) == ir  &&
X	      gm[i]>>(COLOR_DEPTH-C_DEPTH) == ig  &&
X	      bm[i]>>(COLOR_DEPTH-C_DEPTH) == ib)
X	  {
X	    register int	tmp, dist ;
X
X	    ptr->entries[ptr->num_ents][0] = i ;
X	    ptr->entries[ptr->num_ents][1] = 0 ;
X	    ++ptr->num_ents ;
X
X	    tmp = rm[i] - red ;
X	    if( tmp < (MAX_COLOR/C_LEN/2) )
X	      tmp = MAX_COLOR/C_LEN-1 - tmp ;
X	    dist = tmp*tmp ;
X
X	    tmp = gm[i] - green ;
X	    if( tmp < (MAX_COLOR/C_LEN/2) )
X	      tmp = MAX_COLOR/C_LEN-1 - tmp ;
X	    dist += tmp*tmp ;
X
X	    tmp = bm[i] - blue ;
X	    if( tmp < (MAX_COLOR/C_LEN/2) )
X	      tmp = MAX_COLOR/C_LEN-1 - tmp ;
X	    dist += tmp*tmp ;
X
X	    if( dist < mindist )
X	      mindist = dist ;
X	  }
X
X
X	/* step 3: find all points within that distance to cell */
X
X	for( i=0; i<num_colors; ++i )
X	{
X	  register int	dist, tmp ;
X
X	  if( rm[i] >> (COLOR_DEPTH-C_DEPTH) != ir  ||
X	      gm[i] >> (COLOR_DEPTH-C_DEPTH) != ig  ||
X	      bm[i] >> (COLOR_DEPTH-C_DEPTH) != ib)
X	  {
X	    dist = 0 ;
X
X	    if( (tmp = red - rm[i]) > 0  ||
X		(tmp = rm[i] - (red + MAX_COLOR/C_LEN-1)) > 0 )
X	      dist += tmp*tmp ;
X
X	    if( (tmp = green - gm[i]) > 0  ||
X		(tmp = gm[i] - (green + MAX_COLOR/C_LEN-1)) > 0 )
X	      dist += tmp*tmp ;
X
X	    if( (tmp = blue - bm[i]) > 0  ||
X		(tmp = bm[i] - (blue + MAX_COLOR/C_LEN-1)) > 0 )
X	      dist += tmp*tmp ;
X
X	    if( dist < mindist )
X	    {
X	      ptr->entries[ptr->num_ents][0] = i ;
X	      ptr->entries[ptr->num_ents][1] = dist ;
X	      ++ptr->num_ents ;
X	    }
X	  }
X	}
X
X	/* sort color cells by distance, use cheap exchange sort */
X	{
X	  register int	n, tmp, i ;
X	  int		next_n ;
X
X	  n = ptr->num_ents - 1 ;
X	  while( n > 0 )
X	  {
X	    next_n = 0 ;
X	    for( i=0; i<n; ++i)
X	    {
X	      if( ptr->entries[i][1] > ptr->entries[i+1][1] )
X	      {
X		tmp = ptr->entries[i][0] ;
X		ptr->entries[i][0] = ptr->entries[i+1][0] ;
X		ptr->entries[i+1][0] = tmp ;
X		tmp = ptr->entries[i][1] ;
X		ptr->entries[i][1] = ptr->entries[i+1][1] ;
X		ptr->entries[i+1][1] = tmp ;
X		next_n = i ;
X	      }
X	    }
X	    n = next_n ;
X	  }
X	}
X	return( ptr ) ;
X}
X
X
X
X
Xstatic
Xmap_colortable()
X{
X	int		ir,ig,ib ;
Xregister int		*histp = &histogram[0][0][0] ;
Xregister C_cell		*cell ;
X
X	for(ir = 0; ir < B_LEN; ++ir)
X	  for(ig = 0; ig < B_LEN; ++ig)
X	    for(ib = 0; ib < B_LEN; ++ib)
X	    {
X	      if( *histp == 0 )
X		*histp = -1 ;
X	      else
X	      {
X		int		i ;
X		register int	j, tmp, d2, dist ;
X
X		cell = *( ColorCells + ( ((ir>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2)
X		          + ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH )
X			  + (ib>>(B_DEPTH-C_DEPTH)) ) ) ;
X
X		if(cell == NULL )
X		  cell = create_colorcell( ir<<(COLOR_DEPTH-B_DEPTH),
X					   ig<<(COLOR_DEPTH-B_DEPTH),
X					   ib<<(COLOR_DEPTH-B_DEPTH) ) ;
X
X		dist = 9999999 ;
X		for(i = 0;
X		    i < cell->num_ents  &&  dist > cell->entries[i][1] ;
X		    ++i)
X		{
X		  j = cell->entries[i][0] ;
X		  d2 = rm[j] - (ir << (COLOR_DEPTH-B_DEPTH)) ;
X		  d2 *= d2 ;
X		  tmp = gm[j] - (ig << (COLOR_DEPTH-B_DEPTH)) ;
X		  d2 += tmp*tmp ;
X		  tmp = bm[j] - (ib << (COLOR_DEPTH-B_DEPTH)) ;
X		  d2 += tmp*tmp ;
X		  if( d2 < dist )
X		  {
X		    dist = d2 ;
X		    *histp = j ;
X		  }
X		}
X#ifdef	DEBUG
X		if( dist == 9999999 )
X		  fprintf(stderr,"??? map_colortable: dist=9999999\n") ;
X#endif	DEBUG
X	      }
X	      ++histp ;
X	    }
X}
X
X
X
X
X
X
X
X
X
X
X/*
X * straight quantization.  Each pixel is mapped to the colors
X * closest to it.  Color values are rounded to the nearest color
X * table entry.
X */
X
Xstatic
Xquant()
X{
X	unsigned char	*outline ;
X	unsigned char	*inline ;
Xregister unsigned char	*outptr ;
Xregister unsigned char	*inptr ;
Xregister int		i, j ;
X
X
X
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X
X	outline = (unsigned char *) malloc( olinebytes ) ;
X
X	for(i=0; i < rh.ras_height; ++i)
X	{
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  inptr = inline ;
X	  outptr = outline;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    register int	tmp,red,green,blue ;
X
X	    switch( rh.ras_depth )
X	    {
X	      case 8: blue = green = red = *inptr++ ; break ;
X	      case 32: ++inptr ;
X	      case 24: blue = *inptr++ ; green = *inptr++ ; red = *inptr++ ;
X	    }
X
X	    if (rh.ras_maplength > 0)
X	    {
X	      red = colormap.map[0][red] ;
X	      green = colormap.map[1][green] ;
X	      blue = colormap.map[2][blue] ;
X	    }
X
X	    red >>= (COLOR_DEPTH-B_DEPTH) ;
X	    green >>= (COLOR_DEPTH-B_DEPTH) ;
X	    blue >>= (COLOR_DEPTH-B_DEPTH) ;
X
X	    *outptr++ = histogram[red][green][blue] ;
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X	free(inline) ;
X	free(outline) ;
X}
X
X
X
X
X
Xstatic
Xquant_fsdither()
X{
X	unsigned char	*outline, *inline ;
X	short		*thisline, *nextline, *tmpptr ;
X	unsigned char	*inptr ;
Xregister unsigned char	*outptr ;
Xregister short		*thisptr, *nextptr ;
Xregister int		i, j ;
X	int		imax, jmax ;
X	int		lastline, lastpixel ;
X
X
X	imax = rh.ras_height - 1 ;
X	jmax = rh.ras_width - 1 ;
X
X	inline = (unsigned char *) malloc( linebytes ) ;
X	thisline = (short *) malloc( rh.ras_width * 3 * sizeof(short)) ;
X	nextline = (short *) malloc( rh.ras_width * 3 * sizeof(short)) ;
X
X	outline = (unsigned char *) malloc( olinebytes ) ;
X
X	/*
X	 * get first line
X	 */
X	pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	{
X	  inptr = inline ;
X	  nextptr = nextline ;
X	  for(j=0; j < rh.ras_width; ++j)
X	  {
X	    register int	red,green,blue ;
X
X	    switch( rh.ras_depth )
X	    {
X	      case 8: blue = green = red = *inptr++ ; break ;
X	      case 32: ++inptr ;
X	      case 24: blue = *inptr++ ; green = *inptr++ ; red = *inptr++ ;
X	    }
X
X	    if (rh.ras_maplength > 0)
X	    {
X	      red = colormap.map[0][red] ;
X	      green = colormap.map[1][green] ;
X	      blue = colormap.map[2][blue] ;
X	    }
X	    *nextptr++ = blue ;
X	    *nextptr++ = green ;
X	    *nextptr++ = red ;
X	  }
X	}
X
X	for(i=0; i < rh.ras_height; ++i)
X	{
X	  tmpptr = thisline ;
X	  thisline = nextline ;
X	  nextline = tmpptr ;
X	  lastline = (i == imax) ;
X	  pr_get_bytes(stdin, &rh, linebytes, inline) ;
X	  {
X	    register int	red,green,blue ;
X
X	    inptr = inline ;
X	    nextptr = nextline ;
X	    for(j=0; j < rh.ras_width; ++j)
X	    {
X	      switch( rh.ras_depth )
X	      {
X		case 8: blue = green = red = *inptr++ ; break ;
X		case 32: ++inptr ;
X		case 24: blue = *inptr++ ; green = *inptr++ ; red = *inptr++ ;
X	      }
X
X	      if (rh.ras_maplength > 0)
X	      {
X		red = colormap.map[0][red] ;
X		green = colormap.map[1][green] ;
X		blue = colormap.map[2][blue] ;
X	      }
X	      *nextptr++ = blue ;
X	      *nextptr++ = green ;
X	      *nextptr++ = red ;
X	    }
X	  }
X
X	  thisptr = thisline ;
X	  nextptr = nextline ;
X	  outptr = outline;
X
X	  for(j=0; j < rh.ras_width ; ++j)
X	  {
X	    int			red,green,blue ;
X	    register int	oval, r2,g2,b2 ;
X
X	    lastpixel = (j == jmax) ;
X
X	    if( (b2 = *thisptr++) < 0 ) b2 = 0 ;
X	    else if( b2 >= MAX_COLOR ) b2 = MAX_COLOR-1 ;
X	    if( (g2 = *thisptr++) < 0 ) g2 = 0 ;
X	    else if( g2 >= MAX_COLOR ) g2 = MAX_COLOR-1 ;
X	    if( (r2 = *thisptr++) < 0 ) r2 = 0 ;
X	    else if( r2 >= MAX_COLOR ) r2 = MAX_COLOR-1 ;
X
X	    red = r2 ;
X	    green = g2 ;
X	    blue = b2 ;
X
X	    r2 >>= (COLOR_DEPTH-B_DEPTH) ;
X	    g2 >>= (COLOR_DEPTH-B_DEPTH) ;
X	    b2 >>= (COLOR_DEPTH-B_DEPTH) ;
X
X	    if( (oval = histogram[r2][g2][b2]) == -1)
X	    {
X	      int		ci ;
X	      register int	cj, tmp, d2, dist ;
X	      register C_cell	*cell ;
X
X	      cell = *( ColorCells + ( ((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2)
X			+ ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH )
X			+ (b2>>(B_DEPTH-C_DEPTH)) ) ) ;
X
X	      if(cell == NULL )
X		cell = create_colorcell( red, green, blue ) ;
X
X
X	      dist = 9999999 ;
X	      for(ci = 0;
X		  ci < cell->num_ents  &&  dist > cell->entries[ci][1] ;
X		  ++ci)
X	      {
X		cj = cell->entries[ci][0] ;
X		d2 = (rm[cj] >> (COLOR_DEPTH-B_DEPTH)) - r2 ;
X		d2 *= d2 ;
X		tmp = (gm[cj] >> (COLOR_DEPTH-B_DEPTH)) - g2 ;
X		d2 += tmp*tmp ;
X		tmp = (bm[cj] >> (COLOR_DEPTH-B_DEPTH)) - b2 ;
X		d2 += tmp*tmp ;
X		if( d2 < dist )
X		{
X		  dist = d2 ;
X		  oval = cj ;
X		}
X	      }
X	      histogram[r2][g2][b2] = oval ;
X#ifdef	DEBUG
X	      if( dist == 9999999 )
X		fprintf(stderr,"??? quant: dist=9999999\n") ;
X#endif	DEBUG
X	    }
X
X	    *outptr++ = oval ;
X
X	    r2 = (red -= rm[oval]) ;
X	    g2 = (green -= gm[oval]) ;
X	    b2 = (blue -= bm[oval]) ;
X
X
X	    if( !lastline )
X	    {
X	      if( j != 0 )
X	      {
X		nextptr[-3] += b2 * 3 / 16 ;
X		nextptr[-2] += g2 * 3 / 16 ;
X		nextptr[-1] += r2 * 3 / 16 ;
X	      }
X	      nextptr[0] += b2 * 5 / 16 ;
X	      nextptr[1] += g2 * 5 / 16 ;
X	      nextptr[2] += r2 * 5 / 16 ;
X	      if( !lastpixel )
X	      {
X		nextptr[3] += b2 / 16 ;
X		nextptr[4] += g2 / 16 ;
X		nextptr[5] += r2 / 16 ;
X	      }
X	      nextptr += 3 ;
X	    }
X	    if( !lastpixel )
X	    {
X	      red -= r2 * 3 / 16 ;
X	      red -= r2 * 5 / 16 ;
X	      red -= r2 / 16 ;
X	      green -= g2 * 3 / 16 ;
X	      green -= g2 * 5 / 16 ;
X	      green -= g2 / 16 ;
X	      blue -= b2 * 3 / 16 ;
X	      blue -= b2 * 5 / 16 ;
X	      blue -= b2 / 16 ;
X	      thisptr[0] += blue ;
X	      thisptr[1] += green ;
X	      thisptr[2] += red ;
X	    }
X	  }
X	  fwrite(outline, 1, olinebytes, stdout) ;
X	}
X	free(inline) ;
X	free(thisline) ;
X	free(nextline) ;
X	free(outline) ;
X}
X
X
X
X
X#ifdef	DEBUG
Xshowboxes()
X{
X	Colorbox	*ptr ;
X
X	printf("boxes:\n") ;
X	ptr = usedboxes ;
X	while(ptr != NULL)
X	{
X	  printf("  %d<r<%d, %d<g<%d, %d<b<%d\n", ptr->rmin,ptr->rmax,
X	      ptr->gmin,ptr->gmax, ptr->bmin,ptr->bmax) ;
X	  ptr = ptr->next ;
X	}
X}
X
X
X
Xshowcell(ptr)
Xregister C_cell	*ptr ;
X{
X	int	i ;
X	int	j ;
X
X	fprintf(stderr, "n=%d\n", ptr->num_ents) ;
X	for(i=0; i<ptr->num_ents; ++i)
X	{
X	  j = ptr->entries[i][0] ;
X	  fprintf(stderr, "(%d,%d): (%d,%d,%d)\n",
X		  j, ptr->entries[i][1], rm[j],gm[j],bm[j]) ;
X	}
X}
X
X
X
Xint
Xtestbox(box)
Xregister Colorbox	*box ;
X{
Xregister int		ir,ig,ib, total ;
X
X	total = 0 ;
X	for(ir=box->rmin; ir<=box->rmax; ++ir)
X	  for(ig=box->gmin; ig<=box->gmax; ++ig)
X	    for(ib=box->bmin; ib<=box->bmax; ++ib)
X	      total += histogram[ir][ig][ib] ;
X
X	return total ;
X}
X#endif	DEBUG
SHAR_EOF
len=`wc -c < median.c`
if test $len !=    27988 ; then
echo error: median.c was $len bytes long, should have been    27988
fi
fi # end of overwriting check
if test -f pr_stream.c
then
echo shar: will not over-write existing file pr_stream.c
else
echo shar: extracting 'pr_stream.c',     3512 characters
sed 's/^X//' > pr_stream.c <<'SHAR_EOF'
X/* pr_stream routines:  allow images to be read in stream
X * fashion rather than loading them into memory.
X *
X * These routines are used in conjunction with the pr_io routines.  It
X * is the application's responsibility to read the header and
X * colormaps
X *
X *
X * routines:
X *
X * pr_read_init(header)
X *	struct rasterfile *header ;
X *
X *	sets up to read an image
X *
X *
X * pr_get_bytes(file, header, len, buffer)
X *	FILE		*file ;
X *	struct rasterfile *header ;
X *	int		len ;
X *	char		*buffer ;
X *
X *	read data from input.  len should be a multiple of 3 for 24-bit
X *	images and a multiple of 4 for 32-bit images.
X *
X *
X * int
X * pr_get_byte(file, header)
X *	FILE		*file ;
X *	struct rasterfile *header ;
X *
X *	read and return one byte from input
X *
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <pixrect/pixrect.h>
X#include <pixrect/memvar.h>
X#include <pixrect/pr_io.h>
X#include <rasterfile.h>
X
X#define	ESCAPE	128
X
X
X
Xstatic	unsigned char	icvalue ;
Xstatic	int		icount ;
Xstatic	unsigned char	icalpha, icblue, icgreen, icred ;
X
X
X
Xint
Xpr_read_init(header)
X	struct rasterfile	*header ;
X{
X	icount = 0 ;
X}
X
X
X
Xint
Xpr_get_bytes(file, header, len, buffer)
Xregister FILE			*file ;
Xregister struct rasterfile	*header ;
Xregister int			len ;
Xregister unsigned char		*buffer ;
X{
X	if(header->ras_type != RT_BYTE_ENCODED)
X	  return( fread(buffer, 1, len, file) ) ;
X
X	else
X	  switch( header->ras_depth )
X	  {
X	    case 1:
X	    case 8:
X	      while( --len >= 0)
X	      {
X		if(icount > 0)
X		{
X		  --icount ;
X		  *buffer++ = icvalue ;
X		}
X		else
X		{
X		  if( (icvalue = getc(file)) != ESCAPE )
X		    *buffer++ = icvalue ;
X		  else
X		  {
X		    if( (icount = getc(file)) == 0 )
X		      *buffer++ = ESCAPE ;
X		    else
X		    {
X		      *buffer++ = icvalue = getc(file) ;
X		    }
X		  }
X		}
X	      }
X	      break ;
X	    
X	    case 24:
X	      len /= 3 ;
X	      while( len-- > 0 )
X	      {
X		if(icount > 0)
X		{
X		  --icount ;
X		  *buffer++ = icblue ;
X		  *buffer++ = icgreen ;
X		  *buffer++ = icred ;
X		}
X		else
X		{
X		  if( (icvalue = getc(file)) != ESCAPE )
X		  {
X		    *buffer++ = icvalue ;
X		    *buffer++ = getc(file) ;
X		    *buffer++ = getc(file) ;
X		  }
X		  else
X		  {
X		    if( (icount = getc(file)) == 0 )
X		    {
X		      *buffer++ = ESCAPE ;
X		      *buffer++ = getc(file) ;
X		      *buffer++ = getc(file) ;
X		    }
X		    else
X		    {
X		      *buffer++ = icblue = getc(file) ;
X		      *buffer++ = icgreen = getc(file) ;
X		      *buffer++ = icred = getc(file) ;
X		    }
X		  }
X		}
X	      }
X	      break ;
X
X	    case 32:
X	      len /= 4 ;
X	      while( len-- > 0 )
X	      {
X		if(icount > 0)
X		{
X		  --icount ;
X		  *buffer++ = icalpha ;
X		  *buffer++ = icblue ;
X		  *buffer++ = icgreen ;
X		  *buffer++ = icred ;
X		}
X		else
X		{
X		  if( (icvalue = getc(file)) != ESCAPE )
X		  {
X		    *buffer++ = icvalue ;
X		    *buffer++ = getc(file) ;
X		    *buffer++ = getc(file) ;
X		    *buffer++ = getc(file) ;
X		  }
X		  else
X		  {
X		    if( (icount = getc(file)) == 0 )
X		    {
X		      *buffer++ = ESCAPE ;
X		      *buffer++ = getc(file) ;
X		      *buffer++ = getc(file) ;
X		      *buffer++ = getc(file) ;
X		    }
X		    else
X		    {
X		      *buffer++ = icalpha = getc(file) ;
X		      *buffer++ = icblue = getc(file) ;
X		      *buffer++ = icgreen = getc(file) ;
X		      *buffer++ = icred = getc(file) ;
X		    }
X		  }
X		}
X	      }
X	      break ;
X	  }
X
X
X
X	return 0 ;
X}
X
X
X
Xunsigned char
Xpr_get_byte(file, header)
X	FILE			*file ;
X	struct rasterfile	*header ;
X{
X	unsigned char		rval ;
X
X	pr_get_bytes(file, header, 1, &rval) ;
X	return( rval ) ;
X}
SHAR_EOF
len=`wc -c < pr_stream.c`
if test $len !=     3512 ; then
echo error: pr_stream.c was $len bytes long, should have been     3512
fi
fi # end of overwriting check
exit 0
-- 
		-ed falk, sun microsystems
		 sun!falk, falk@sun.com
		 card-carrying ACLU member.

falk@sun.uucp (Ed Falk) (02/03/89)

OOOPS.  I forgot a header file, ntsc.h.  Here it is:


static	unsigned int	ntscr[] = {
	    0,   77,  153,  230,  306,  383,  459,  536,
	  612,  689,  765,  842,  919,  995, 1072, 1148,
	 1225, 1301, 1378, 1454, 1531, 1607, 1684, 1761,
	 1837, 1914, 1990, 2067, 2143, 2220, 2296, 2373,
	 2449, 2526, 2602, 2679, 2756, 2832, 2909, 2985,
	 3062, 3138, 3215, 3291, 3368, 3444, 3521, 3598,
	 3674, 3751, 3827, 3904, 3980, 4057, 4133, 4210,
	 4286, 4363, 4440, 4516, 4593, 4669, 4746, 4822,
	 4899, 4975, 5052, 5128, 5205, 5282, 5358, 5435,
	 5511, 5588, 5664, 5741, 5817, 5894, 5970, 6047,
	 6124, 6200, 6277, 6353, 6430, 6506, 6583, 6659,
	 6736, 6812, 6889, 6966, 7042, 7119, 7195, 7272,
	 7348, 7425, 7501, 7578, 7654, 7731, 7807, 7884,
	 7961, 8037, 8114, 8190, 8267, 8343, 8420, 8496,
	 8573, 8649, 8726, 8803, 8879, 8956, 9032, 9109,
	 9185, 9262, 9338, 9415, 9491, 9568, 9645, 9721,
	 9798, 9874, 9951,10027,10104,10180,10257,10333,
	10410,10487,10563,10640,10716,10793,10869,10946,
	11022,11099,11175,11252,11329,11405,11482,11558,
	11635,11711,11788,11864,11941,12017,12094,12170,
	12247,12324,12400,12477,12553,12630,12706,12783,
	12859,12936,13012,13089,13166,13242,13319,13395,
	13472,13548,13625,13701,13778,13854,13931,14008,
	14084,14161,14237,14314,14390,14467,14543,14620,
	14696,14773,14850,14926,15003,15079,15156,15232,
	15309,15385,15462,15538,15615,15692,15768,15845,
	15921,15998,16074,16151,16227,16304,16380,16457,
	16534,16610,16687,16763,16840,16916,16993,17069,
	17146,17222,17299,17375,17452,17529,17605,17682,
	17758,17835,17911,17988,18064,18141,18217,18294,
	18371,18447,18524,18600,18677,18753,18830,18906,
	18983,19059,19136,19213,19289,19366,19442,19519,} ;
static	unsigned int	ntscg[] = {
	    0,  150,  301,  451,  601,  751,  902, 1052,
	 1202, 1352, 1503, 1653, 1803, 1954, 2104, 2254,
	 2404, 2555, 2705, 2855, 3005, 3156, 3306, 3456,
	 3607, 3757, 3907, 4057, 4208, 4358, 4508, 4658,
	 4809, 4959, 5109, 5260, 5410, 5560, 5710, 5861,
	 6011, 6161, 6311, 6462, 6612, 6762, 6913, 7063,
	 7213, 7363, 7514, 7664, 7814, 7964, 8115, 8265,
	 8415, 8566, 8716, 8866, 9016, 9167, 9317, 9467,
	 9617, 9768, 9918,10068,10218,10369,10519,10669,
	10820,10970,11120,11270,11421,11571,11721,11871,
	12022,12172,12322,12473,12623,12773,12923,13074,
	13224,13374,13524,13675,13825,13975,14126,14276,
	14426,14576,14727,14877,15027,15177,15328,15478,
	15628,15779,15929,16079,16229,16380,16530,16680,
	16830,16981,17131,17281,17432,17582,17732,17882,
	18033,18183,18333,18483,18634,18784,18934,19085,
	19235,19385,19535,19686,19836,19986,20136,20287,
	20437,20587,20738,20888,21038,21188,21339,21489,
	21639,21789,21940,22090,22240,22391,22541,22691,
	22841,22992,23142,23292,23442,23593,23743,23893,
	24044,24194,24344,24494,24645,24795,24945,25095,
	25246,25396,25546,25697,25847,25997,26147,26298,
	26448,26598,26748,26899,27049,27199,27350,27500,
	27650,27800,27951,28101,28251,28401,28552,28702,
	28852,29002,29153,29303,29453,29604,29754,29904,
	30054,30205,30355,30505,30655,30806,30956,31106,
	31257,31407,31557,31707,31858,32008,32158,32308,
	32459,32609,32759,32910,33060,33210,33360,33511,
	33661,33811,33961,34112,34262,34412,34563,34713,
	34863,35013,35164,35314,35464,35614,35765,35915,
	36065,36216,36366,36516,36666,36817,36967,37117,
	37267,37418,37568,37718,37869,38019,38169,38319,} ;
static	unsigned int	ntscb[] = {
	    0,   29,   58,   88,  117,  146,  175,  204,
	  233,  263,  292,  321,  350,  379,  409,  438,
	  467,  496,  525,  554,  584,  613,  642,  671,
	  700,  730,  759,  788,  817,  846,  876,  905,
	  934,  963,  992, 1021, 1051, 1080, 1109, 1138,
	 1167, 1197, 1226, 1255, 1284, 1313, 1342, 1372,
	 1401, 1430, 1459, 1488, 1518, 1547, 1576, 1605,
	 1634, 1663, 1693, 1722, 1751, 1780, 1809, 1839,
	 1868, 1897, 1926, 1955, 1985, 2014, 2043, 2072,
	 2101, 2130, 2160, 2189, 2218, 2247, 2276, 2306,
	 2335, 2364, 2393, 2422, 2451, 2481, 2510, 2539,
	 2568, 2597, 2627, 2656, 2685, 2714, 2743, 2772,
	 2802, 2831, 2860, 2889, 2918, 2948, 2977, 3006,
	 3035, 3064, 3094, 3123, 3152, 3181, 3210, 3239,
	 3269, 3298, 3327, 3356, 3385, 3415, 3444, 3473,
	 3502, 3531, 3560, 3590, 3619, 3648, 3677, 3706,
	 3736, 3765, 3794, 3823, 3852, 3881, 3911, 3940,
	 3969, 3998, 4027, 4057, 4086, 4115, 4144, 4173,
	 4202, 4232, 4261, 4290, 4319, 4348, 4378, 4407,
	 4436, 4465, 4494, 4524, 4553, 4582, 4611, 4640,
	 4669, 4699, 4728, 4757, 4786, 4815, 4845, 4874,
	 4903, 4932, 4961, 4990, 5020, 5049, 5078, 5107,
	 5136, 5166, 5195, 5224, 5253, 5282, 5311, 5341,
	 5370, 5399, 5428, 5457, 5487, 5516, 5545, 5574,
	 5603, 5633, 5662, 5691, 5720, 5749, 5778, 5808,
	 5837, 5866, 5895, 5924, 5954, 5983, 6012, 6041,
	 6070, 6099, 6129, 6158, 6187, 6216, 6245, 6275,
	 6304, 6333, 6362, 6391, 6420, 6450, 6479, 6508,
	 6537, 6566, 6596, 6625, 6654, 6683, 6712, 6742,
	 6771, 6800, 6829, 6858, 6887, 6917, 6946, 6975,
	 7004, 7033, 7063, 7092, 7121, 7150, 7179, 7208,
	 7238, 7267, 7296, 7325, 7354, 7384, 7413, 7442,} ;
-- 
		-ed falk, sun microsystems
		 sun!falk, falk@sun.com
		 card-carrying ACLU member.

mpksla%ritcv@cs.rit.edu (Michael Kirby) (02/08/89)

   We have compiled the code posted earlier for optimizing a 24 bit image
   to an 8 bit image (Dither24to8 and Median, posted by Ed Falk).

   We are aware that the program should take a 24 bit rasterfile,
   but dont know the format for one.  We are aware of how to use
   rasterfiles but dont wish to trudge through all the code to find
   what the exact format is.  Any clues?

   -Michael Kirby
      &
    Eric S. Law

mpk9172%ucss@cs.rit.edu rochester!ritcv!mpk9172 mpk9172@ritvax.bitnet
esl0422%ucss@cs.rit.edu rochester!ritcv!esl0422 esl0422@ritvax.bitnet
"Who else ray-traces out there?"