/* Framebuffer Graphics Libary for Linux, Copyright 1993 Harm Hanemaayer */
/* line.c	Line drawing */


#include <stdlib.h>
#include <vga.h>
#include "inlstring.h"		/* include inline string operations */

#include "vgagl.h"
#include "def.h"
#include "driver.h"


/* We use the 32-bit to 64-bit multiply and 64-bit to 32-bit divide of the */
/* 386 (which gcc doesn't know well enough) to efficiently perform integer */
/* scaling without having to worry about overflows. */

static inline int muldiv64( int m1, int m2, int d ) {
/* int32 * int32 -> int64 / int32 -> int32 */
	int result;
	__asm__(
		"imull %%edx\n\t"
		"idivl %3\n\t"
		: "=a" (result)			/* out */
		: "a" (m1), "d" (m2), "g" (d)	/* in */
		: "ax", "dx"			/* mod */
	);
	return result;
}

static inline int gl_regioncode( int x, int y ) {
	int dx1, dx2, dy1, dy2;
	int result;
	result = 0;
	dy2 = __clipy2 - y;
	__asm__("btl $31,%1\n\t"
		"adcl $0,%0"
		: "=r" ((int) result)
		: "rm" ((int) dy2), "0" ((int) result)
		);
	result <<= 1;
	dy1 = y - __clipy1;
	__asm__("btl $31,%1\n\t"
		"adcl $0,%0"
		: "=r" ((int) result)
		: "rm" ((int) dy1), "0" ((int) result)
		);
	result <<= 1;
	dx2 = __clipx2 - x;
	__asm__("btl $31,%1\n\t"
		"adcl $0,%0"
		: "=r" ((int) result)
		: "rm" ((int) dx2), "0" ((int) result)
		);
	result <<= 1;
	dx1 = x - __clipx1;
	__asm__("btl $31,%1\n\t"
		"adcl $0,%0"
		: "=r" ((int) result)
		: "rm" ((int) dx1), "0" ((int) result)
		);
	return result;
}			


/* Partly based on vgalib by Tommy Frandsen */
/* This would be a lot faster if setpixel was inlined */

void gl_line( int x1, int y1, int x2, int y2, int c ) {
	uchar *vp;
	int dx, dy, ax, ay, sx, sy, x, y;
	
	if (__clip)
		/* Cohen & Sutherland algorithm */
		for (;;) {
			int r1 = gl_regioncode(x1, y1);
			int r2 = gl_regioncode(x2, y2);
			if (!(r1 | r2))
				break;		/* completely inside */
			if (r1 & r2)
				return;		/* completely outside */
			if (r1 == 0) {
				swap(x1, x2);		/* make sure first */
				swap(y1, y2);		/* point is outside */
				r1 = r2;
			}
			if (r1 & 1) {		/* left */ 
				y1 += muldiv64(__clipx1 - x1, y2 - y1, x2 - x1);
				x1 = __clipx1;
			}
			else
			if (r1 & 2) {		/* right */
				y1 += muldiv64(__clipx2 - x1, y2 - y1, x2 - x1);
				x1 = __clipx2;
			}
			else
			if (r1 & 4) {		/* top */
				x1 += muldiv64(__clipy1 - y1, x2 - x1, y2 - y1);
				y1 = __clipy1;
			}
			else
			if (r1 & 8) {		/* bottom */
				x1 += muldiv64(__clipy2 - y1, x2 - x1, y2 - y1);
				y1 = __clipy2;
			}
		}

    	dx = x2 - x1;
    	dy = y2 - y1;
    	ax = abs(dx) << 1;
    	ay = abs(dy) << 1;
    	sx = (dx >= 0) ? 1 : -1;
    	sy = (dy >= 0) ? 1 : -1;
	x = x1;
	y = y1;
	
	if (ax > ay) {
		int d = ay - (ax >> 1);
		while (x != x2) {
			setpixel(x, y, c);

	            	if (d > 0 || d == 0 && sx == 1) {
        	        	y += sy;
                		d -= ax;
          	 	}
	            	x += sx;
            		d += ay; 
  		}
    	} 
    	else {
    		int sy = (dy >= 0) ? 1 : -1;
		int d = ax - (ay >> 1);
		while (y != y2) {
			setpixel(x, y, c);

        	    	if (d > 0 || d == 0 && sy == 1) {
                		x += sx;
	                	d -= ay;
        	    	}
            		y += sy;
            		d += ax;
        	}
    	}
    	setpixel(x, y, c);
}


static void gl_setcirclepixels( int x, int y, int sx, int sy, int c ) {
	if (__clip) {
		int z = max(x, y);
		if (sx - z < __clipx1 || sx + z  > __clipx2 
		|| sy - z < __clipy1 || sy + z > __clipy2) {
			/* use setpixel clipping */
			gl_setpixel(sx + x, sy + y, c);
			gl_setpixel(sx - x, sy + y, c);
			gl_setpixel(sx + x, sy - y, c);
			gl_setpixel(sx - x, sy - y, c);
			gl_setpixel(sx + y, sy + x, c);
			gl_setpixel(sx - y, sy + x, c);
			gl_setpixel(sx + y, sy - x, c);
			gl_setpixel(sx - y, sy - x, c);
			return;
		}
	}
	setpixel(sx + x, sy + y, c);
	setpixel(sx - x, sy + y, c);
	setpixel(sx + x, sy - y, c);
	setpixel(sx - x, sy - y, c);
	setpixel(sx + y, sy + x, c);
	setpixel(sx - y, sy + x, c);
	setpixel(sx + y, sy - x, c);
	setpixel(sx - y, sy - x, c);
}

void gl_circle( int sx, int sy, int r, int c ) {
	int x, y, d;
	if (r < 1) {
		gl_setpixel(sx, sy, c); 
		return;
	}
	if (__clip)
		if (sx + r < __clipx1 || sx - r > __clipx2
		|| sy + r < __clipy1 || sy - r > __clipy2)
			return;
	x = 0;
	y = r;
	d = 1 - r;
	gl_setcirclepixels(x, y, sx, sy, c);
	while (x < y) {
		if (d < 0) 
			d += x * 2 + 3;
		else {
			d += x * 2 - y * 2 + 5;
			y--;
		}
		x++;
		gl_setcirclepixels(x, y, sx, sy, c);
	}
}
