[comp.sources.amiga] v89i161: iff2sun - convert iff to sun rasterfile format v1.4

page%swap@Sun.COM (Bob Page) (06/22/89)

Submitted-by: raz@kilowatt (Steve -Raz- Berry)
Posting-number: Volume 89, Issue 161
Archive-name: iff/iff2sun14.1

Here's the latest iff2sun.  This one does HAM!  Enjoy.

[Compile and run this on a Sun workstation, not your Amiga.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	iff2sun.n
#	Makefile
#	iff2sun.c
#	convert.c
#	hamtosun
# This is archive 1 of a 1-part kit.
# This archive created: Wed May 31 18:38:31 1989
echo "extracting iff2sun.n"
sed 's/^X//' << \SHAR_EOF > iff2sun.n
X.* uncomment the next line for the Amiga nro program
X.* .so an
X.nr h 5 	 @" header indent
X.nr i 10	 @" normal text indent
X.nr s @ni-@nh	 @" section heading indent (to the LEFT)
X.TH IFF2SUN 1 "5/29/89" "Version 1.4" "UNIX UTILITIES" "Checkered Ball"
X.* .TH IFF2SUN 1 "Checkered Ball" "UNIX UTILITIES" "Version 1.4"
X.sp 2
X.SH NAME
Xiff2sun/convert - 
X.BR "Amiga
Xto Sun rasterfile conversion program
X.SH SYNOPSIS
X.in +5
X.ta +0
X.ti -5
X.BR "iff2sun [-chp] [infile] [outfile]
X.sp 1
Xif infile is not specified stdin is used.
Xif outfile is not specified then stdout is used.
X.br
X-c yields a color rasterfile (greyscale is the default).
X.br
X-h switches extra half-bright mode on.
X.br
X-p enables horizontal pixel replication. This 
X.br 
X.in +3
Xswitch is usefull for lores and HAM pictures to
Xoffset the aspect ratio difference between the
Xtwo machines (also EHB).
X.in -3
X.sp 1
X.ti -5
X.BR "convert [-c n] [-f] infile outfile
X.sp 1
Xthe (in/out)files MUST be entered (or you get an error message).
X.br
X-c n Set the output colortable to n entries. The default
Xis 256 colors.
X.br
X-f Use Floyd-Steinberg dithering on the output.
X.sp 1
X.in -5
X.ta
X.SH "DESCRIPTION
X.IR "IFF2SUN & CONVERT
X.sp 1
XWhat's it do? This little guy reads an Amiga IFF ILBM file and 
Xoutputs Sun rasterfile format. GIGO (Georgous In Garbage Out).
X.PP
XFor HAM pictures 
X.IR "iff2sun
Xsimply outputs an RGB color file that the Sun
XWILL NOT UNDERSTAND. You then need to run another program called
X.IR "convert
Xto format it into a Sun rasterfile. 
X.PP
XTo properly use this program, you must unshar the distribution
Xand type 'make'. Place the binaries in a convient directory.
XMAKE your executables on a Sun! It does you very little good
Xto try to compile this on your Amiga.
X.PP
X.BR "Convert
Xtakes a partly converted iff file, generated by
X.BR "iff2sun,
Xand converts it the rest of the way to produce a Sun rasterfile.
XThe only supported and tested use of convert is 
X.BR "convert 
X.IR "in out.
XThe other documented switches that the program accepts are not
Xtested at this time.
X.sp 1
X.SH "CONVERTING A HAM PICTURE:
X.PP
XIn order to do conversion from a HAM Iff to a Sun readable
Xfile format, I had to find a routine that would do color quantization
Xfrom 4096 colors to 256. I was able to modify a program released in
X1982 at SIGGRAPH to do this function. Unfortunately, integrating this 
Xinto iff2sun would prove to be too much trouble. Instead I modified
Xiff2sun to detect when it was working on a HAM picture, and do a straight
XRGB color dump, and then use the secondary program to finish doing the
Xcolor quantization.
XSo, when you want to do HAM conversion, follow a sequence like
Xthis:
X.sp 1
X.in +5
X.BR "iff2sun -cp porche930 porche930.out
X.br
X.BR "convert porche930.out porche930.pic
X.in -5
X.sp 1
XThe resulting porche930.pic, in this case, will be a Sun readable
X8 bit plane rasterfile. You can then go and do some dithering or whatever
Xit is that you want to do. There is a supplied script called hamtosun
Xthat will do all of the standard things to convert a ham picture.
X.PP
X.BR "Iff2sun
Xoutputs an 8 bitplane sun rasterfile with a color table,
X(EXCEPT WHEN A HAM PICTURE IS DETECTED, THEN YOU NEED TO RUN 
X.BR "CONVERT
X) which defaults to shades of gray. Using the -c color flag will
Xput the IFF
X.BR "Amiga
Xcolor table into the rasterfile. If you want to display
Xthis on a monochrome tube (which is what I have) you should go 
Xthrough the folowing steps:
X.sp 1
X.in +5
X1> iff2sun inputfile outputfile
X.br
X2> rastrepl outputfile output.bigger
X.br
X3> rasfilter8to1 -d output.bigger output.mono
X.in -5
X.sp 1
XThe rastrepl just pixel replicates the image so that it is large
Xenough to see the detail. you can run this as many times as you
Xlike, but remember the image quadruples each time you use it.
XMost sane people will only run it once.
X.PP
XNOTE: For HAM conversion you NEED to include the -c switch. The
Xcolor tables are used internally to simulate the Amiga HAM mode.
X.PP
XThe rasfilter8to1 program converts the color file (8 bit
Xplanes) to mono (1 plane) and does some fancy dithering (-d).
XTo prepare an image for a color system, just include
Xthe -c switch and either display it as it is, or replicate it
Xto the desired size first.
X.PP
XBoth the rastrepl and rasfilter8to1 program are part of a standard Sun
Xrelease. They should be included on your system.
X.PP
XUsing 
X.IR "Extra-half-bright
Xor 
X.IR "HAM
Xmode, or any 320x400 format will
Xproduce an image that will looked elongated on a Sun screen. There
Xare several ways to fix this. The easiest is to convert your pictures
Xto 640x400 mode and then display them. The second easiest is to rip
Xout 1/3 of the horizontal scan lines in the resultant image. The third
Xis to have ME write iff2sun to pixel replicate horizontally to offset
Xthe aspect ratio change. If the third option is the only one acceptable
Xto you, then you are in luck. All you have to do is run iff2sun with
Xthe -p switch on the command line. You will now get pictures that will look 
Xsomewhat better in this 320 (lo-res) mode. Remember, the executable
Xat this time only pixel-replicates in the horizontal direction. 
XThat means that a pixel replicated
X640 (hi-res) image will come out 1280x400 before you do anything to it. 
XIt is recommended that you pixel replicate your HAM pictures.
X.sp 1  
X.SH "Limitations:
XI have only tested this on DPaintII images, it is
Xvery possible that it might break with other formats.
XUnlikely, but possible :*)
X.sp 1
X.SH "Revision History:
X.sp 1
X10/31/88 First release, does only simple conversion and no color.
X.sp 1  
X12/88 Re-released with color support. Unfortunately few if any
Xone saw this release (it got lost on the way to the moderator)
XThis modification is Mark's claim to fame.
X.sp 1  
X4/26/89 Added Extra-half-bright support, also looking for the
Xbest way to convert HAM (4096) to 8 bit (256) format.
XAlso stuck in the pixel replicating code (trivial).
X.sp 1
X5/26/89 Added HAM mode conversion (with convert) and fixed an iff
Xparsing bug (parse()), and added new argument parsing supplied
Xby Tony Schene. The original convert.c was supplied by
XTony Kennedy as well as the bug report on DPaintIII images.
X.sp 1
X.SH "AUTHORS
X.sp 1
X.PP
XThis Program created (under tremendous pressure) by Steve Berry
Xwith help and modifications by Mark Thompson, Tony Schene and
XTony Kennedy.
X.sp 1 
XI hope you guys keep it clean. (If you don't, let me know :-)
X.sp 1  
X.in +3
XSteve Berry ...sun!kilowatt!raz 
X.br
Xor raz%kilowatt.EBay@@sun.com or even BIX: razberry
X.sp 1
XMark Thompson  ...decvax!savax!thompson
X.in -3
X.sp 1
X-------
X.br
X.IR "This is the revision 2 update information as posted by Mark Thompson.
X.sp 1
XWell I said I would do it, so here it is....the modified and improved
Xversion of iff2sun. The following changes have been made:
X.sp 1
X1) Can now convert IFF images up to 1024 lines vertical resolution.
X    (horizontal res is still 1024)
X.br
X2) Improved greyscale contrast.
X.br
X3) Now generates a Sun Rasterfile colormap.
X.br
X4) Supports either greyscale or color output.
X.sp 1
XAs for the problems some people have been having with the notorious
X'compression messed' error, it seems this is due to certain implementations
Xof kermit. Binary mode kermit in vt100 on the Amiga does work. If you
Xdon't have any luck with this method, uuencode seems to perform flawlessly.
X.sp 1
XMark Thompson
X.br
Xdecvax!savax!thompson
X
X
X
SHAR_EOF
echo "extracting Makefile"
sed 's/^X//' << \SHAR_EOF > Makefile
X#
X#	Makefile for iff2sun
X#
X#	Steve -Raz- Berry
X#	4/25/89
X#
XCFLAGS = -O
X#
X
XOBJS = iff2sun.o convert.o
X
Xall:	iff2sun convert doc
X
Xiff2sun:	$(OBJS)
X	cc $(CFLAGS) -o $@ $@.c
X
Xconvert:	$(OBJS)
X	cc $(CFLAGS) -o $@ $@.c
X
Xclean:
X	rm $(OBJS)
X
Xdoc:	$(OBJS)
X	nroff -man iff2sun.n > iff2sun.doc
X
Xshar:	
X	shar -f iff2sun.$@ iff2sun.n Makefile iff2sun.c convert.c hamtosun
X
SHAR_EOF
echo "extracting iff2sun.c"
sed 's/^X//' << \SHAR_EOF > iff2sun.c
X/*
X**      This Program created (under tremendous pressure) by Steve Berry
X**      with help and modifications by Mark Thompson and others.
X**
X**      I hope you guys keep it clean. (If you don't, let me know :-)
X**
X**      Status: PD all the way.
X**
X**      Please read the file named DOC, that came with this distribution for
X**      information on how to use iff2sun.
X**
X**	Limitations:
X**		  I have only tested this on DPaintII images, it is
X**		very possible that it might break with other formats.
X**		Unlikely, but possible :*)
X**
X**	CREDITS:
X**			Mark Thompson
X**			Steve (Raz) Berry
X**
X**	Contacts:
X**				Steve Berry ...sun!kilowatt!raz or 
X**                                             raz%kilowatt.EBay@sun.com
X**                                          BIX: razberry
X**                              Mark Thompson  ...decvax!savax!thompson
X**
X**      Revision History:
X**
X**      10/31/88 First release, does only simple conversion and no color.
X**
X**      12/88    Re-released with color support. Unfortunately few if any
X**               one saw this release (it got lost on the way to the moderator)
X**               (This is Mark's claim to fame)
X**
X**      4/26/89  Added Extra-half-bright support, also looking for the
X**               best way to convert HAM (4096) to 8 bit (256) format.
X**
X**	5/26/89	 Added HAM mode conversion (with convert) and fixed an iff
X**		 parsing bug (parse()), and added new argument parsing supplied
X**		 by Tony Schene. The original convert.c was supplied by
X**		 Tony Kennedy as well as the bug report on DPaintIII images.
X*/
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <rasterfile.h>
X
X#define HAM 0x800       /* Amiga Viewmodes */
X#define HALF 0x80
X#define HIRES 0x8000
X#define LACE 0x4
X#define TRUE 1
X#define FALSE 0
X
X/* Stupic C needs to be told about functions used before they
X   are defined */
X
Xunsigned char RGBtoLuma();
Xlong parse();
X
X/* Some variables and arrays */
X
Xu_char	rmap[128],rhmap[128];
Xu_char	gmap[128],ghmap[128];
Xu_char	bmap[128],bhmap[128];
Xu_char currentcolor[3];
Xu_char scanline[1024][6][128]; /* vert, planes, width (bytes) */
X
Xlong width, hieght, planes, hammode, numcolors;
Xlong temp, compress, i, j, k, l;
Xint colorpic=FALSE, halfb = FALSE, pixelrep = FALSE;
Xchar *Usage = "Usage: %s [ -chp ] [ infile ] [ outfile ]\n";
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	struct rasterfile header;
X	unsigned char clr;
X	long formsize, twidth, thieght;
X	static char chunk[5];
X	FILE *inh, *outh;
X	char	*progname= argv[0];
X	char c;
X	extern char *optarg;
X	extern int optind;
X        
X	chunk[4] = 0;
X        
X	inh = stdin;
X	outh = stdout;
X
X	while ((c = getopt (argc, argv, "chp")) != -1)
X		switch (c) {
X		case 'c':
X			colorpic = TRUE;
X			fprintf(stderr,"Color output selected\n");
X			break;
X		case 'h':
X			halfb = TRUE;
X			fprintf(stderr,"Extra Half-bright selected\n");
X			break;
X		case 'p':
X			pixelrep = TRUE;
X			break;
X		default:
X			fprintf (stderr, Usage, argv[0]);
X			exit (1);
X	      }
X	if (optind < argc)
X		inh = fopen(argv[optind],"r");
X	if (++optind < argc)
X		outh = fopen(argv[optind],"w");
X	if (++optind < argc) {
X		fprintf (stderr, Usage, argv[0]);
X		exit (1);
X	}
X        
X	if (inh == NULL){
X		perror(argv[1]);
X		exit(-10);
X	}        
X
X	if (outh == NULL){
X		perror(argv[2]);
X		exit(-10);
X	}        
X
X	/* Now comes the fun part... Reading the ILBM's */
X        
X	if (parse(inh,"FORM")) {
X		perror("I don't think that this is an IFF file");
X		exit(-10);
X	}
X
X	fread(&formsize, 4, 1, inh);    /* length of the FORM */
X
X	if (parse(inh,"ILBM")) {
X		perror("I don't think that this is an ILBM");
X		exit(-10);
X	}
X
X	if (parse(inh,"BMHD")) { 
X		perror("Bad format for ILBM.");
X		exit(-10);
X	}
X
X	cc(chunk);
X	fread(chunk, 4, 1, inh);    /* length of the BMHD */
X
X	cc(chunk);
X	fread(chunk, 2, 1, inh);    /* width of bitmap */
X	width = *((short *)chunk);
X
X	cc(chunk);
X	fread(chunk, 2, 1, inh);    /* Height of bitmap */
X	hieght = *((short *)chunk);
X
X	fread(chunk, 4, 1, inh);    /* Don't want the orgins */
X
X	cc(chunk);
X	fread(chunk, 1, 1, inh);    /* # of Planes */
X	planes = *((unsigned char *)chunk);
X
X	fread(chunk, 1, 1, inh);    /* Ignore the Mask */
X
X	cc(chunk);
X	fread(chunk, 1, 1, inh);    /* Compression? */
X
X	compress = *((unsigned char *)chunk);
X
X	fread(chunk, 1, 1, inh);    /* just a pad byte */
X
X/***********************************************************************
X**	DpaintII does not use the CAMG chunk, so we will have to ignore
X**	it for now...
X***********************************************************************/
X
X/*	if (planes == 6) {
X		perror("Sorry, I can't do HAM or Halfbright.");
X		exit(0);
X	} */
X
X	if (parse(inh,"CMAP")) {
X		perror("Bad format for ILBM, couldn't find Color Map.");
X	exit(-10);
X	} 
X
X	cc(chunk);
X	fread(chunk, 4, 1, inh);    /* # of color registers */
X	numcolors = *((long *)chunk);
X        
X	for(i=0; i<64; i++) {
X	  rmap[i]= i;
X	  gmap[i]= i;
X	  bmap[i]= i;
X	}
X
X	numcolors = numcolors/3 ;
X	for (i=0;i<numcolors;i++){
X		fread(chunk, 3, 1, inh);
X		chunk[3] = 0; chunk[4] = 0; 
X		chunk[0] = (chunk[0] >> 4) & 0x0f;
X		chunk[2] = (chunk[2] >> 4) & 0x0f;
X		chunk[1] = (chunk[1] >> 4) & 0x0f;
X		if(colorpic==TRUE) {
X		  rmap[i] = (chunk[0] << 4) | chunk[0];
X		  gmap[i] = (chunk[1] << 4) | chunk[1];
X		  bmap[i] = (chunk[2] << 4) | chunk[2];
X		} else {
X		  rmap[i] = RGBtoLuma(chunk);
X		  gmap[i] = RGBtoLuma(chunk);
X		  bmap[i] = RGBtoLuma(chunk);
X		}
X		if (halfb == TRUE) {
X		  rhmap[i] = rmap[i]>>1;
X		  ghmap[i] = gmap[i]>>1;
X		  bhmap[i] = bmap[i]>>1;
X		}
X	}
X
X	if (numcolors == 2)	/* if a mono image, realign to long word */
X		fread(chunk,1,2,inh);
X
X	if (parse(inh,"BODY")){
X		perror("No BODY data.");
X		exit(0);
X	}
X
X	if ((planes == 6) && (halfb == TRUE) && (numcolors == 16)
X          || (planes != 6) && (halfb == TRUE)){
X	  perror("You specified Half-Bright, but file is not in the correct format\n");
X	  exit(0);
X	}
X
X	if ((planes == 6) && (halfb != TRUE)) {
X	  fprintf(stderr, "Ham mode... raw color output initiated.\n");
X	  hammode = TRUE;
X	}
X
X	fread(chunk, 4, 1, inh);	/* length of body */
X
X	if (!hammode) {
X	  header.ras_magic= 0x59a66a95;
X	  if (pixelrep == FALSE)
X	    header.ras_width= width;
X	  else
X	    header.ras_width= width * 2;
X	  header.ras_height= hieght;
X	  header.ras_depth= 8;
X	  if (pixelrep == FALSE)
X	    header.ras_length= width * hieght;
X	  else
X	    header.ras_length= width * 2 * hieght;
X	  header.ras_type= RT_STANDARD;
X	  header.ras_maptype= RMT_EQUAL_RGB;
X	  header.ras_maplength= 3*256;
X
X/* Write out the rasterfile header to the ouput */
X
X	  fwrite(&header, sizeof(header), 1, outh);
X
X/* Write out the red colormap to the ouput */
X
X
X	  if (fwrite(rmap, sizeof(rmap), 1, outh) == 0) {
X	    perror(progname);
X	    exit(1);
X	  } 
X	  if (halfb == TRUE) 
X	    fwrite(rhmap, sizeof(rhmap), 1, outh);
X	  else 
X	    fwrite(rmap, sizeof(rmap), 1, outh);
X
X/* Write out the green colormap to the ouput */
X	
X	  if (fwrite(gmap, sizeof(gmap), 1, outh) == 0) {
X	    perror(progname);
X	    exit(1);
X	  } 
X	  if (halfb == TRUE) 
X	    fwrite(ghmap, sizeof(ghmap), 1, outh);
X	  else 
X	    fwrite(gmap, sizeof(gmap), 1, outh);
X
X/* Write out the blue colormap to the ouput */
X
X	  if (fwrite(bmap, sizeof(bmap), 1, outh) == 0) {
X	    perror(progname);
X	    exit(1);
X	  } 
X	  if (halfb == TRUE) 
X	    fwrite(bhmap, sizeof(bhmap), 1, outh);
X	  else 
X	    fwrite(bmap, sizeof(bmap), 1, outh);
X	}
X	else {
X	  twidth = width;
X	  if (pixelrep) {
X	    twidth = width * 2;
X	  }
X	  fwrite(&twidth, 4, 1, outh);
X	  fwrite(&hieght, 4, 1, outh);
X	}
X
X/* This part does all the work */
X
X/* Either we uncompress the file, or we read it in... */
X
X	if (compress == 1)
X	  uncompress(planes, width, inh);
X	else 
X	  for(k = 0;k<hieght;k++)
X	    for(i = 0;i<planes;i++) {
X	      for(j=0;j<width/8;j++){
X		cc(chunk);
X		if(feof(inh))
X		  continue;
X		fread(chunk, 1, 1, inh);
X		scanline[k][i][j] = *((unsigned char *)chunk);
X	      }
X	    }
X
X/* Here we take the data and output it (finally!) */
X
X	currentcolor[0] = rmap[0];
X	currentcolor[1] = gmap[0];
X	currentcolor[2] = bmap[0];
X
X	for(j = 0;j<hieght;j++){
X	  for(i = 0;i<width/8;i++)
X	    process_pixel(i,j,outh);
X	}
X	fclose(inh);
X	fclose(outh);
X} /* end of main */
X
X/* The uncompression routine 					*/
X/* Here we uncompress the data for the number of planes 	*/
X/* and store it in the global array scanline[][][] 		*/
X/* Credits: Leo (the great Caped one) was the originator of the */
X/* code I ripped this off from. 				*/
X
Xuncompress(planes, width, inh)
XFILE *inh;
Xlong planes, width;
X{
X	long i,j,count,bytesperrow;
X	char len;
X	unsigned char byte;
X
X	for(i=0;i<hieght;i++){
X		for(j=0;j<planes;j++){
X			count = 0;
X			bytesperrow = width / 8;
X			while(bytesperrow > 0){
X				if ((len = getc(inh)) >= 0){
X					bytesperrow -= ++len;
X					fread(&scanline[i][j][count],len,1,inh);
X					count += len;
X				} else if (len == -128)
X					;
X				else if (len < 0){
X					len = -len + 1;
X					bytesperrow -= len;
X					byte = getc(inh);
X					while(--len >= 0)
X						scanline[i][j][count++] = byte;
X				}
X			}
X			if(bytesperrow)
X				perror("Compression is messed.");
X		}
X		
X	}
X}
X
X
X/* RGBtoLuma converts the colortable from the Amiga to Luminance information */
X/* This routine was written by Mark Thompson 				     */
X
Xunsigned char RGBtoLuma(color)
Xunsigned char *color;
X{
X	unsigned char r, g, b;
X	unsigned char grey;
X	float fgrey;
X
X	r = (color[0] << 4) + color[0];
X	g = (color[1] << 4) + color[1];
X	b = (color[2] << 4) + color[2];
X	fgrey = 0.3 * r + 0.59 * g + 0.11 * b + 0.5;
X	grey = fgrey;
X	return grey;
X}
X
X/* Process the byte for the scanline 		*/
X/* Can you say shift 3 times fast? 		*/
X
Xprocess_pixel(byte,scan,outh)
XFILE *outh;
Xunsigned char byte;
Xlong scan;
X{
X	long j, i, p, result = 0;
X	unsigned char temp;
X
X	temp = 0; result = 0;
X	for(i=7;i>=0;i--){
X		for(p=0;p<planes;p++) 
X			result = ((((1 << i) & scanline[scan][p][byte])>>i)<<p) | result;
X		result = result & 0x3f;
X		temp = result;
X
X		if (hammode) {
X		  temp = (result & 0x30) >> 4;
X		  switch (temp){
X		  case 0x0:
X		    temp = result & 0x0f;
X		    currentcolor[2] = bmap[temp]<<4;
X		    currentcolor[1] = gmap[temp]<<4; 
X		    currentcolor[0] = rmap[temp]<<4;
X		    break;
X		  case 0x1:
X		    result = result & 0x0f;
X		    currentcolor[2] = result<<4;
X		    break;
X		  case 0x2:
X		    result = result & 0x0f;
X		    currentcolor[0] = result<<4;
X		    break;
X		  case 0x3:
X		    result = result & 0x0f;
X		    currentcolor[1] = result<<4;
X		    break;
X		  }
X
X		  fwrite(currentcolor, 1, 3, outh);
X		  if (pixelrep == TRUE)
X		    fwrite(currentcolor, 1, 3, outh);
X		}
X		else {
X		  fwrite(&temp, 1, 1, outh);
X		  if (pixelrep == TRUE)
X		    fwrite(&temp, 1, 1, outh);
X		}
X		result = 0;
X	}
X}
X
X/* look for a chunk in the file */
Xlong parse(inh, str)
XFILE *inh;
Xchar *str;
X{
X        static char chunk[5];
X        chunk[4] = 0;
X        while (1){
X	  fread(chunk, 2, 1, inh);
X	  if (strncmp(chunk,str,2) == 0){
X	    fread(chunk, 2, 1, inh);
X	    if (strncmp(chunk,&str[2],2) == 0)
X	      return FALSE;
X	  }
X	  if(feof(inh) != 0)
X	    return TRUE;
X        }
X}
X
X/* Clear Chunk variable */
X
Xcc(chunk)
Xchar chunk[5];
X{
X	register int i;
X	for(i=0;i<5;i++)
X		chunk[i] = 0;
X}
SHAR_EOF
echo "extracting convert.c"
sed 's/^X//' << \SHAR_EOF > convert.c
X
X/*
X * Apply median cut on an image.
X *
X * median [-c n] [-f] input output
X *     -c n		- set colortable size.  Default is 256.
X *     -f		- use Floyd-Steinberg dithering.
X *     -lzw		- compress output with LZW
X *     -none		- use no compression on output
X *     -picio		- use picio compression on output
X *     -packbits	- use packbits compression on output
X *     -rowsperstrip n	- create output with n rows/strip of data
X * (by default the compression scheme and rows/strip are taken
X *  from the input file)
X *
X * Notes:
X *
X * [1] Floyd-Steinberg dither:
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 * [2] Color Image Quantization for Frame Buffer Display, Paul Heckbert,
X *	Siggraph '82 proceedings, pp. 297-307
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include <rasterfile.h>
X
X#define	MAX_CMAP_SIZE	256
X#define	howmany(x, y)	(((x)+((y)-1))/(y))
X#define	streq(a,b)	(strcmp(a,b) == 0)
X
X#define	COLOR_DEPTH	8
X#define	MAX_COLOR	256
X
X#define	B_DEPTH		4		/* # 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#define	COLOR_SHIFT	(COLOR_DEPTH-B_DEPTH)
X#define COMPRESSION_NONE 0
X#define COMPRESSION_PACKBITS 1
X#define COMPRESSION_LZW 2
X#define COMPRESSION_PICIO 3
X
X
Xtypedef	struct colorbox {
X	struct	colorbox *next, *prev;
X	int	rmin, rmax;
X	int	gmin, gmax;
X	int	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
Xu_short	rm[MAX_CMAP_SIZE], gm[MAX_CMAP_SIZE], bm[MAX_CMAP_SIZE];
Xint	bytes_per_pixel;
Xlong	num_colors, width, hieght, length;
Xint	histogram[B_LEN][B_LEN][B_LEN];
XColorbox *freeboxes;
XColorbox *usedboxes;
XColorbox *largest_box();
XC_cell	**ColorCells;
XFILE	*in, *out;
X
Xchar	*usage = "usage: median [-c n] [-f] [-none] [-lzw] [-picio] [-packbits] [-rowsperstrip r] input output\n";
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	struct rasterfile header;
X	int i, j, dither = 0;
X	long chunk;
X	char *ifile_name = NULL, *ofile_name = NULL, color;
X	Colorbox *box_list, *ptr;
X	u_short rowsperstrip = 128, compression = -1, shortv;
X	u_short bitspersample, samplesperpixel, photometric;
X	float floatv;
X
X	num_colors = MAX_CMAP_SIZE;
X	for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
X		if (streq(argv[0], "-f")) {
X			dither = 1;
X			continue;
X		}
X		if (streq(argv[0], "-c")) {
X			argc--, argv++;
X			if (argc < 1) {
X				fprintf(stderr, "-c: missing colormap size\n%s",
X				    usage);
X				exit(1);
X			}
X			num_colors = atoi(argv[0]);
X			if (num_colors > MAX_CMAP_SIZE) {
X				fprintf(stderr,
X				   "-c: colormap too big, max %d\n%s",
X				   MAX_CMAP_SIZE, usage);
X				exit(1);
X			}
X			continue;
X		}
X		if (streq(argv[0], "-none")) {
X			compression = COMPRESSION_NONE;
X			continue;
X		}
X		if (streq(argv[0], "-packbits")) {
X			compression = COMPRESSION_PACKBITS;
X			continue;
X		}
X		if (streq(argv[0], "-lzw")) {
X			compression = COMPRESSION_LZW;
X			continue;
X		}
X		if (streq(argv[0], "-picio")) {
X			compression = COMPRESSION_PICIO;
X			continue;
X		}
X		if (streq(argv[0], "-rowsperstrip")) {
X			argc--, argv++;
X			rowsperstrip = atoi(argv[0]);
X			continue;
X		}
X		fprintf(stderr, "%s: unknown option\n%s", argv[0], usage);
X		exit(1);
X	}
X	if (argc < 2) {
X		fprintf(stderr, "%s", usage);
X		exit(-1);
X	}
X	in = fopen(argv[0], "r");
X	if (in == NULL)
X		exit(-1);
X
X	fread(&width, 4, 1, in); /* Get size of picture */
X	fread(&hieght, 4, 1, in);
X	length = hieght;
X
X	/*
X	 * STEP 1:  create empty boxes
X	 */
X	usedboxes = NULL;
X	box_list = freeboxes = (Colorbox *)malloc(num_colors*sizeof (Colorbox));
X	freeboxes[0].next = &freeboxes[1];
X	freeboxes[0].prev = NULL;
X	for (i = 1; i < num_colors-1; ++i) {
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	 * STEP 2: get histogram, initialize first box
X	 */
X	ptr = freeboxes;
X	freeboxes = ptr->next;
X	if (freeboxes)
X		freeboxes->prev = NULL;
X	ptr->next = usedboxes;
X	usedboxes = ptr;
X	if (ptr->next)
X		ptr->next->prev = ptr;
X	get_histogram(in, ptr);
X
X	/*
X	 * STEP 3: continually subdivide boxes until no more free
X	 * boxes remain or until all colors assigned.
X	 */
X	while (freeboxes != NULL) {
X		ptr = largest_box();
X		if (ptr != NULL)
X			splitbox(ptr);
X		else
X			freeboxes = NULL;
X	}
X
X	/*
X	 * STEP 4: assign colors to all boxes
X	 */
X	for (i = 0, ptr = usedboxes; ptr != NULL; ++i, ptr = ptr->next) {
X		rm[i] = ((ptr->rmin + ptr->rmax) << COLOR_SHIFT) / 2;
X		gm[i] = ((ptr->gmin + ptr->gmax) << COLOR_SHIFT) / 2;
X		bm[i] = ((ptr->bmin + ptr->bmax) << COLOR_SHIFT) / 2;
X	}
X
X	fprintf(stderr,"Number of colors used %d\n", i);
X
X	/* We're done with the boxes now */
X	free(box_list);
X	box_list = freeboxes = usedboxes = NULL;
X
X	/*
X	 * STEP 5: scan histogram and map all values to closest color
X	 */
X	/* 5a: create cell list as described in Heckbert[2] */
X	ColorCells = (C_cell **)calloc(C_LEN*C_LEN*C_LEN, sizeof (C_cell *));
X	/* 5b: create mapping from truncated pixel space to color
X	   table entries */
X	map_colortable();
X
X	/*
X	 * STEP 6: scan image, match input values to table entries
X	 */
X	out = fopen(argv[1], "w");
X	if (out == NULL)
X		exit(-2);
X
X	header.ras_magic= 0x59a66a95;
X	header.ras_width= width;
X  	header.ras_height= hieght;
X  	header.ras_depth= 8;
X	header.ras_length= width * hieght;
X  	header.ras_type= RT_STANDARD;
X  	header.ras_maptype= RMT_EQUAL_RGB;
X  	header.ras_maplength= 3*256;
X
X/* Write out the rasterfile header to the ouput */
X
X	fwrite(&header, sizeof(header), 1, out);
X
X/* Write the colormap to the ouput */
X
X	for(i=0;i<256;i++) {
X	  color = (char)rm[i];
X	  fwrite(&color, 1, 1, out);
X	}
X
X	for(i=0;i<256;i++) {
X	  color = (char)gm[i];
X	  fwrite(&color, 1, 1, out);
X	}
X
X	for(i=0;i<256;i++) {
X	  color = (char)bm[i];
X	  fwrite(&color, 1, 1, out);
X	}
X
X	rewind(in);		/* rewind input file XXX */
X
X	fread(&chunk, 4, 1, in);
X	fread(&chunk, 4, 1, in);
X	if (dither)
X		quant_fsdither(in, out);
X	else
X		quant(in, out);
X	(void) fclose(out);
X
X}
X
Xstatic
Xget_histogram(in, box)
X	FILE *in;
X	register Colorbox *box;
X{
X	register u_char *inptr;
X	register int red, green, blue, j, i;
X	u_char *inline;
X
X	i = 24 * width;
X	inline = (u_char *)malloc(howmany(i, 8));
X	if (inline == NULL) {
X		fprintf(stderr, "No space for scanline buffer\n");
X		exit(-1);
X	}
X	box->rmin = box->gmin = box->bmin = 999;
X	box->rmax = box->gmax = box->bmax = -1;
X	box->total = width * length;
X
X	{ register int *ptr = &histogram[0][0][0];
X	  for (i = B_LEN*B_LEN*B_LEN; --i >= 0;)
X		*ptr++ = 0;
X	}
X	for (i = length; i-- > 0;) {
X		if (fread(inline, width*3, 1, in) <= 0)
X			break;
X		inptr = inline;
X		for (j = width; j-- > 0;) {
X			red = *inptr++ >> COLOR_SHIFT;
X			green = *inptr++ >> COLOR_SHIFT;
X			blue = *inptr++ >> COLOR_SHIFT; 
X			if (red < box->rmin)
X				box->rmin = red;
X		        if (red > box->rmax)
X				box->rmax = red;
X		        if (green < box->gmin)
X				box->gmin = green;
X		        if (green > box->gmax)
X				box->gmax = green;
X		        if (blue < box->bmin)
X				box->bmin = blue;
X		        if (blue > box->bmax)
X				box->bmax = blue;
X		        histogram[red][green][blue]++;
X		}
X	}
X	free(inline);
X}
X
Xstatic Colorbox *
Xlargest_box()
X{
X	register Colorbox *p, *b;
X	register int size;
X
X	b = NULL;
X	size = -1;
X	for (p = usedboxes; p != NULL; p = p->next)
X		if ((p->rmax > p->rmin || p->gmax > p->gmin ||
X		    p->bmax > p->bmin) &&  p->total > size)
X		        size = (b = p)->total;
X	return (b);
X}
X
Xstatic
Xsplitbox(ptr)
X	register Colorbox *ptr;
X{
X	int		hist2[B_LEN];
X	int		first, last;
X	register Colorbox	*new;
X	register int	*iptr, *histp;
X	register int	i, j;
X	register int	ir,ig,ib;
X	register int sum, sum1, sum2;
X	int		total;
X	enum { RED, GREEN, BLUE } axis;
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	i = ptr->rmax - ptr->rmin;
X	if (i >= ptr->gmax - ptr->gmin  && i >= ptr->bmax - ptr->bmin)
X		axis = RED;
X	else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin)
X		axis = GREEN;
X	else
X		axis = BLUE;
X	/* get histogram along longest axis */
X	switch (axis) {
X	case RED:
X		histp = &hist2[ptr->rmin];
X	        for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
X			*histp = 0;
X			for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
X				iptr = &histogram[ir][ig][ptr->bmin];
X				for (ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X					*histp += *iptr++;
X			}
X			histp++;
X	        }
X	        first = ptr->rmin;
X		last = ptr->rmax;
X	        break;
X	case GREEN:
X	        histp = &hist2[ptr->gmin];
X	        for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
X			*histp = 0;
X			for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
X				iptr = &histogram[ir][ig][ptr->bmin];
X				for (ib = ptr->bmin; ib <= ptr->bmax; ++ib)
X					*histp += *iptr++;
X			}
X			histp++;
X	        }
X	        first = ptr->gmin;
X		last = ptr->gmax;
X	        break;
X	case BLUE:
X	        histp = &hist2[ptr->bmin];
X	        for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) {
X			*histp = 0;
X			for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
X				iptr = &histogram[ir][ptr->gmin][ib];
X				for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
X					*histp += *iptr;
X					iptr += B_LEN;
X				}
X			}
X			histp++;
X	        }
X	        first = ptr->bmin;
X		last = ptr->bmax;
X	        break;
X	}
X	/* find median point */
X	histp = &hist2[first];
X	sum2 = ptr->total / 2;
X	histp = &hist2[first];
X	sum = 0;
X	for (i = first; i <= last && (sum += *histp++) < sum2; ++i)
X		;
X	if (i == first)
X		i++;
X
X	/* Create new box, re-allocate points */
X	new = freeboxes;
X	freeboxes = new->next;
X	if (freeboxes)
X		freeboxes->prev = NULL;
X	if (usedboxes)
X		usedboxes->prev = new;
X	new->next = usedboxes;
X	usedboxes = new;
X
X	total = ptr->total;
X	histp = &hist2[first];
X	for (sum1 = 0, j = first; j < i; j++)
X		sum1 += *histp++;
X	for (sum2 = 0, j = i; j <= last; j++)
X	    sum2 += *histp++;
X	new->total = sum1;
X	ptr->total = sum2;
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 (axis) {
X	case RED:
X		new->rmax = i-1;
X	        ptr->rmin = i;
X	        break;
X	case GREEN:
X	        new->gmax = i-1;
X	        ptr->gmin = i;
X	        break;
X	case BLUE:
X	        new->bmax = i-1;
X	        ptr->bmin = i;
X	        break;
X	}
X	shrinkbox(new);
X	shrinkbox(ptr);
X}
X
Xstatic
Xshrinkbox(box)
X	register Colorbox *box;
X{
X	register int *histp, ir, ig, ib;
X
X	if (box->rmax > box->rmin) {
X		for (ir = box->rmin; ir <= box->rmax; ++ir)
X			for (ig = box->gmin; ig <= box->gmax; ++ig) {
X				histp = &histogram[ir][ig][box->bmin];
X			        for (ib = box->bmin; ib <= box->bmax; ++ib)
X					if (*histp++ != 0) {
X						box->rmin = ir;
X						goto have_rmin;
X					}
X			}
X	have_rmin:
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					histp = &histogram[ir][ig][box->bmin];
X					ib = box->bmin;
X					for (; ib <= box->bmax; ++ib)
X						if (*histp++ != 0) {
X							box->rmax = ir;
X							goto have_rmax;
X						}
X			        }
X	}
Xhave_rmax:
X	if (box->gmax > box->gmin) {
X		for (ig = box->gmin; ig <= box->gmax; ++ig)
X			for (ir = box->rmin; ir <= box->rmax; ++ir) {
X				histp = &histogram[ir][ig][box->bmin];
X			        for (ib = box->bmin; ib <= box->bmax; ++ib)
X				if (*histp++ != 0) {
X					box->gmin = ig;
X					goto have_gmin;
X				}
X			}
X	have_gmin:
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					histp = &histogram[ir][ig][box->bmin];
X					ib = box->bmin;
X					for (; ib <= box->bmax; ++ib)
X						if (*histp++ != 0) {
X							box->gmax = ig;
X							goto have_gmax;
X						}
X			        }
X	}
Xhave_gmax:
X	if (box->bmax > box->bmin) {
X		for (ib = box->bmin; ib <= box->bmax; ++ib)
X			for (ir = box->rmin; ir <= box->rmax; ++ir) {
X				histp = &histogram[ir][box->gmin][ib];
X			        for (ig = box->gmin; ig <= box->gmax; ++ig) {
X					if (*histp != 0) {
X						box->bmin = ib;
X						goto have_bmin;
X					}
X					histp += B_LEN;
X			        }
X		        }
X	have_bmin:
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					histp = &histogram[ir][box->gmin][ib];
X					ig = box->gmin;
X					for (; ig <= box->gmax; ++ig) {
X						if (*histp != 0) {
X							box->bmax = ib;
X							goto have_bmax;
X						}
X						histp += B_LEN;
X					}
X			        }
X	}
Xhave_bmax:
X	;
X}
X
Xstatic C_cell *
Xcreate_colorcell(red, green, blue)
X	int red, green, blue;
X{
X	register int ir, ig, ib, i;
X	register C_cell *ptr;
X	int mindist, next_n;
X	register int tmp, dist, n;
X
X	ir = red >> (COLOR_DEPTH-C_DEPTH);
X	ig = green >> (COLOR_DEPTH-C_DEPTH);
X	ib = blue >> (COLOR_DEPTH-C_DEPTH);
X	ptr = (C_cell *)malloc(sizeof (C_cell));
X	*(ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib) = ptr;
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 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			continue;
X		ptr->entries[ptr->num_ents][0] = i;
X		ptr->entries[ptr->num_ents][1] = 0;
X		++ptr->num_ents;
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	        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	        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	        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		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			continue;
X		dist = 0;
X	        if ((tmp = red - rm[i]) > 0 ||
X		    (tmp = rm[i] - (red + MAX_COLOR/C_LEN-1)) > 0 )
X			dist += tmp*tmp;
X	        if ((tmp = green - gm[i]) > 0 ||
X		    (tmp = gm[i] - (green + MAX_COLOR/C_LEN-1)) > 0 )
X			dist += tmp*tmp;
X	        if ((tmp = blue - bm[i]) > 0 ||
X		    (tmp = bm[i] - (blue + MAX_COLOR/C_LEN-1)) > 0 )
X			dist += tmp*tmp;
X	        if (dist < mindist) {
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	for (n = ptr->num_ents - 1; n > 0; n = next_n) {
X		next_n = 0;
X		for (i = 0; i < n; ++i)
X			if (ptr->entries[i][1] > ptr->entries[i+1][1]) {
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	return (ptr);
X}
X
Xstatic
Xmap_colortable()
X{
X	register int *histp = &histogram[0][0][0];
X	register C_cell *cell;
X	register int j, tmp, d2, dist;
X	int ir, ig, ib, i;
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, histp++) {
X				if (*histp == 0) {
X					*histp = -1;
X					continue;
X				}
X				cell = *(ColorCells +
X				    (((ir>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) +
X				    ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH) +
X				    (ib>>(B_DEPTH-C_DEPTH))));
X				if (cell == NULL )
X					cell = create_colorcell(
X					    ir << COLOR_SHIFT,
X					    ig << COLOR_SHIFT,
X					    ib << COLOR_SHIFT);
X				dist = 9999999;
X				for (i = 0; i < cell->num_ents &&
X				    dist > cell->entries[i][1]; ++i) {
X					j = cell->entries[i][0];
X					d2 = rm[j] - (ir << COLOR_SHIFT);
X					d2 *= d2;
X					tmp = gm[j] - (ig << COLOR_SHIFT);
X					d2 += tmp*tmp;
X					tmp = bm[j] - (ib << COLOR_SHIFT);
X					d2 += tmp*tmp;
X					if (d2 < dist) {
X						dist = d2;
X						*histp = j;
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 */
Xstatic
Xquant(in, out)
X	FILE *in, *out;
X{
X	u_char	*outline, *inline;
X	register u_char	*outptr, *inptr;
X	register int i, j, red, green, blue;
X
X	i = 24 * width;
X	inline = (u_char *)malloc(howmany(i, 8));
X	outline = (u_char *)malloc(width);
X	for (i = 0; i < length; i++) {
X		if (fread(inline, 3*width, 1, in) <= 0)
X			break;
X		inptr = inline;
X		outptr = outline;
X		for (j = 0; j < width; j++) {
X			red = *inptr++ >> COLOR_SHIFT;
X			green = *inptr++ >> COLOR_SHIFT;
X			blue = *inptr++ >> COLOR_SHIFT;
X			*outptr++ = histogram[red][green][blue];
X		}
X		if (fwrite(outline, width, 1, out) <= 0)
X			break;
X	}
X	free(inline);
X	free(outline);
X}
X
Xstatic
Xquant_fsdither(in, out)
X	FILE *in, *out;
X{
X	u_char *outline, *inline, *inptr;
X	short *thisline, *nextline, *tmpptr;
X	register u_char	*outptr;
X	register short *thisptr, *nextptr;
X	register int i, j, tmp;
X	int imax, jmax, lastline, lastpixel;
X
X	imax = length - 1;
X	jmax = width - 1;
X	i = 12 * width;
X	inline = (u_char *)malloc(howmany(i, 8));
X	thisline = (short *)malloc(width * 3 * sizeof (short));
X	nextline = (short *)malloc(width * 3 * sizeof (short));
X	outline = (unsigned char *) malloc(width);
X
X	/*
X	 * Get first line
X	 */
X	if (fread(inline, 3*width, 1, in) <= 0)
X		return;
X	inptr = inline;
X	nextptr = nextline;
X	for (j = 0; j < width; ++j) {
X		*nextptr++ = *inptr++;
X		*nextptr++ = *inptr++;
X		*nextptr++ = *inptr++;
X	}
X	for (i = 0; i < length; ++i) {
X		tmpptr = thisline;
X		thisline = nextline;
X		nextline = tmpptr;
X		lastline = (i == imax);
X		if (fread(inline, 3*width, 1, in) <= 0)
X			break;
X		inptr = inline;
X		nextptr = nextline;
X		for (j = 0; j < width; ++j) {
X			*nextptr++ = *inptr++;
X			*nextptr++ = *inptr++;
X			*nextptr++ = *inptr++;
X		}
X		thisptr = thisline;
X		nextptr = nextline;
X		outptr = outline;
X		for (j = 0; j < width; ++j) {
X			int red, green, blue;
X			register int oval, r2, g2, b2;
X
X			lastpixel = (j == jmax);
X			b2 = *thisptr++;
X			g2 = *thisptr++;
X			r2 = *thisptr++;
X			if (r2 < 0)
X				r2 = 0;
X			else if (r2 >= MAX_COLOR)
X				r2 = MAX_COLOR-1;
X			if (g2 < 0)
X				g2 = 0;
X			else if (g2 >= MAX_COLOR)
X				g2 = MAX_COLOR-1;
X			if (b2 < 0)
X				b2 = 0;
X			else if (b2 >= MAX_COLOR)
X				b2 = MAX_COLOR-1;
X			red = r2;
X			green = g2;
X			blue = b2;
X			r2 >>= COLOR_SHIFT;
X			g2 >>= COLOR_SHIFT;
X			b2 >>= COLOR_SHIFT;
X			oval = histogram[r2][g2][b2];
X			if (oval == -1) {
X				int ci;
X				register int cj, tmp, d2, dist;
X				register C_cell	*cell;
X
X				cell = *(ColorCells +
X				    (((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) +
X				    ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH ) +
X				    (b2>>(B_DEPTH-C_DEPTH))));
X				if (cell == NULL )
X					cell = create_colorcell(red,
X					    green, blue);
X				dist = 9999999;
X				for (ci = 0; ci < cell->num_ents && dist > cell->entries[ci][1]; ++ci) {
X					cj = cell->entries[ci][0];
X					d2 = (rm[cj] >> COLOR_SHIFT) - r2;
X					d2 *= d2;
X					tmp = (gm[cj] >> COLOR_SHIFT) - g2;
X					d2 += tmp*tmp;
X					tmp = (bm[cj] >> COLOR_SHIFT) - b2;
X					d2 += tmp*tmp;
X					if (d2 < dist) {
X						dist = d2;
X						oval = cj;
X					}
X				}
X				histogram[r2][g2][b2] = oval;
X			}
X			*outptr++ = oval;
X			red -= rm[oval];
X			green -= gm[oval];
X			blue -= bm[oval];
X			if (!lastpixel) {
X				thisptr[0] += blue * 7 / 16;
X				thisptr[1] += green * 7 / 16;
X				thisptr[2] += red * 7 / 16;
X			}
X			if (!lastline) {
X				if (j != 0) {
X					nextptr[-3] += blue * 3 / 16;
X					nextptr[-2] += green * 3 / 16;
X					nextptr[-1] += red * 3 / 16;
X				}
X				nextptr[0] += blue * 5 / 16;
X				nextptr[1] += green * 5 / 16;
X				nextptr[2] += red * 5 / 16;
X				if (!lastpixel) {
X					nextptr[3] += blue / 16;
X				        nextptr[4] += green / 16;
X				        nextptr[5] += red / 16;
X				}
X				nextptr += 3;
X			}
X		}
X		if (fwrite(outline, width, 1, out) < 0)
X			break;
X	}
X	free(inline);
X	free(thisline);
X	free(nextline);
X	free(outline);
X}
SHAR_EOF
echo "extracting hamtosun"
sed 's/^X//' << \SHAR_EOF > hamtosun
X#! /bin/csh
X
Xiff2sun -cp $1 $1.out
Xconvert $1.out $1.pic
Xrasfilter8to1 -d $1.pic | screenload -g
X/bin/rm $1.out
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit