
/* This module implements XOR mode in pixel drawing under SVGALIB */
/* 
	It only supports 256 colors and 800x600 mode for now.

  The circle drawing and line drawing algorithms aren't the best... :-)
  Feel free to improve them. :)
*/

#include <stdio.h>
#include <math.h>
#include <vga.h>
#include <vgagl.h>

#include "vgaxor.h"

#include "pc8x8.font"
#define FONT_WIDTH	8
#define FONT_HEIGHT	8

static GraphicsContext *screen=NULL;		/* GL context of the screen */
static unsigned char  *Screen_Bits;		/* Bitmap of the screen */

static int  draw_mode;			/* One of the above defined modes */
static unsigned char back_color;	/* The background color (black) */
static unsigned char draw_color;	/* The current color we draw in */
static unsigned int  line_width=1;	/* The current drawn line width */
static unsigned int  pixel_incr=1;	/* One half of the line style */
static unsigned int  blank_incr=0;	/* The other half of the line style */

#ifndef LIBRARY
#define LIBRARY
#endif

LIBRARY void Set_Palette(Color *palette)
{
	int c;

	if ( palette ) {
		for ( c=0; c<256; ++c ) {
			if ( !palette[c].red && 
			     !palette[c].green && !palette[c].blue )
				back_color = c;
			gl_setpalettecolor(c, palette[c].red/4,
				palette[c].green/4, palette[c].blue/4);
		}
	} else
		gl_setrgbpalette();
}

LIBRARY int Init_800x600(void)
{
	/* Allocate a screen bitmap, and set it to zero */
	Screen_Bits = (unsigned char *)malloc((SCREEN_WIDTH/8)*SCREEN_HEIGHT);
	if ( ! Screen_Bits )
		return(-1);
	memset(Screen_Bits, 0, ((SCREEN_WIDTH/8)*SCREEN_HEIGHT));

	/* Turn on the SVGA Graphics mode */
	if ( vga_setmode(G800x600x256) != 0 )
		return(-1);
	gl_setcontextvga(G800x600x256);
	screen = gl_allocatecontext();
	gl_getcontext(screen);

	/* Initialize variables */
	back_color = BLACK_COLOR;
	draw_color = WHITE_COLOR;
	Set_Palette(NULL);
	line_width = 1;
	pixel_incr = 1;
	blank_incr = 0;
	return(0);
}
	
LIBRARY void End_800x600(void)
{
	(void) free(Screen_Bits);
	vga_setmode(TEXT);
}

LIBRARY void Set_DrawMode(int mode)
{
	draw_mode = mode;
}

LIBRARY void Set_LineWidth(unsigned int width)
{
	line_width = width;
}

LIBRARY void Set_LineIncr(unsigned int pixeli, unsigned int blanki)
{
	if ( pixeli > 0 )
		pixel_incr = pixeli;
	blank_incr = blanki;
}

LIBRARY void Set_Color(unsigned char color)
{
	draw_color = color;
}

LIBRARY void Draw_Pixel(unsigned int x, unsigned int y)
{
	int offset, mask;

	/* Do bounds checking */
	if ( (x > SCREEN_WIDTH) || (y > SCREEN_HEIGHT) )
		return;

	/* Draw the pixel in the current graphics mode */
	switch (draw_mode) {
		case DRAW_MXOR:	offset = ((y*SCREEN_WIDTH)+x);
				mask = (0x80>>(offset%BPP));
				if ( (Screen_Bits[offset/BPP] ^= mask) & mask )
					gl_setpixel(x, y, draw_color);
				else
					gl_setpixel(x, y, back_color);
				break;
		case DRAW_COPY:	gl_setpixel(x, y, draw_color);
				break;
		default:	/* Error! */ break;
	}
}

LIBRARY void Draw_Text(unsigned int x, unsigned int y, unsigned char *text)
{
	int row, col;

	while ( *text ) {
		for ( row=0; row<FONT_HEIGHT; ++row ) {
			for ( col=0; col<FONT_WIDTH; ++col ) {
				if ( (pc8x8_fontdata[*text][row]>>col) & 0x01 )
					Draw_Pixel(x+col, y+row);
			}
		}
		x += FONT_WIDTH;
		++text;
	}
}

LIBRARY void Draw_Line(unsigned int x0, unsigned int y0, 
					unsigned int x1, unsigned int y1)
{
	int i, x, y;
	int lo, hi, lo_bound, hi_bound;
	double slope, b;

	if ( y0 == y1 )  {  /* Horizontal line */
		lo_bound = (y0 - (line_width/2));
		hi_bound = (y0 + (line_width/2)+(line_width%2));
		lo = (x0 < x1 ? x0 : x1);
		hi = (x0 > x1 ? x0 : x1);
		for ( y=lo_bound; y<hi_bound; ++y ) {
			for ( x=lo; x<hi; x += blank_incr ) {
				for ( i=0; (i<pixel_incr)&&(x<hi); ++i, ++x )
					Draw_Pixel(x, y);
			}
		}
	} else if ( x0 == x1 ) {  /* Vertical line */
		lo_bound = (x0 - (line_width/2));
		hi_bound = (x0 + (line_width/2)+(line_width%2));
		lo = (y0 < y1 ? y0 : y1);
		hi = (y0 > y1 ? y0 : y1);
		for ( x=lo_bound; x<hi_bound; ++x ) {
			for ( y=lo; y<hi; y += blank_incr ) {
				for ( i=0; (i<pixel_incr)&&(y<hi); ++i, ++y )
					Draw_Pixel(x, y);
			}
		}
	} else {
		/* Equation:  y = mx + b */
		slope = ((double)((int)(y1 - y0))/(double)((int)(x1 - x0)));
		b = (double)(y0 - slope*(double)x0);
		lo_bound = (0 - (line_width/2));
		hi_bound = (0 + (line_width/2)+(line_width%2));
		if ( ((slope < 0) ? slope > -1 : slope < 1) ) {
			lo = (x0 < x1 ? x0 : x1);
			hi = (x0 > x1 ? x0 : x1);
			for ( ; lo_bound < hi_bound; ++lo_bound ) {
				for ( x=lo; x<hi; x += blank_incr ) {
					for (i=0; (i<pixel_incr)&&(x<hi); ++i, ++x) {
						y = (int)
					  (slope*(double)x) + (b+lo_bound);
						Draw_Pixel(x, y);
					}
				}
			}
		} else {
			lo = (y0 < y1 ? y0 : y1);
			hi = (y0 > y1 ? y0 : y1);
			for ( ; lo_bound < hi_bound; ++lo_bound ) {
				for ( y=lo; y<hi; y += blank_incr ) {
					for (i=0; (i<pixel_incr)&&(y<hi); ++i, ++y) {
						x = (int)
					  ((((double)y - b)/slope)+lo_bound);
						Draw_Pixel(x, y);
					}
				}
			}
		}
	}
}

LIBRARY void Draw_Circle(unsigned int x0, unsigned int y0, unsigned int r)
{
	double x, y;
	double A, d;

	for ( d=0; d<(r*r+8); ++d ) {
		A = ((d/((r*r+8)/2))*PI);
		x = ((cos(A)*r) + (double)x0);
		y = ((sin(A)*r) + (double)y0);
		Draw_Pixel((unsigned int)x, (unsigned int)y);
	}
}

LIBRARY void Draw_Rect(unsigned int x0, unsigned int y0, 
					unsigned int x1, unsigned int y1)
{
	Draw_Line(x0, y0, x1, y0);
	Draw_Line(x0, y1, x1, y1);
	Draw_Line(x0, y0, x0, y1);
	Draw_Line(x1, y0, x1, y1);
}

LIBRARY void Fill_Rect(unsigned int x0, unsigned int y0, 
					unsigned int x1, unsigned int y1)
{
	int x, y;

	for ( y=y0; y<y1; ++y ) {
		for ( x=x0; x<x1; ++x )
			Draw_Pixel(x, y);
	}
}

LIBRARY void Clear_Area(unsigned int x0, unsigned int y0,
					unsigned int x1, unsigned int y1)
{
	int x, y;

	for ( y=y0; y<y1; ++y ) {
		for ( x=x0; x<x1; ++x )
			gl_setpixel(x, y, back_color);
	}
}
