/*
*	options.c
*
*    This set of routines takes care of the image and palette manipulations
*  after an image has been read in and displayed.
*
*  National Center for Supercomputing Applications, University of Illinois
*  153 Computing Applications Building
*  605 E. Springfield Ave.
*  Champaign, IL  61820     (217)244-0072
*
*  Quincey Koziol			August 1988
*
*/

#include "showext.h"

/**********************************************************************
*  Function	:	draw_mouse()
*  Purpose	:	draw the mouse cursor on the screen
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	mousecml(), makecur9()
*  Called by	:	mousefunc(), parse(), window(),
**********************************************************************/
void draw_mouse()
{
	if(mode==NO9)
		makecur9(mx,my);
	else {
		m1=1;
		mousecl(&m1,&m2,&m3,&m4);
	  }	/* end else */
}	/* end draw_mouse() */

/**********************************************************************
*  Function	:	erase_mouse()
*  Purpose	:	erase the mouse cursor from the screen
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	mousecml(), makecur9()
*  Called by	:	mousefunc(), parse(), window(),
**********************************************************************/
void erase_mouse()
{
	if(mode==NO9)
		makecur9(mx,my);
	else {
		m1=2;
		mousecl(&m1,&m2,&m3,&m4);
	  }	/* end else */
}	/* end erase_mouse() */

/**********************************************************************
*  Function	:	read_mouse()
*  Purpose	:	read the mouse position and button status and scale for correct
*		display mode
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	mousecml()
*  Called by	:	mousefunc(), parse(), window(),
**********************************************************************/
void read_mouse()
{
	m1=3;		/* get mouse position and button status */
	mousecl(&m1,&m2,&m3,&m4);
	leftbutton=rightbutton=0;
	leftbutton=m2&1;		/* get left button bit from mouse variable */
	rightbutton=(m2&2)>>1;		/* get right button bit from mouse variable */
	if(mode!=EGA)
		m3=m3>>1;				/* compensate for the mouse */
	if(mode==NO9) {			/* if no9 then compensate the mouse more */
		m3=m3>>2;
		m4=m4>>3;
	  }	/* end if */
}	/* end read_mouse()*/

/**********************************************************************
*  Function	:	readstr
*  Purpose	:	get a simple set of characters from stdin
*  Parameters	:
*			charstr -	pointer to the character string to be read in
*  Returns	:	none
*  Calls	:	getch()
*  Called by	:	parse()
**********************************************************************/
void readstr(charstr)
unsigned char *charstr;
{
	charstr--;
	do {
		charstr++;
		*charstr=getch();
		if(*charstr==0)
			*charstr=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
		printf("%c",*charstr);
	  }	while(*charstr!=13);		/* end while */
	*charstr=0;
}

/**********************************************************************
*  Function	:	swapint
*  Purpose	:	swap two integer values
*  Parameters	:
*			x,y - pointers to the two values to swap
*  Returns	:	none
*  Calls	:	none
*  Called by	:	mousefunc()
**********************************************************************/
void swapint(x,y)
int	*x,*y;		/* values to exchange */
{
	int	t;		/* temporary variable */

	t=*x;
	*x=*y;
	*y=t;
} /* end swapint() */

/**********************************************************************
*  Function	:	swap
*  Purpose	:	swap two character values
*  Parameters	:
*			x,y - pointers to the two values to swap
*  Returns	:	none
*  Calls	:	none
*  Called by	:	options()
**********************************************************************/
void swap(x,y)
unsigned char *x,*y;		/* values to exchange */
{
	unsigned char t;		/* temporary variable */

	t=*x;
	*x=*y;
	*y=t;
} /* end swap() */

/**********************************************************************
*  Function	:	findpal
*  Purpose	:	loads in a new palette from an HDF file after the first one
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	DFfind(), DFgetelement(), palloc(), palloce(), select_pal()
*  Called by	:	options()
**********************************************************************/
void findpal()
{
	int i,	/* local counting variable */
		palold;		/* last palmax value */
	char *pi; /* pointer to character array of palette read in */

	DFsetfind(dff,DFTG_IP8,DFREF_WILDCARD);	/* set the HDF file to a palette */

	palold=palmax;
	if (!DFfind(dff,&ddstr)) {	/* HDF file should be set for palettes */
		if(DFgetelement(dff,ddstr.tag,ddstr.ref,pal)>=0) {
			pi = pal;
			for (i=0; i<256; i++) {
				rmap[i] = *pi++;
				gmap[i] = *pi++;
				bmap[i] = *pi++;
			  } /* end for */
			palmax++;
		  }	/* end if */
	} /* end if */

/* see if there is enough space for another palette */

	if(palmax!=palold) {
		if(mode!=EGA) {
			if(!palloc(palmax)) {	/* if no space available then go back to last one */
				palmax--;
				displayerr("No Room For Another Palette");
				memcpy(rmap,rpal[palnum],256);
				memcpy(gmap,gpal[palnum],256);
				memcpy(bmap,bpal[palnum],256);
			  } /* end if */
			else				/* palloc() was ok, so switch to the palette */
				palnum=palmax;
		  } /* end if */
		else {
			if(!palloce(palmax)) {	/* no space, so switch to old palette */
				displayerr("No Room For Another Palette");
				palmax--;
				memcpy(regrs,egapals[palnum],16);
			  } /* end if */
			else {			/* enough space for a palette, so use it */
				palnum=palmax;
				select_pal(xdim,ydim,1);	/* do a frequency count and set up the ega palette */
			  } /* end else */
		  }	/* end else */
	  }	/* end if */
} /* end findpal() */

/**********************************************************************
*  Function	:	showpal
*  Purpose	:	display the current palette on the screen.  EGA monitors
*			directly change to data in memory, whereas VGA and NO9 monitors
*			just change the data on the screen.
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	showpalv(), showpal9(), egaline()
*  Called by	:	options()
**********************************************************************/
void showpal()
{
#ifdef MOUSE
	if(mouse)
		erase_mouse();
#endif
	switch (mode) {		/* different methods for showing the palette on the screen */
		case VGA:
			showpalv(&palstore,pal_xoff,pal_yoff);
			break;

		case NO9:
			showpal9(&palstore,pal_xoff,pal_yoff);
			break;

		case EGA:
		default:
			for(i=0; i<16; i++)
				for(k=0; k<16; k++)
					palstore[0][i*16+k]=i;
			for(j=0; j<8; j++)		/* for no. 9 and vga, direct memory copies are used in assembly, but ega must be done 'by hand' */
				egaline(xwhere+pal_xoff,ywhere+j+pal_yoff,&palstore[0][0],256,trans1,0);
			break;
	  }	/* end switch(mode) */
#ifdef MOUSE
	if(mouse)
		draw_mouse();
#endif
} /* end showpal() */

/**********************************************************************
*  Function	:	nopal
*  Purpose	:	replace the palette on the screen with the image under it.
*			Once again, EGA mode changes the data in memory, whereas VGA
*			NO9 change it only on the screen.
*  Parameters	:
*		xoff,yoff - the x & y offsets into the image where the screen is
*			positioned
*  Returns	:	none
*  Calls	:	nopalv(), nopal9(), egaline()
*  Called by	:	options()
**********************************************************************/
void nopal(xoff,yoff)
int xoff,yoff;	/* the x & y offset into the image being displayed */
{
#ifdef MOUSE
	if(mouse)
		erase_mouse();
#endif
	switch(mode){		/* different routines to restore the screen */
		case VGA:
			nopalv(&palstore,pal_xoff,pal_yoff);
			break;

		case NO9:
			nopal9(&palstore,pal_xoff,pal_yoff);
			break;

		case EGA:
		default:
			if(pal_yoff<(ydim-pal_height))
				for(j=0; j<8; j++)		/* just re plot that section of the screen */
					egaline(xwhere+pal_xoff,ywhere+j+pal_yoff,store[j+pal_yoff+yoff],min(256,xdim-pal_xoff),trans,xoff+pal_xoff);
			break;
	  }	/* end switch(mode) */
#ifdef MOUSE
	if(mouse)
		draw_mouse();
#endif
} /* end nopal() */
		
/**********************************************************************
*  Function	:	waitq
*  Purpose	:	wait for a keypress
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	none
*  Called by	:	many places...
**********************************************************************/
void waitq()
{
	unsigned char c;	/* temporary variable to hold the character read in */

	c='!';
	while (c=='!') {
		if(kbhit()) {	/* get the next command */
			c=getch();
			if(c==0)
				c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
		  }	/* end if */
 	  }	/* end while */
}	/* waitq() */

/**********************************************************************
*  Function	:	printdir
*  Purpose	:	print out a listing of files ending in '.pal' in the 
*			current local directory
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	textmode()
*  Called by	:	options()
**********************************************************************/
void printdir()
{
	unsigned char	palnames[4096];		/* array to store palette names */
	char *p,				/* pointer to position if names file */
		*fnp="*.pal";		/* pattern to look for */
	int	n,					/* number of matching file names */
		attr=0;				/* look for normal files */
	
	if((n=getfnl(fnp,palnames,sizeof(palnames),attr))<=0)	/* load the names of the palettes into an array */
		printf("No Palettes In The Current Directory\n");
	else
		for(p=palnames; *p!='\0'; p+=strlen(p)+1)	/* write out all the names */
			printf("%s\n",p);
	printf("\nHit Any Key To Continue");
	waitq();
	textmode();
} /* end printdir() */

#ifdef QAK
/**********************************************************************
*  Function :	slidepal
*  Purpose	:	rotate the palette an arbitary amount
*  Parameters	:
*		offset - the amount to slide the palette
*  Returns	:	none
*  Calls	:	none
*  Called by	:	options()
**********************************************************************/
void slidepal(x)
int	x;	/* amount of positive or negative offset to slide the palette */
{
	if(mode!=EGA){
		if(x<0){
			memcpy(rmap,rpal[palnum]-x,256+x);		/* shift palettes down x bytes */
			memcpy(gmap,gpal[palnum]-x,256+x);
			memcpy(bmap,bpal[palnum]-x,256+x);
			for(j=255+x; j<=255; j++){	/* fill in the leftover places */
				rmap[j]=rmap[j-1];
				gmap[j]=gmap[j-1];
				bmap[j]=bmap[j-1];
			  }	/* end for */
		  }	/* end if */
		else{
			memcpy(rmap+x,rpal[palnum],256-x);		/* shift palettes up x bytes */
			memcpy(gmap+x,gpal[palnum],256-x);
			memcpy(bmap+x,bpal[palnum],256-x);
			for(j=x; j>=0; j--){		/* fill in the leftover places */
				rmap[j]=rmap[j+1];
				gmap[j]=gmap[j+1];
				bmap[j]=bmap[j+1];
			  }	/* end for */
		  } /* end else */
	  }	/* end if */
} /* end slidepal() */
#endif

/**********************************************************************
*  Function :	squishpal
*  Purpose	:	compress the palette an arbitrary amount
*  Parameters	:
*		slope  - the slope of the line to compress palette with
*		offset - the amount to slide the palette
*  Returns	:	none
*  Calls	:	abs()
*  Called by	:	options()
**********************************************************************/
void squishpal(x,y)
int	x,		/* the slope of the line to squish the palette with */
	y;		/* the offset to slide the palette by when the squishing is done */
{
	int		j,k,	/* temporary counting variables */
			lox,	/* the value of the x-intercept if y=0 */
			hix;	/* the value of the x-intercept if y=255 */

	if(x!=0){
		lox=(10*((-127)-y)/x)+127;
		hix=(10*(128-y)/x)+127;
	  }	/* end if */
	else{
		lox=127;
		hix=127;
	  }	/* end else */
	if(lox!=0)		/* check for the beginning value not on the beginning of the palette */
		if(lox<0)
			lox=0;	/* if i=it is before the palette then set it to beginning of the palette */
		else{
			for(j=lox-1; j>=0; j--){	/* if it is after the beginning of the palette then fill to beginning */
				rmap[j]=rpal[palnum][lox-1];
				gmap[j]=gpal[palnum][lox-1];
				bmap[j]=bpal[palnum][lox-1];
			  }	/* end for j */
		  }	/* end else */
	if(hix!=255)	/* check for end value not exactly on the end of the palette */
		if(hix>255)	/* if it is after the end of the palette then set it to the end of the palette */
			hix=255;
		else{
			for(j=hix+1; j<=255; j++){	/* if it is before the end of the palette then fill to the end */
				rmap[j]=rpal[palnum][hix+1];
				gmap[j]=gpal[palnum][hix+1];
				bmap[j]=bpal[palnum][hix+1];
			  }	/* end for j */
		  }	/* end else */
	if(hix<lox)		/* make sure it increments in correct fashion */
		swap(&lox,&hix);
	for(j=lox; j<=hix; j++){
		k=x*(j-127)/10+127+y;	/* y = mx+b equation of a line */
		rmap[j]=rpal[palnum][k];
		gmap[j]=gpal[palnum][k];
		bmap[j]=bpal[palnum][k];
	  }	/* end for j */
} /* end squishpal() */

#ifdef QAK
/**********************************************************************
*  Function	:	pixelcolor
*  Purpose	:	returns the color of a specified pixel
*  Parameters	:
*			x - the x coor. of the pixel
*			y - the y coor. of the pixel
*  Returns	:	the color of the pixel as a char
*  Calls	:	none
*  Called by	:	mousefunc()
**********************************************************************/
unsigned char pixelcolor(x,y)
int	x,y;	/* the x and y coor. of the pixel whose color is returned */
{
	if(show && ((x>=pal_xoff && x<=(pal_xoff+255)) && (y>=pal_yoff && y<=(pal_yoff+pal_height))))
		switch(mode) {	/* return different values for different screen types */
			case VGA:
			case NO9:
				return((unsigned char) (x-pal_xoff));
				break;

			case EGA:
			default:
				return((unsigned char) ((x-pal_xoff)/16));
				break;
		  }	/* end switch */
	else	/* if not in the color bar then return the normal value */
		return((unsigned char) store[y+yoff][x+xoff]);
}	/* end pixelcolor() */
#endif

/**********************************************************************
*  Function	:	updatescrn
*  Purpose	:	updates the screen with the current image, color bar,
*					and the mouse cursor
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	showpic(), showpal(), pixelcolor()
*  Called by	:	options()
**********************************************************************/
void updatescrn()
{
	showpic();
	if(show)
		showpal();
#ifdef MOUSE
	if(mouse)			/* if the mouse is on then replace the mouse cursor */
		draw_mouse();
#endif
}	/* end updatescrn() */

/**********************************************************************
*  Function	:	scroll
*  Purpose	:	move the image around the screen
*  Parameters	:
*			func -	the scrolling function to be performed
*  Returns	:	none
*  Calls	:	updatescrn()
*  Called by	:	options(), mousefunc()
**********************************************************************/
void scroll(func)
unsigned char	func;	/* the function to be performed on the screen */
{
	int	i;				/* temporary variables to hold the old xoff and yoff */

#ifdef MOUSE
	if(mouse)			/* if the mouse is on then hide the cursor while moving */
		erase_mouse();
#endif
	switch(func) {	/* switch to perform the appropriate scroll */
		case 'l':	/* scroll left */
			i=xoff;
			xoff-=10;
			if(xoff<0) 
				xoff=0;
			if(xoff!=i)
				updatescrn();
			break;
		
		case 'r':	/* scroll right */
			i=xoff;
			xoff+=10;
			if(xdim-maxx<0)
				xoff=0;
			else
				if(xoff>xdim-maxx)
					xoff=xdim-maxx;
			if(i!=xoff)
				updatescrn();
			break;

		case 'u':	/* scroll up */
			i=yoff;
			yoff-=10;
			if(yoff<0)
				yoff=0;
			if(yoff!=i)
				updatescrn();
			break;

		case 'd':	/* scroll down */
			i=yoff;
			yoff+=10;
			if(ydim-maxy<0)
				yoff=0;
			else
				if(yoff>ydim-maxy)
					yoff=ydim-maxy;
			if(i!=yoff)
				updatescrn();
			break;

		case 'H':	/* Move to upper left hand corner of image */
			if(yoff!=0||xoff!=0) {
				xoff=0;
				yoff=0;
				updatescrn();
			  } /* end if */
			break;

		case 'E':	/* Move to lower right hand corner of image */
			if(yoff!=ydim-maxy||xoff!=xdim-maxx) {
				xoff=xdim-maxx;
				if(xoff<0)
					xoff=0;
				yoff=ydim-maxy;
				if(yoff<0)
					yoff=0;
				updatescrn();
			  } /* end if */
			break;

		case 'U':	/* page image up */
			i=yoff;
			yoff-=maxy;
			if(yoff<0)
				yoff=0;
			if(yoff!=i)
				updatescrn();
			break;

		case 'D':	/* page image down */
			i=yoff;
			yoff+=maxy;
			if(ydim-maxy<0)
				yoff=0;
			else
				if(yoff>ydim-maxy)
					yoff=ydim-maxy;
			if(i!=yoff)
				updatescrn();
			break;
	  }	/* end switch */
#ifdef MOUSE
	if(mouse)			/* if the mouse is on then replace the cursor after moving */
		draw_mouse();
#endif
}	/* end scroll() */

/**********************************************************************
*  Function	:	rotate
*  Purpose	:	rotate the palette left or right
*  Parameters	:
*			func -	character to determine which direction to rotate
*  Returns	:	none
*  Calls	:	putpal(), memcpy()
*  Called by	:	options(), mousefunc()
**********************************************************************/
void rotate(func)
unsigned char	func;	/* parameter to determine which way to rotate the palette */
{
	switch(func) {		/* switch for what to do to the palette */
		case 'l':		/* rotate the palette left */
			if(mode!=EGA) {
				i=rmap[255];
				j=gmap[255];
				k=bmap[255];
				memcpy(rmap+1,rmap,255);		/* shift palettes up one byte */
				memcpy(gmap+1,gmap,255);
				memcpy(bmap+1,bmap,255);
				rmap[0]=i;
				gmap[0]=j;
				bmap[0]=k;
			  } /* end if */
			else {
				i=regrs[15];
				for(j=15; j>=1; j--)
					regrs[j]=regrs[j-1];
				regrs[0]=i;
			  } /* end else */
			putpal();
			break;

		case 'r':		/* rotate the palette right */
			if(mode!=EGA) {
				i=rmap[0];
				j=gmap[0];
				k=bmap[0];
				memcpy(rmap,rmap+1,255);		/* shift palettes down one byte */
				memcpy(gmap,gmap+1,255);
				memcpy(bmap,bmap+1,255);
				rmap[255]=i;
				gmap[255]=j;
				bmap[255]=k;
			  } /*end if */
			else {
				i=regrs[0];
				for(j=0; j<=14; j++)
					regrs[j]=regrs[j+1];
				regrs[15]=i;
			  } /* end else */
			putpal();
			break;
	  }	/* end switch */
}	/* end rotate() */

/**********************************************************************
*  Function	:	expand
*  Purpose	:	routine to blow up a section of the screen
*  Parameters	:
*			x1,y1 -	location of the upper left hand corner of the box to blow up
*			x2,y2 -	location of the lower right hand corner of the box to blow up
*  Returns	:	
*			1 - if routine was successfully finished
*			0 - if routine was interupted
*  Calls	:	vgaline1()
*  Called by	:	mousefunc()
**********************************************************************/
int expand(x1,y1,x2,y2)
int	x1,y1,					/* location of the upper left hand corner of the box to blow up */
	x2,y2;					/* location of the lower right hand corner of the box to blow up */

{
	unsigned char line[640];	/* a line to blow a line of the box up into */
	float	xdiff,ydiff,			/* the differences between the x and y corners */
			maxxf,maxyf;			/* a float variable for maxx and maxy */
	int		i,j,k,l,m;

	xdiff=x2-x1;					/* find difference between endpoints */
	ydiff=y2-y1;
	maxxf=maxx;
	maxyf=maxy;
	l=-1;							/* preset the line counter to calculate the first line */
	for(i=0; i<maxy; i++) {		/* go through all the lines in the image */
		k=(int)((i/maxyf)*ydiff)+y1;
		if(k!=l) {				/* if this line is not the same as the previous then re-calculate it */
			for(j=0; j<maxx; j++)		/* go through each line and magnify */
				line[j]=store[k][(int)((j/maxxf)*xdiff)+x1];
			l=k;						/* set the line */
		  }	/* end if */
		switch(mode) {		/* use different line drawing routines for different screens */
			case VGA:
				vgaline1(0,i,line,0,maxx);		/* send each line out to the screen */
				break;

			case NO9:
				no9line(0,i,line,0,maxx);
				break;

			case EGA:
			default:
				egaline(0,i,line,maxx,trans,0);
				break;
		 }	/* end switch */
		if(kbhit())	{			/* break out if the keyboard is hit */
			m=getch();			/* clear the keyboard buffer */
			if(m==0)			/* get extended code if necessary */
				m=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
			return(0);
		  }	/* end if */
	  }	/* end for */
	return(1);
}	/* end expand() */

/**********************************************************************
*  Function	:	interpolation
*  Purpose	:	routine to blow up a section of the screen and interpolate
*					data to a smooth image
*  Parameters	:
*			x1,y1 -	location of the upper left hand corner of the box to blow up
*			x2,y2 -	location of the lower right hand corner of the box to blow up
*  Returns	:	
*			1 - if routine was successfully finished
*			0 - if routine was interupted
*  Calls	:	vgaline1(), egaline(), no9line(), interdata()
*  Called by	:	mousefunc()
**********************************************************************/
int interpolate(x1,y1,x2,y2)
int	x1,y1,					/* location of the upper left hand corner of the box to blow up */
	x2,y2;					/* location of the lower right hand corner of the box to blow up */

{
	unsigned char line[640];	/* a line to blow a line of the box up into */
	float	hpercent,vpercent,	/* the percentage of the distance the pixel is towards the hi value */
			vdata1,vdata2,		/* the data for the vertical interpolation */
			hfinal,				/* the final result for horizontal interpolation */
			hdata1,hdata2,		/* the data for horizontal interpolation */
			vfinal,				/* the final result for vertical interpolation */
			xdiff,ydiff,		/* the differences between the x and y corners */
			tempy,tempx,		/* temporary floating value */
			maxxf,maxyf;		/* a float variable for maxx and maxy */
	int		i,j,hix,lox,hiy,loy;	/* integer values for the coor. in the images around the current pixel */

	xdiff=x2-x1;					/* find difference between endpoints */
	ydiff=y2-y1;
	maxxf=maxx;
	maxyf=maxy;
	for(i=0; i<maxy; i++) {		/* go through all the lines in the image */
		tempy=((i/maxyf)*ydiff)+(float)y1;	/* calculate the fractional pixel position in the image */
		loy=tempy;		/* calculate the address of the lower y bound */
		hiy=ceil(tempy);		/* calculate the address of the upper y bound */
		vpercent=(tempy-(float)loy)/((float)hiy-(float)loy);
		for(j=0; j<maxx; j++) {		/* go through each line and interpolate */
			tempx=((j/maxxf)*xdiff)+(float)x1;	/* calculate the fractional pixel position in the image */
			lox=tempx;				/* calculate the lower x bound */
			hix=ceil(tempx);		/* calculate the upper x bound */
			vdata1=(store[hiy][lox]-store[loy][lox])*vpercent+store[loy][lox];
			vdata2=(store[hiy][hix]-store[loy][hix])*vpercent+store[loy][hix];
			hpercent=(tempx-(float)lox)/((float)hix-(float)lox);
			hfinal=(vdata2-vdata1)*hpercent+vdata1;
			hdata1=(store[loy][hix]-store[loy][lox])*hpercent+store[loy][lox];
			hdata2=(store[hiy][hix]-store[hiy][lox])*hpercent+store[hiy][lox];
			vfinal=(hdata2-hdata1)*vpercent+hdata1;
			line[j]=(hfinal+vfinal)/2.0;
		  }	/* end for */
		switch(mode) {		/* use different line drawing routines for different screens */
			case VGA:
				vgaline1(0,i,line,0,maxx);		/* send each line out to the screen */
				break;

			case NO9:
				no9line(0,i,line,0,maxx);
				break;

			case EGA:
			default:
				egaline(0,i,line,maxx,trans,0);
				break;
		 }	/* end switch */
		if(kbhit()) {				/* break out if the keyboard is hit */
			i=getch();
			if(i==0)				/* get extended code if necessary */
				i=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
			return(0);
		  }	/* end if */
	  }	/* end for */
	return(1);
}	/* end interpolation() */

#ifdef MOUSE
/**********************************************************************
*  Function	:	makehex
*  Purpose	:	convert an ascii string of binary digits into an integer
*  Parameters	:
*				binstr -	string of binary digits
*  Returns	:	integer value of binary string
*  Calls	:	pow(), strlen()
*  Called by	:	makemouse()
**********************************************************************/
int makehex(binstr)
char *binstr;
{
	int i,j,k;		/* local counting variables */
	double pow();	/* pow(x,y) returns x to the y power */

	k=strlen(binstr);	/* find the length of the binary string */
	i=0;
	for(j=k-1; j>=0; j--)		/* make a binary string into a hex number */
		i+=((int)pow((double)2,(double)j))*((int)*(binstr++)-48);
	return(i);
}

/**********************************************************************
*  Function	:	makemouse
*  Purpose	:	make the mouse cursor into a crosshair for vga and ega modes
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	mousecl()
*  Called by	:	options()
**********************************************************************/
void makemouse()
{
	int	cursor[2][16];		/* array for screen and cursor bit masks */

	if(mode!=NO9) {			/* only do this for vga and ega modes */
		/* screen mask */
		cursor[0][0]= makehex("1001111111111111");
		cursor[0][1]= makehex("1010111111111111");
		cursor[0][2]= makehex("1011011111111111");
		cursor[0][3]= makehex("1011101111111111");
		cursor[0][4]= makehex("1011110111111111");
		cursor[0][5]= makehex("1011111011111111");
		cursor[0][6]= makehex("1011111101111111");
		cursor[0][7]= makehex("1011111011111111");
		cursor[0][8]= makehex("1010011011111111");
		cursor[0][9]= makehex("1001101101111111");
		cursor[0][10]=makehex("1111101101111111");
		cursor[0][11]=makehex("1111110011111111");
		cursor[0][12]=makehex("1111111111111111");
		cursor[0][13]=makehex("1111111111111111");
		cursor[0][14]=makehex("1111111111111111");
		cursor[0][15]=makehex("1111111111111111");

		/* cursor mask */
		for(i=0; i<16; i++)		/* set the entire cursor mask to inverse of the screen mask */
			cursor[1][i]=~cursor[0][i];
		m1=9;					/* function to define cursor shape and hot spot */
		m2=0;					/* horizontal hot spot */
		m3=0;					/* vertical hot spot */
		mousecl(&m1,&m2,&m3,&cursor[0][0]);
		m1=1;					/* function to show the cursor */
		mousecl(&m1,&m2,&m3,&m4);
	  }	/* end if */
}	/* end makemouse() */
#endif

#ifdef QAK
/**********************************************************************
*  Function	:	bounce
*  Purpose	:	bounce a ball on the screen
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	drand48(), ball()
*  Called by	:	options()
**********************************************************************/
void bounce()
{
	int	x1[5],x2[5],y1[5],y2[5],		/* variables to keep track of the balls position */
		j,					/* timing variable */
		i,					/* local counting variable */
		maxball=0,			/* number of balls on the screen */
		ballsize,			/* size of the ball in pixels */
		xspeed[5],yspeed[5];		/* variables to keep track of the balls speed */

	double	drand48();		/* random number function */
	long	time(),l;			/* the system time for seeding the random pointer */
	void	parse(),srand48();		/* command parsing function */

	srand48(time(&l));		/* seed the random number generator */
	if(mode==NO9)			/* set the ball sizes for different screens */
		ballsize=8;
	else
		ballsize=4;
	x1[0]=(int)(drand48()*(double)(maxx-ballsize-1))+1;		/* randomize starting position */
	x2[0]=x1[0];
	y1[0]=(int)(drand48()*(double)(maxy-ballsize-1))+1;
	y2[0]=y1[0];
	xspeed[0]=0;
	while(xspeed[0]==0)				/* randomize speed, not allowing 0 speed in a direction */
		xspeed[0]=(int)(drand48()*(double)11)-6;
	yspeed[0]=0;
	while(yspeed[0]==0)
		yspeed[0]=(int)(drand48()*(double)11)-6;
#ifdef MOUSE
	m1=11;						/* reset the mouse position counters */
	mousecl(&m1,&m2,&m3,&m4);
#endif
	m3=m4=0;
	if(mode==NO9)				/* plot the ball */
		ball9(x1[0],y1[0]);
	else
		ball(x1[0],y1[0]);
	while(!kbhit() && !m3 && !m4) {	/* bounce the ball until a key is pressed */
		for(i=maxball; i>=0; i--) {		/* check through all the balls */
			if(mode==NO9)
				ball9(x1[i],y1[i]);
			else
				ball(x1[i],y1[i]);	/* remove the ball */
			x2[i]+=xspeed[i];		/* move the ball */
			y2[i]+=yspeed[i];
			if(x2[i]<0 || x2[i]>(maxx-ballsize-1)) {	/* check for off screen in the x direction */
				xspeed[i]=-(xspeed[i]);	/* reverse the direction of the movement */
				x2[i]+=xspeed[i];
			  }	/* end if */
			if(y2[i]<0 || y2[i]>(maxy-ballsize-1)) {	/* check for off screen in the y direction */
				yspeed[i]=-(yspeed[i]);
				y2[i]+=yspeed[i];
			  }	/* end if */
			if(mode==NO9)
				ball9(x2[i],y2[i]);
			else
				ball(x2[i],y2[i]);		/* plot the ball in it's new position */
			x1[i]=x2[i];				/* update the balls position */
			y1[i]=y2[i];
			if((int)(drand48()*(double)10000)==0)	/* on very rare occasions change the ball speed */
				switch ((int)(drand48()*4)) {		/* choose which speed to change */
					case 0:
						xspeed[i]++;
						break;

					case 1:
						xspeed[i]--;
						break;

					case 2:
						yspeed[i]++;
						break;

					case 3:
						yspeed[i]--;
						break;
				  }	/* end switch */
		  }	/* end for */
		for(j=0; j<(5-maxball)*600+2000; j++);	/* waiting loop */
		if(maxball<4)		/* if not at the maximum amount of balls then check for addition of a ball */
			if((int)(drand48()*(double)1000)==0) {	/* less than one in a hundred chance */
				maxball++;
				x1[maxball]=(int)(drand48()*(double)(maxx-ballsize-1))+1;		/* randomize starting position */
				x2[maxball]=x1[maxball];
				y1[maxball]=(int)(drand48()*(double)(maxy-ballsize-1))+1;
				y2[maxball]=y1[maxball];
				xspeed[maxball]=0;
				while(xspeed[maxball]==0)				/* randomize speed, not allowing 0 speed in a direction */
					xspeed[maxball]=(int)(drand48()*(double)11)-6;
				yspeed[maxball]=0;
				while(yspeed[maxball]==0)
					yspeed[maxball]=(int)(drand48()*(double)11)-6;
				if(mode==NO9)
					ball9(x1[maxball],y1[maxball]);
				else
					ball(x1[maxball],y1[maxball]);		/* plot the new ball */
			  }	/* end if */
#ifdef MOUSE
		m1=11;				/* check for the mouse moving */
		mousecl(&m1,&m2,&m3,&m4);
#endif
	  }	/* end while */
	for(i=maxball; i>=0; i--)
		if(mode==NO9)
			ball9(x1[i],y1[i]);
		else
			ball(x1[i],y1[i]);			/* erase balls from screen */
	if(kbhit()) {					/* if it was the keyboard and not the mouse */
		j=getch();					/* flush the keyboard buffer */
		if(j==0)					/* get extended code if necessary */
			j=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
		parse((unsigned char)j);
	  }	/* end if */
} /* end bounce() */
#endif

/**********************************************************************
*  Function	:	outlinee
*  Purpose	:	draw an outlined box on the ega screen
*  Parameters	:
*				x1,y1	- the x & y coor. of the upper left hand corner
*				x2,y2	- the x & y coor. of the lower right hand corner
*  Returns	:	none
*  Calls	:	lineega()
*  Called by	:	outline()
**********************************************************************/
void outlinee(x1,y1,x2,y2)
int	x1,y1,x2,y2;
{
	int	i,j;			/* temporary counting variables */

	if(x2<x1)
		swapint(&x1,&x2);
	if(y2<y1)
		swapint(&y1,&y2);
	if(x2>=xdim)			/* clip the box if it is outside the image */
		x2=xdim-1;
	if(x1>=xdim)
		x1=xdim-1;
	if(y2>=ydim)
		y2=ydim-1;
	if(y1>=ydim)
		y1=ydim-1;
	for(i=x1+xoff; i<=x2+xoff; i++)		/* go through each pixel in the top line */
		*(store[y1+yoff]+i)=~*(store[y1+yoff]+i);
	for(i=x1+xoff; i<=x2+xoff; i++)		/* go through each pixel in the bottom line */
		*(store[y2+yoff]+i)=~*(store[y2+yoff]+i);
	for(j=y1+1+yoff; j<y2+yoff; j++) {		/* go thru and fix all the lines in between */
		*(store[j]+x1+xoff)=~*(store[j]+x1+xoff);
		*(store[j]+x2+xoff)=~*(store[j]+x2+xoff);
	  }	/* end for */
	for(i=y1; i<=y2; i++) {		/* output the lines to the screen */
		egaline(xwhere+x1,ywhere+i,store[i+yoff],x2-x1+1,trans,x1+xoff);
	  }	/* end for */
}

/**********************************************************************
*  Function	:	outline
*  Purpose	:	call routines to put an outlined box on the screen
*  Parameters	:
*				x1,y1	- the x & y coor. of the upper left hand corner
*				x2,y2	- the x & y coor. of the lower right hand corner
*  Returns	:	none
*  Calls	:	outline9(), outlinev(), outlinee()
*  Called by	:	mousefunc()
**********************************************************************/
void outline(x1,y1,x2,y2)
int	x1,y1,x2,y2;
{
	switch(mode) {			/* switch to call different outline routines depending on monitor type */
		case NO9:
			outline9(x1,y1,x2,y2);
			break;

		case VGA:
			outlinev(x1,y1,x2,y2);
			break;

		case EGA:
		default:
			outlinee(x1,y1,x2,y2);	/* need offsets because ega has to modify the image data */
			break;
	  }	/* end switch */
}

#ifdef MOUSE
/**********************************************************************
*  Function	:	mousefunc
*  Purpose	:	perform all of the mouse functions
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	mousecl(), pixelcolor()
*  Called by	:	options()
**********************************************************************/
void mousefunc()
{
	int	off_x,off_y,			/* the offset the mouse cursor is inside the color bar */
		firstx,firsty;			/* the location of the first corner plotted in drawing a box */

	read_mouse();		/* get mouse position and button status */
	if(mx!=m3 || my!=m4) {	/* the mouse has been moved so update the screen */
		if(mode==NO9) {			/* update the mouse position for no9 screen */
			makecur9(mx,my);
			mx=m3;
			my=m4;
			makecur9(mx,my);
		  }	/* end if */
		else {
			mx=m3;				/* update the old mouse positions */
			my=m4;
		  }	/* end else */
	  }	/* end if */
	if(rightbutton && show && (mx>=pal_xoff && mx<=(pal_xoff+255)))	/* if inside the color bar when the right button is pressed, then move color bar */
		if(my>=pal_yoff && my<=(pal_yoff+pal_height)) {	/* if mouse cursor is in the color bar then move it */
			nopal(xoff,yoff);		/* erase the color bar */
			if(mode==NO9) {
				makecur9(mx,my);
				off_x=mx-pal_xoff;		/* set the offset in the color bar */
				off_y=my-pal_yoff;
			  }	/* end if */
			else {
				m1=2;				/* erase the cursor */
				mousecl(&m1,&m2,&m3,&m4);
				off_x=mx-pal_xoff;		/* set the offset in the color bar */
				off_y=my-pal_yoff;
			  }	/* end else */
			outline(pal_xoff,pal_yoff,pal_xoff+255,pal_yoff+pal_height);		/* plot the outline of the color bar */
			do {		/* keep moving the palette until the button is let up */
				read_mouse();		/* get mouse position and button status */
				if((m3+256-off_x)>maxx)	/* if trying to move the color bar off the left side of the screen */
					m3=maxx-256+off_x;
				if((m3-off_x)<0)		/* if trying to move off the right side of the screen */
					m3=off_x;
				if((m4+pal_height+1-off_y)>maxy)	/* if trying to move off the bottom of the screen */
					m4=maxy-pal_height-1+off_y;
				if((m4-off_y)<0)		/* if trying to move off the top of the screen */
					m4=off_y;
				if(m3!=mx || m4!=my) {	/* if mouse is moved then move outline of color bar */
					outline(mx-off_x,my-off_y,mx-off_x+255,my-off_y+pal_height);
					mx=m3;
					my=m4;
					outline(mx-off_x,my-off_y,mx-off_x+255,my-off_y+pal_height);
				  }	/* end if */
			} while(rightbutton);
			pal_xoff=mx-off_x;			/* find to upper left hand corner for the color bar */
			pal_yoff=my-off_y;
			outline(pal_xoff,pal_yoff,pal_xoff+255,pal_yoff+pal_height);
			draw_mouse();
			showpal();					/* put the color bar back on the screen */
		  }	/* end if */
	if(rightbutton) {		/* rightbutton functions */
		erase_mouse();
		if(mx>9 && mx<(maxx-9)) {	/* if not within the active region in the x dir. */
			if(my<10)			/* if within 10 pixels of top then scroll up */
				scroll('u');
			if(my>(maxy-10))	/* if within 10 pixels of bottn then scroll down */
				scroll('d');
		  }	/* end if */
		if(mx>(maxx-10)) 	/* if within 10 pixels of edge of screen then do something */
			if(my<10 || my>(maxy-10))	/* if in one of the corners then page */
				if(my<10)				/* page up */
					scroll('U');
				else					/* page down */
					scroll('D');
			else						/* scroll right */
				scroll('r');
		if(mx<10)			/* if within 10 pixels of edge of the screen then do something */
			if(my<10 || my>(maxy-10))	/* if in one of the corners then home/end */
				if(my<10)				/* Home */
					scroll('H');
				else					/* End */
					scroll('E');
			else						/* scroll left */
				scroll('l');
		draw_mouse();
	  }	/* end if */
	if(leftbutton) {	/* leftbutton functions */
		if(show && (mx>=pal_xoff && mx<=(pal_xoff+255)) && (my>=pal_yoff && my<=(pal_yoff+pal_height))) {
			if((mx-pal_xoff)<128) {			/* if on the left side then rotate that way */
				rotate('r');
				for(j=abs(mx-pal_xoff)*10; j>=0; j--);
			  }	/* end if */
			else {							/* must be on right side so rotate that way */
				rotate('l');
				for(j=(255-abs(mx-pal_xoff))*10; j>=0; j--);
			  }	/* end else */
		  }	/* end if */
		else {			/* draw an outlined box on the screen */
			erase_mouse();
			firstx=mx;
			firsty=my;
			outline(firstx,firsty,mx,my);
			do {		/* keep changing the size of the outline until the button is let up */
				read_mouse();		/* get mouse position and button status */
				if(m3!=mx || m4!=my) {	/* if mouse is moved then move outline of color bar */
					outline(firstx,firsty,mx,my);		/* erase the outline */
					mx=m3;
					my=m4;
					outline(firstx,firsty,mx,my);		/* re plot the outline */
				  }	/* end if */
			  } while(leftbutton);
			outline(firstx,firsty,mx,my);
			if(firstx>mx)					/* if square is defined backwards then flip ends */
				swapint(&firstx,&mx);
			if(firsty>my)
				swapint(&firsty,&my);
			if(mx>xdim-xoff)						/* prevent the box from being outside the image */
				mx=xdim;
			if(my>ydim-yoff)
				my=ydim;
			if(firstx<xdim-xoff && firsty<ydim-yoff) {	/* if the box is actually in the image on the screen then expand */
				grafmode();									/* clear the screen */
				if(expandit) {				/* if preference is to expand the image */
					if(expand(firstx+xoff,firsty+yoff,mx+xoff,my+yoff)==1)			/* expand chosen area to fill screen */
						waitq();	/* wait for keypress */
				  }	/* end if */
				else {						/* if preference is to interpolate the image */
					if(interpolate(firstx+xoff,firsty+yoff,mx+xoff,my+yoff)==1)			/* expand and smooth the chosen area to fill the screen */
						waitq();	/* wait for keypress */
				  }	/* end else */
			  }	/* end if */
			updatescrn();								/* go back to old display */
		  }	/* end else */	
	  }	/* end if */
}	/* end mousefunc() */
#endif

/**********************************************************************
*  Function	:	options
*  Purpose	:	do all the fancy run-time things to the image
*  Parameters	:	none
*  Returns	:	none
*  Calls	:	parse(), mousecl(), mousefunc(), bounce()
*  Called by	:	main()
**********************************************************************/
void options()
{
	palnum=0;		/* start out at the first palette */
#ifdef MOUSE
	if(mouse)		/* if the mouse is already on then put it on the screen */
		draw_mouse();
#endif
	c='!';
	if(kbhit()) {
		c=getch();
		if(c==0)		/* check for extended character code */
			c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
	  }	/* end if */

	while(c!='q' && !(c=='a' && animate) && c!='Q') {               	/* continue until 'q' */
		c='!';
#ifdef QAK
		waitcount=0;
#endif
		while (c=='!') {
			if(kbhit()) {		/* get the next command */
				c=getch();
				if(c==0)
					c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
			  }	/* end if */
#ifdef MOUSE
			if(mouse) {		/* if the mouse is active then perform some functions */
				mousefunc();
#ifdef QAK
				waitcount=0;		/* reset the counter to time out */
#endif
			  }	/* end if */
#endif
#ifdef QAK
			if(waitcount>100000) {		/* if timed out then bounce the ball */
				if(mode==VGA || mode==NO9)
					bounce();
				waitcount=0;		/* reset the timed out counter */
			  }	/* end if */
			waitcount++;
#endif
		  }	/* end while */
		if(c=='?') {				/* if help is requisitioned */
			if(mouse && mode==NO9)
				makecur9(mx,my);
			textmode();
			printf("General Help Screen\n\n");
			printf("i\t\t - Show Information About Image\n");
			printf("a\t\t - Show Animation Again\n");
			printf("k\t\t - End Animation Mode\n");
			printf("o\t\t - Turn VGA Screen Off Or On\n");
			printf("m\t\t - Activate/Deactivate Mouse\n");
			printf(",\t\t - Magnify\Interpolate A Portion Of The Screen\n");
			printf("w\t\t - Switch Between Magnifying And Interpolating\n");
			printf("/\t\t - Slow Down Animation Speed\n");
			printf("*\t\t - Speed Up Animation Speed\n");
			printf("q\t\t - Quit Viewing Current Image\n");
			printf("Q\t\t - Exit Program\n");
			printf("?\t\t - Show This Help Sequence\n\n");
			printf("Hit <enter> For More Help, Or Any Key to Return to Graphics Mode");
			c='!';
			while (c=='!') {
				if(kbhit()) {	/* get the next command */
					c=getch();
					if(c==0)
						c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
				  }	/* end if */
			  }	/* end while */
			printf("\n");
			if(c==13){	/* if enter is hit for next help screen */
				textmode();
				printf("Palette Help Screen\n\n");
				printf("f\t\t - Rotate Palette Continuously Forward\n");
				printf("b\t\t - Rotate Palette Continuously Backward\n");
				printf("+\t\t - Speed Up Palette Rotation\n");
				printf("-\t\t - Slow Down Palette Rotation\n");
				printf("space bar\t - Stop Rotation Of Palette Or Animation\n");
				printf("e\t\t - Rotate Palette Once Forward\n");
				printf("v\t\t - Rotate Palette Once Backward\n");
				printf("r\t\t - Reset Palette To Original Settings\n");
				printf("c\t\t - Toggle Color Bar On Or Off The Screen\n");
				printf("l\t\t - Load In A New Palette From Disk\n");
				printf("n\t\t - Display Next Palette In Memory\n");
				printf("s\t\t - Store Palette On Disk\n");
				printf("u\t\t - Swap The Red, Green, And Blue Components Of Palettes\n");
				printf("t\t\t - Transpose Palette\n");
				printf("g\t\t - Invert Palette Bitwise\n");
				printf("h\t\t - Enter Fiddle Mode\n");
				printf("p\t\t - Move Color Bar On The Screen\n");
				printf("d\t\t - Make Current Palette The Default Values\n\n");
				printf("Hit <enter> For More Help, Or Any Key to Return to Graphics Mode");
				c='!';
				while (c=='!') {
					if(kbhit()) {	/* get the next command */
						c=getch();
						if(c==0)
							c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
					  }	/* end if */
				  }	/* end while */
				printf("\n");
				if(c==13) {
					textmode();
					printf("Image Help Screen\n\n");
					printf("left arrow\t - Move Image Left Ten Pixels\n");
					printf("up arrow\t - Move Image Up Ten Pixels\n");
					printf("down arrow\t - Move Image Down Ten Pixels\n");
					printf("right arrow\t - Move Image Right Ten Pixels\n");
					printf("page up\t - Page Image Up A Full Screen\n");
					printf("page down\t - Page Image Down A Full Screen\n");
					printf("home\t\t - Move To The Upper Left Hand Corner Of The Image\n");
					printf("end\t\t - Move To The Lower Right Hand Corner Of The Image\n");
					printf("x\t\t - Input X and Y Coor. For Upper Left Hand Corner\n");
					printf("Hit Any Key to Return To Graphics Mode");
					c='!';
					while (c=='!') {
						if(kbhit()) {	/* get the next command */
							c=getch();
							if(c==0)
								c=getch()|(unsigned char)128;	/* set high bit to indicate extended code */
						  }	/* end if */
				 	  }	/* end while */
					printf("\n");
			  	  }	/* end if */
			  }	/* end if */
			grafmode();
			updatescrn();
		  }	/* end if */
		parse(c);
	  } /* end while */
#ifdef MOUSE
	if(mouse)					/* if the mouse is on then remove the cursor before leaving */
		erase_mouse();
#endif
} /* end options() */
