/*
 *  TEX Device Driver
 *
 *	bitblt.c:	tpic specials (version 2.2) support routines
 *				Based on the specifications included in transfig package
 *				by Oh-Yeah?		14 November 1992
 *				Note.  bitblt_ap() is almost equivalent to w_font() in the
 *				original package.  bitblt_ow() is an overwriting version
 *				of bitblt_ap(), i.e., destination bitmap underneath is lost.
 */

#pragma inline

#define	asm_tpp
#define	asm_tcc	asm

#include "dd.h"
#include <math.h>

#ifndef NOTPIC
#ifndef NOTPIC_EXTENSION
/* bitmap.c */
typedef struct {
	PIXEL x, y;
} pixelpoint;

pixelpoint turn_off_trimming(void);
void turn_on_trimming(void);

#endif
#endif

void bitblt_ap(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
			   int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
			   int d_offset_x_bit, int d_offset_y_bit);

/* ptr to the actual function */
void (*bitblt) (BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
				int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
				int d_offset_x_bit, int d_offset_y_bit) = bitblt_ap;

#ifndef NOTPIC

void bitblt_ow(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
			   int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
			   int d_offset_x_bit, int d_offset_y_bit);

/* called by init_tile() and fin_tile() in bitmap.c */
void alter_bitblt_ow(BOOL flag);

#ifndef NOTPIC_EXTENSION

/* called by tpic_rt(), freeze_tpic_status(), melt_tpic_status() in tpic.c */
void alter_bitblt_rt(BOOL flag);
void set_rot_matrix(PIXEL origin_x, PIXEL origin_y, double angle);

void bitblt_rt_ap(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
				  int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
				  int d_offset_x_bit, int d_offset_y_bit);
void bitblt_rt_ow(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
				  int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
				  int d_offset_x_bit, int d_offset_y_bit);

#else

#define bitblt_rt_ow bitblt_ow	/* just for alter_bitblt_ow() */
#define bitblt_rt_ap bitblt_ap

#endif /* end of ifndef NOTPIC_EXTENSION ... else ... */

#endif /* end of ifndef NOTPIC */

void bitblt_ap(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
			   int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
			   int d_offset_x_bit, int d_offset_y_bit)
	/* bitmap block transfer from source to dest (bits are appended) */
	/* dest: byte addr = dest_byte + d_offset_y_bit * d_linewidth_byte
	+ d_offset_x_bit / 8); bit offset = d_offset_x_bit & 0x07 */
	/* if d_offset_x_bit or d_offset_y_bit < 0, trim away left/top bits */
	/* caution: no trimming is carried out for right/bottom bits */
{
	static BUFFER *d_byte_prev = NULL;
	static BUFFER *d_addr_prev;
	static int d_y_prev = -1;	/* for dest y cache */
	static int d_lwidth_prev = -1;	/* for dest linewidth cache */

	/*	BUFFER *s; */
	/*	BUFFER *d; *//* this is actually a huge ptr */
	unsigned char r_mask, leftest_mask;	/* byte mask */
	unsigned int rightest_mask;	/* word mask */
	int word_count, byte;

	if (d_offset_y_bit < 0) {	/* trim top */
		s_height_bit += d_offset_y_bit;
		if (s_height_bit <= 0)
			return;				/* nothing inside */
		source_byte += (-d_offset_y_bit) * s_linewidth_byte;
		/* trim source */
		d_offset_y_bit = 0;
	}

	if (d_offset_y_bit == d_y_prev && dest_byte == d_byte_prev
		&& d_lwidth_prev == d_linewidth_byte) {	/* cache */
		dest_byte = d_addr_prev;
	}
	else {
		d_y_prev = d_offset_y_bit;
		d_byte_prev = dest_byte;
		d_lwidth_prev = d_linewidth_byte;
		/* huge ptr calc:
			dest_byte += (ulong) d_offset_y_bit * d_linewidth_byte; */
		asm_tpp
		{
			asm_tcc mov ax, d_linewidth_byte;
			asm_tcc mul WORD PTR d_offset_y_bit;	/* dx:ax = mul result */
			asm_tcc add ax, dest_byte;	/* add to dx:ax as unsigned long */
			asm_tcc adc dx, 0;
			asm_tcc mov bx, 0fh;	/* normalize */
			asm_tcc and bx, ax;
			asm_tcc mov dest_byte, bx;	/* offset */
			asm_tcc mov cl, 4;
			asm_tcc shr ax, cl;
			asm_tcc shl dx, cl;
			asm_tcc add ah, dl;
			asm_tcc add dest_byte[2], ax;	/* segment */
		}
		d_addr_prev = dest_byte;
	}

	if (d_offset_x_bit >= 0) {
		dest_byte += d_offset_x_bit >> 3;
		leftest_mask = 0xff;	/* write all */
		d_offset_x_bit &= 0x07;	/* positive mod (0-7) */
	}
	else {						/* trim left bits */
		if (s_width_bit + d_offset_x_bit <= 0)
			return;				/* nothing inside */
		byte = (-d_offset_x_bit) >> 3;
		s_width_bit -= byte << 3;
		source_byte += byte;	/* trim source bytes */
		d_offset_x_bit &= 0x07;
		/* positive mod (0-7) = (8 * (byte + 1) + d_offset_x_bit) % 8 */
		if (d_offset_x_bit == 0) {
			leftest_mask = 0xff;/* write all */
		}
		else {					/* residual bits, trim */
			asm_tpp
			{
				asm_tcc mov ax, dest_byte;
				asm_tcc or ax, ax;
				asm_tcc jg not_zero;
				asm_tcc mov WORD PTR dest_byte, 0010h;
				/* if offset = 0, carry down */
				asm_tcc dec WORD PTR dest_byte[2];	/* from segment */
			}
		  not_zero:
			dest_byte--;		/* won't be written anyway */
			leftest_mask = 0x00;/* trim away leftest bytes */
		}
	}

	r_mask = (unsigned char)0xff >> d_offset_x_bit;
	/* AND mask for getting right bits */
	leftest_mask &= r_mask;		/* leftest byte: get right bits and trim */

	s_width_bit += d_offset_x_bit;	/* total bits */
	word_count = s_width_bit >> 4;
	s_width_bit &= 0x0f;		/* mod bits (0 - 15) */
	rightest_mask = ~((unsigned int)0xffff >> s_width_bit);
	/* rightest word: get left bits */

	while (s_height_bit-- > 0) {
		asm_tpp
		{
			asm_tcc push ds;
			asm_tcc lds si, source_byte;	/* ds:si = s = source_byte; */
			asm_tcc les di, dest_byte;	/* es:di = d = dest_byte; */
			asm_tcc mov bx, word_count;	/* bx = x = word_count; */
			asm_tcc mov cl, d_offset_x_bit;
			/* cl = rotation bits: keep throughout */

			/*		if (x-- > 0) {*//* leftest word */
			asm_tcc mov ax, bx;
			asm_tcc dec bx;
			asm_tcc or ax, ax;
			asm_tcc jg leftest;
			asm_tcc mov dl, leftest_mask;
			/* one (rightest) word only. trim it */
			asm_tcc xor ch, ch;
			/* ch = 0: initial keep for previous left bits */
			asm_tcc jmp noleftest;
		}
	  leftest:
		asm_tpp
		{
			asm_tcc mov dl, r_mask;	/* keep right mask */
			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;	/* ah = left, al = right */
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc mov ch, dl;
			asm_tcc not ch;	/* l_mask */
			asm_tcc and ch, ah;	/* keep left bits */
			asm_tcc and ah, leftest_mask;	/* get right bits and trim */
			asm_tcc xchg ah, al;
			asm_tcc or es:[di], ax;	/* *d |= ax */
			asm_tcc inc si;
			asm_tcc inc si;
			asm_tcc inc di;	/* might be dangerous but Okay, normalized */
			asm_tcc inc di;
		}

		/*			while (x-- > 0) {*//* following words */
	  followloop:
		asm_tpp
		{
			asm_tcc mov ax, bx;
			asm_tcc dec bx;
			asm_tcc or ax, ax;
			asm_tcc jng nofollow;

			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc mov dh, dl;
			asm_tcc not dh;
			asm_tcc and dh, ah;	/* l_mask: keep left bits */
			asm_tcc and ah, dl;	/* r_mask: get right bits */
			asm_tcc or ah, ch;	/* plus previous left bits */
			asm_tcc mov ch, dh;	/* save left bits for next writing */
			asm_tcc xchg ah, al;
			asm_tcc or es:[di], ax;	/* *d |= ax */
			asm_tcc inc si;
			asm_tcc inc si;
			asm_tcc inc di;	/* Okay, normalized */
			asm_tcc inc di;
			asm_tcc jmp followloop;
		}

		/*			} */
		/*		} */

	  noleftest:
	  nofollow:
		/*		if (s_width_bit > 0) {*//* rightest word */
		asm_tpp
		{
			asm_tcc mov ah, s_width_bit;
			asm_tcc or ah, ah;
			asm_tcc jng norightest;

			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc and ah, dl;
			/* r_mask: get right bits (and trim if only this) */
			asm_tcc or ah, ch;	/* plus previous left bits */
			asm_tcc and ax, rightest_mask;	/* finally, get left bits */
			asm_tcc xchg ah, al;
			asm_tcc or es:[di], ax;	/* *d |= ax */
		}
		/*		} */
	  norightest:
		asm_tpp
		{
			asm_tcc pop ds;
		}

		/*		dest_byte += d_linewidth_byte; */

		asm_tpp
		{						/* huge pointer calc */
			asm_tcc mov ax, d_linewidth_byte;
			asm_tcc add dest_byte, ax;	/* offset */
			asm_tcc jns exit;	/* if non negative, exit */

			asm_tcc mov ax, dest_byte;	/* normalize */
			asm_tcc mov bx, 0fh;
			asm_tcc and bx, ax;
			asm_tcc mov dest_byte, bx;	/* offset */
			asm_tcc mov cl, 4;
			asm_tcc shr ax, cl;
			asm_tcc add dest_byte[2], ax;	/* segment */
		}
	  exit:

		source_byte += s_linewidth_byte;

	}
	return;
}

#ifndef NOTPIC

void bitblt_ow(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
			   int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
			   int d_offset_x_bit, int d_offset_y_bit)
	/* this is an OVERWRITE version of bilblt_ap() */
	/* bitmap block transfer from source to dest */
	/* dest: byte addr = dest_byte + d_offset_y_bit * d_linewidth_byte
	+ d_offset_x_bit / 8); bit offset = d_offset_x_bit & 0x07 */
	/* if d_offset_x_bit or d_offset_y_bit < 0, trim away left/top bits */
	/* caution: no trimming is carried out for right/bottom bits */
{
	static BUFFER *d_byte_prev = NULL;
	static BUFFER *d_addr_prev;
	static int d_y_prev = -1;	/* for dest y cache */
	static int d_lwidth_prev = -1;	/* for dest linewidth cache */

	/*	BUFFER *s; */
	/*	BUFFER *d; *//* this is actually a huge ptr */
	unsigned char r_mask, leftest_mask;	/* byte mask */
	unsigned int rightest_mask;	/* word mask */
	int word_count, byte;

	if (d_offset_y_bit < 0) {	/* trim top */
		s_height_bit += d_offset_y_bit;
		if (s_height_bit <= 0)
			return;				/* nothing inside */
		source_byte += (-d_offset_y_bit) * s_linewidth_byte;
		/* trim source */
		d_offset_y_bit = 0;
	}

	if (d_offset_y_bit == d_y_prev && dest_byte == d_byte_prev
		&& d_lwidth_prev == d_linewidth_byte) {	/* cache */
		dest_byte = d_addr_prev;
	}
	else {
		d_y_prev = d_offset_y_bit;
		d_byte_prev = dest_byte;
		d_lwidth_prev = d_linewidth_byte;
		/* huge ptr calc:
			dest_byte += (ulong) d_offset_y_bit * d_linewidth_byte; */
		asm_tpp
		{
			asm_tcc mov ax, d_linewidth_byte;
			asm_tcc mul WORD PTR d_offset_y_bit;	/* dx:ax = mul result */
			asm_tcc add ax, dest_byte;	/* add to dx:ax as unsigned long */
			asm_tcc adc dx, 0;
			asm_tcc mov bx, 0fh;	/* normalize */
			asm_tcc and bx, ax;
			asm_tcc mov dest_byte, bx;	/* offset */
			asm_tcc mov cl, 4;
			asm_tcc shr ax, cl;
			asm_tcc shl dx, cl;
			asm_tcc add ah, dl;
			asm_tcc add dest_byte[2], ax;	/* segment */
		}
		d_addr_prev = dest_byte;
	}

	if (d_offset_x_bit >= 0) {
		dest_byte += d_offset_x_bit >> 3;
		leftest_mask = 0xff;	/* write all */
		d_offset_x_bit &= 0x07;	/* positive mod (0-7) */
	}
	else {						/* trim left bits */
		if (s_width_bit + d_offset_x_bit <= 0)
			return;				/* nothing inside */
		byte = (-d_offset_x_bit) >> 3;
		s_width_bit -= byte << 3;
		source_byte += byte;	/* trim source bytes */
		d_offset_x_bit &= 0x07;
		/* positive mod (0-7) = (8 * (byte + 1) + d_offset_x_bit) % 8 */
		if (d_offset_x_bit == 0) {
			leftest_mask = 0xff;/* write all */
		}
		else {					/* residual bits, trim */
			asm_tpp
			{
				asm_tcc mov ax, dest_byte;
				asm_tcc or ax, ax;
				asm_tcc jg not_zero;
				asm_tcc mov WORD PTR dest_byte, 0010h;
				/* if offset = 0, carry down */
				asm_tcc dec WORD PTR dest_byte[2];	/* from segment */
			}
		  not_zero:
			dest_byte--;		/* won't be written anyway */
			leftest_mask = 0x00;/* trim away leftest bytes */
		}
	}

	r_mask = (unsigned char)0xff >> d_offset_x_bit;
	/* AND mask for getting right bits */
	leftest_mask &= r_mask;		/* leftest byte: get right bits and trim */

	s_width_bit += d_offset_x_bit;	/* total bits */
	word_count = s_width_bit >> 4;
	s_width_bit &= 0x0f;		/* mod bits (0 - 15) */
	rightest_mask = ~((unsigned int)0xffff >> s_width_bit);
	/* rightest word: get left bits */

	while (s_height_bit-- > 0) {
		asm_tpp
		{
			asm_tcc push ds;
			asm_tcc lds si, source_byte;	/* ds:si = s = source_byte; */
			asm_tcc les di, dest_byte;	/* es:di = d = dest_byte; */
			asm_tcc mov bx, word_count;	/* bx = x = word_count; */
			asm_tcc mov cl, d_offset_x_bit;
			/* cl = rotation bits: keep throughout */

			/*		if (x-- > 0) {*//* leftest word */
			asm_tcc mov ax, bx;
			asm_tcc dec bx;
			asm_tcc or ax, ax;
			asm_tcc jg leftest;
			asm_tcc mov dl, leftest_mask;
			/* one (rightest) word only. trim it */

			/* OVERWRITE! asm_tcc xor ch, ch; */
			/* ch = 0: initial keep for previous left bits */
			asm_tcc mov ch, dl;
			asm_tcc not ch;
			asm_tcc and ch, es:[di];
			/* ch = keep left bits of dest 1st byte */
			asm_tcc jmp noleftest;
		}
	  leftest:
		asm_tpp
		{
			asm_tcc mov dl, r_mask;	/* keep right mask */
			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;	/* ah = left, al = right */
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc mov ch, dl;
			asm_tcc not ch;	/* l_mask */
			asm_tcc and ch, ah;	/* keep left bits */

			/* OVERWRITE! asm_tcc and ah, leftest_mask; */
			/* get right bits and trim */
			asm_tcc mov dh, leftest_mask;	/* get right bits and trim */
			asm_tcc and ah, dh;
			asm_tcc not dh;
			asm_tcc and es:[di], dh;	/* clear dest */
			asm_tcc mov BYTE PTR es:[di + 1], 0;
			asm_tcc xchg ah, al;
			asm_tcc or es:[di], ax;	/* *d |= ax */
			asm_tcc inc si;
			asm_tcc inc si;
			asm_tcc inc di;	/* might be dangerous but Okay, normalized */
			asm_tcc inc di;
		}

		/*			while (x-- > 0) {*//* following words */
	  followloop:
		asm_tpp
		{
			asm_tcc mov ax, bx;
			asm_tcc dec bx;
			asm_tcc or ax, ax;
			asm_tcc jng nofollow;

			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc mov dh, dl;
			asm_tcc not dh;
			asm_tcc and dh, ah;	/* l_mask: keep left bits */
			asm_tcc and ah, dl;	/* r_mask: get right bits */
			asm_tcc or ah, ch;	/* plus previous left bits */
			asm_tcc mov ch, dh;	/* save left bits for next writing */
			asm_tcc xchg ah, al;

			/* OVERWRITE! asm_tcc or  es:[di], ax; */
			/* *d |= ax */
			asm_tcc mov es:[di], ax;	/* *d = ax */
			asm_tcc inc si;
			asm_tcc inc si;
			asm_tcc inc di;	/* Okay, normalized */
			asm_tcc inc di;
			asm_tcc jmp followloop;
		}

		/*			} */
		/*		} */

	  noleftest:
	  nofollow:
		/*		if (s_width_bit > 0) {*//* rightest word */
		asm_tpp
		{
			asm_tcc mov ah, s_width_bit;
			asm_tcc or ah, ah;
			asm_tcc jng norightest;

			asm_tcc mov ax, ds:[si];	/* ax = *s; */
			asm_tcc xchg ah, al;
			asm_tcc ror ax, cl;	/* rotate to right */
			asm_tcc and ah, dl;
			/* r_mask: get right bits (and trim if only this) */
			asm_tcc or ah, ch;
			/* plus previous left bits (and recover dest if ..) */

			/* OVERWRITE! asm_tcc and ax, rightest_mask; */
			/* finally, get left bits */
			asm_tcc mov bx, rightest_mask;
			asm_tcc and ax, bx;	/* finally, get left bits */
			asm_tcc not bx;
			asm_tcc xchg bh, bl;
			asm_tcc and es:[di], bx;	/* clear left bits of dest */
			asm_tcc xchg ah, al;
			asm_tcc or es:[di], ax;	/* *d |= ax */
		}
		/*		} */
	  norightest:
		asm_tpp
		{
			asm_tcc pop ds;
		}

		/*		dest_byte += d_linewidth_byte; */

		asm_tpp
		{						/* huge pointer calc */
			asm_tcc mov ax, d_linewidth_byte;
			asm_tcc add dest_byte, ax;	/* offset */
			asm_tcc jns exit;	/* if non negative, exit */

			asm_tcc mov ax, dest_byte;	/* normalize */
			asm_tcc mov bx, 0fh;
			asm_tcc and bx, ax;
			asm_tcc mov dest_byte, bx;	/* offset */
			asm_tcc mov cl, 4;
			asm_tcc shr ax, cl;
			asm_tcc add dest_byte[2], ax;	/* segment */
		}
	  exit:

		source_byte += s_linewidth_byte;

	}
	return;
}

/*static BOOL overwrite = FALSE, rotate = FALSE;*/
static BOOL overwrite = FALSE;
BOOL rotate = FALSE;/* @@@ */
int rot_angle=0;/* @@@ */
PIXEL rot_org_x;/* @@@ */
PIXEL rot_org_y;/* @@@ */

void alter_bitblt_ow(BOOL flag)
{
	if (flag)
		bitblt = (rotate) ? bitblt_rt_ow : bitblt_ow;
	else
		bitblt = (rotate) ? bitblt_rt_ap : bitblt_ap;
	overwrite = flag;
	return;
}

#ifndef NOTPIC_EXTENSION

/* Here are rotation routines */

static PIXEL hmax, vmax;

void alter_bitblt_rt(BOOL flag)
{
	pixelpoint trimmax;

	if (flag) {
		bitblt = (overwrite) ? bitblt_rt_ow : bitblt_rt_ap;
		trimmax = turn_off_trimming();	/* do not trim in bitmap.c */
		hmax = trimmax.x;		/* bitmap max. use 'em for trimming here */
		vmax = trimmax.y;
	}
	else {
		bitblt = (overwrite) ? bitblt_ow : bitblt_ap;
		turn_on_trimming();
		rot_angle = 0;
	}
	rotate = flag;
	return;
}

#define ROT_COEFF long	/* rotation matrix coefficients */
#define ROT_TIMES 10000	/* constant for double-to-int conversion */

static ROT_COEFF rot_mat[2][3];

void set_rot_matrix(PIXEL origin_x, PIXEL origin_y, double angle)
	/* angle in radians, clockwise */
{
	double c, s;

	c = cos(angle);
	s = sin(angle);
	rot_mat[0][0] = (ROT_COEFF) (c * ROT_TIMES);
	rot_mat[0][1] = -(ROT_COEFF) (s * ROT_TIMES);
	rot_mat[0][2] =
		(ROT_COEFF) ((origin_x * (1.0 - c) + origin_y * s) * ROT_TIMES);
	rot_mat[1][0] = (ROT_COEFF) (s * ROT_TIMES);
	rot_mat[1][1] = (ROT_COEFF) (c * ROT_TIMES);
	rot_mat[1][2] =
		(ROT_COEFF) ((origin_y * (1.0 - c) - origin_x * s) * ROT_TIMES);
	rot_angle = (int)(angle * 180.0 / 3.141592 + 0.5) % 360;
	rot_org_x = origin_x;
	rot_org_y = origin_y;
	return;
}

void rotate_address(PIXEL *xp, PIXEL *yp)
{
	long d_x, d_y;
	d_x = rot_mat[0][0] * *xp + rot_mat[0][1] * *yp + rot_mat[0][2];
	d_y = rot_mat[1][0] * *xp + rot_mat[1][1] * *yp + rot_mat[1][2];
	*xp = (int)(d_x / ROT_TIMES);
	*yp = (int)(d_y / ROT_TIMES);
}

void bitblt_rt_ap(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
				  int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
				  int d_offset_x_bit, int d_offset_y_bit)
/* bitmap block transfer from source to dest (bits are appended) */
/* dest (in TeX): byte addr = dest_byte + d_offset_y_bit * d_linewidth_byte
	+ d_offset_x_bit / 8); bit offset = d_offset_x_bit & 0x07 */
/* dest (real)  : every bit is rotated by
				d_x = rot_mat[0][0] * d_offset_x_bit
				+ rot_mat[0][1] * d_offset_y_bit + rot_mat[0][2];
				d_y = rot_mat[1][0] * d_offset_x_bit
				+ rot_mat[1][1] * d_offset_y_bit + rot_mat[1][2];
	byte addr  = dest_byte + d_y * d_linewidth_byte + d_x / 8);
	bit offset = d_x & 0x07 */
/* negative d_offset_x_bit or d_offset_y_bit is just accepted, without any
trimming.  d_x and d_y are checked and trimmed if necessary */
/* Note: this routine is slow, not optimized at all (^^); */
{
	int s_x, s_y, sbit, d_x, d_y, dbit;
	unsigned char sbit_value;
	ROT_COEFF d_xsum0, d_ysum0, d_xsum, d_ysum;
	BUFFER *s_byte;	/* huge ptr */
	BUFFER *d_byte;	/* huge ptr */

	d_xsum0 = rot_mat[0][0] * d_offset_x_bit
		+ rot_mat[0][1] * d_offset_y_bit + rot_mat[0][2];
	d_ysum0 = rot_mat[1][0] * d_offset_x_bit
		+ rot_mat[1][1] * d_offset_y_bit + rot_mat[1][2];
	for (s_y = 0; s_y < s_height_bit; s_y++, source_byte += s_linewidth_byte,
		 d_xsum0 += rot_mat[0][1], d_ysum0 += rot_mat[1][1]) {
		d_xsum = d_xsum0;
		d_ysum = d_ysum0;
		for (s_x = 0, s_byte = source_byte, sbit = 7; s_x < s_width_bit;
		  s_x++, sbit--, d_xsum += rot_mat[0][0], d_ysum += rot_mat[1][0]) {
			if (sbit < 0) {		/* next byte */
				s_byte++;
				sbit = 7;
			}
			sbit_value = (*s_byte >> sbit) & 0x01;
			if (sbit_value) {	/* source bit is on */
				d_x = (int)(d_xsum / ROT_TIMES);
				d_y = (int)(d_ysum / ROT_TIMES);
				if (d_y < 0 || d_y >= vmax || d_x < 0 || d_x >= hmax)
					continue;	/* trim */
				/* huge ptr calc:
				d_byte = dest_byte + (ulong) d_y * d_linewidth_byte; */
				asm_tpp
				{
					asm_tcc mov ax, d_linewidth_byte;
					asm_tcc mul WORD PTR d_y;	/* dx:ax = mul result */
					asm_tcc add ax, dest_byte;
					/* add to dx:ax as unsigned long */
					asm_tcc adc dx, 0;
					asm_tcc mov bx, 0fh;	/* normalize */
					asm_tcc and bx, ax;
					asm_tcc mov d_byte, bx;	/* offset */
					asm_tcc mov cl, 4;
					asm_tcc shr ax, cl;
					asm_tcc shl dx, cl;
					asm_tcc add ah, dl;
					asm_tcc add ax, dest_byte[2];
					asm_tcc mov d_byte[2], ax;	/* segment */
				}
				d_byte += (d_x >> 3);
				dbit = 7 - (d_x & 0x07);
				*d_byte |= (sbit_value << dbit);
			}
		}
	}

	return;
}

void bitblt_rt_ow(BUFFER *source_byte, int s_linewidth_byte, int s_width_bit,
				  int s_height_bit, BUFFER *dest_byte, int d_linewidth_byte,
				  int d_offset_x_bit, int d_offset_y_bit)
/* this is an OVERWRITE version of bitblt_rt_ap() */
/* bitmap block transfer from source to dest */
/* dest (in TeX): byte addr = dest_byte + d_offset_y_bit * d_linewidth_byte
	+ d_offset_x_bit / 8); bit offset = d_offset_x_bit & 0x07 */
/* dest (real)  : every bit is rotated by
				d_x = rot_mat[0][0] * d_offset_x_bit
				+ rot_mat[0][1] * d_offset_y_bit + rot_mat[0][2];
				d_y = rot_mat[1][0] * d_offset_x_bit
				+ rot_mat[1][1] * d_offset_y_bit + rot_mat[1][2];
	byte addr  = dest_byte + d_y * d_linewidth_byte + d_x / 8);
	bit offset = d_x & 0x07 */
/* negative d_offset_x_bit or d_offset_y_bit is just accepted, without any
trimming.  d_x and d_y are checked and trimmed if necessary */
/* Note: this routine is slow, not optimized at all (^^); */
{
	int s_x, s_y, sbit, d_x, d_y, dbit;
	unsigned char sbit_value;
	ROT_COEFF d_xsum0, d_ysum0, d_xsum, d_ysum;
	BUFFER *s_byte;	/* huge ptr */
	BUFFER *d_byte;	/* huge ptr */

	d_xsum0 = rot_mat[0][0] * d_offset_x_bit
		+ rot_mat[0][1] * d_offset_y_bit + rot_mat[0][2];
	d_ysum0 = rot_mat[1][0] * d_offset_x_bit
		+ rot_mat[1][1] * d_offset_y_bit + rot_mat[1][2];
	for (s_y = 0; s_y < s_height_bit; s_y++, source_byte += s_linewidth_byte,
		 d_xsum0 += rot_mat[0][1], d_ysum0 += rot_mat[1][1]) {
		d_xsum = d_xsum0;
		d_ysum = d_ysum0;
		for (s_x = 0, s_byte = source_byte, sbit = 7; s_x < s_width_bit;
		  s_x++, sbit--, d_xsum += rot_mat[0][0], d_ysum += rot_mat[1][0]) {
			if (sbit < 0) {		/* next byte */
				s_byte++;
				sbit = 7;
			}
			sbit_value = (*s_byte >> sbit) & 0x01;
			d_x = (int)(d_xsum / ROT_TIMES);
			d_y = (int)(d_ysum / ROT_TIMES);
			if (d_y < 0 || d_y >= vmax || d_x < 0 || d_x >= hmax)
				continue;		/* trim */
			/* huge ptr calc:
			d_byte = dest_byte + (ulong) d_y * d_linewidth_byte; */
			asm_tpp
			{
				asm_tcc mov ax, d_linewidth_byte;
				asm_tcc mul WORD PTR d_y;	/* dx:ax = mul result */
				asm_tcc add ax, dest_byte;
				/* add to dx:ax as unsigned long */
				asm_tcc adc dx, 0;
				asm_tcc mov bx, 0fh;	/* normalize */
				asm_tcc and bx, ax;
				asm_tcc mov d_byte, bx;	/* offset */
				asm_tcc mov cl, 4;
				asm_tcc shr ax, cl;
				asm_tcc shl dx, cl;
				asm_tcc add ah, dl;
				asm_tcc add ax, dest_byte[2];
				asm_tcc mov d_byte[2], ax;	/* segment */
			}
			d_byte += (d_x >> 3);
			dbit = 7 - (d_x & 0x07);
			if (sbit_value)		/* bit is on */
				*d_byte |= (sbit_value << dbit);
			else				/* bit is off */
				*d_byte &= ~(0x01 << dbit);
		}
	}

	return;
}

#endif /* end of ifndef NOTPIC_EXTENSION */

#endif /* end of ifndef NOTPIC */

/* end of bitblt.c */
