/*
 *  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.
 */

#include <stdio.h>
#include <math.h>
#include "dd.h"
#include "err.h"
#include "inter.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

#ifdef	USE_COLOR
/* epsbox.c */
extern BOOL f_spcolor;
extern int f_ccolor;
extern int f_rpcolor;
extern int f_bcolor;
extern int bmp_pt;

BOOL PutColoredChar(BUFFER *, int, int, int, int, int, int, int);
#endif

void *marea(unsigned int);

static 
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

static
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 */
#define ROT_COEFF long	/* rotation matrix coefficients */
#define ROT_TIMES 10000	/* constant for double-to-int conversion */
#define DOT_TIMES 6000	/* dots for rotation & scale */
#define ROT_ERROR 10	/* error estimate 0.1% */

#define	MAX_LEVEL_MAT	16
static  ROT_COEFF rot_mat[2][3] = {{ROT_TIMES, 0, 0}, {0, ROT_TIMES, 0}};
static  double	  rot_mult[2][3];
static	ROT_COEFF rot_mat_stack[MAX_LEVEL_MAT*6];
static	BOOL	  rotate_stack;
static	int	level_mat;

void alter_bitblt_rt(BOOL flag);

void set_affine_matrix(PIXEL origin_x, PIXEL origin_y, 
	double a_00, double a_01, double a_10, double a_11);
void set_scl_matrix(PIXEL origin_x, PIXEL origin_y, double h, double v);
void set_rot_matrix(PIXEL origin_x, PIXEL origin_y, double angle);

void push_rot_matrix(int mode);
void restore_rot_matrix(void);
void save_rot_matrix(void);
int add_rot_level(int mode);

static void mult_rot_matrix(void);

static
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);
static
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 */

static
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;
	short x; 
	unsigned int  ha, la, ch;
	unsigned char l_mask, h_rightest_mask, l_rightest_mask;

	unsigned char r_mask, leftest_mask;	/* byte mask */
	int word_count, byte;
#ifdef	USE_COLOR
	int bit_and = 0;
#endif

	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;
	}

#ifdef	USE_COLOR
	if(f_rpcolor == 4 || f_ttout)
		goto pcc;
	if( f_ccolor > 0 && f_spcolor ){
		if(bmp_pt || f_bcolor >= 0){
pcc:	  	if(PutColoredChar(source_byte, s_linewidth_byte, s_width_bit,
			   s_height_bit, d_offset_x_bit, d_offset_y_bit, -1, 0) )
			return;
		}else{
			if((f_ccolor & 0xffffff) == 0xffffff)
				bit_and = 1;
			else if(f_ccolor & 0xffffff)
				goto pcc;
		}
	}
#endif

	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_BUF *)dest_byte += (ulong) d_offset_y_bit * d_linewidth_byte;
		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 */
			(HUGE_BUF *)dest_byte -= 1;
			leftest_mask = 0x00;/* trim away leftest bytes */
		}
	}

	r_mask = (unsigned char)0xff >> d_offset_x_bit;
	l_mask = ~r_mask;
	/* 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) */

	x = ~((unsigned int)0xffff >> s_width_bit);
	h_rightest_mask = (x >> 8);
	l_rightest_mask = x & 0xff;
	/* rightest word: get left bits */

	d_offset_x_bit = 8 - d_offset_x_bit;

	while (s_height_bit-- > 0) {
		s = source_byte;
		d = dest_byte;
		x = word_count;
		if(x-- > 0) goto leftest;
		ch = 0;
		goto noleftest;				/* ch = 0, dl = leftest_mask */
leftest:
		ha = *s << d_offset_x_bit;
		la = *(++s) << d_offset_x_bit;
#ifdef	USE_COLOR
		if(bit_and){
			*d &= ~((ha >> 8) & leftest_mask);
			*(++d) &= ~(((la >> 8) | ha) & 0xff);
		}else
#endif
		{
			*d |= (ha >> 8) & leftest_mask;
			*(++d) |= ((la >> 8) | ha) & 0xff;
		}
		d++;
		s++;
		ch = la & l_mask;
		while(x-- > 0){
			ha = *s << d_offset_x_bit;
			la = *(++s) << d_offset_x_bit;
#ifdef	USE_COLOR
			if(bit_and){
				*d &= ~((ha >> 8) | ch);
				*(++d) &= ~(((la >> 8) | ha) & 0xff);
			}else
#endif
			{
				*d |= (ha >> 8) | ch;
				*(++d) |= ((la >> 8) | ha) & 0xff;
			}
			d++;
			s++;
			ch = la & l_mask;
		}
noleftest:
		if(s_width_bit){
			ha = *s << d_offset_x_bit;
			la = *(s+1) << d_offset_x_bit;
#ifdef	USE_COLOR
			if(bit_and){
				*d &= ~(((ha >> 8) | ch) & h_rightest_mask);
				*(d+1) &= ~(((la >> 8) | ha) & l_rightest_mask);
			}else
#endif
			{
				*d |= ((ha >> 8) | ch) & h_rightest_mask;
				*(d+1) |= ((la >> 8) | ha) & l_rightest_mask;
			}
		}
		(HUGE_BUF*)dest_byte += d_linewidth_byte;
		source_byte += s_linewidth_byte;
	}
	return;
}

#ifndef NOTPIC

static
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;
	short x; 
	unsigned int  ha, la, ch;
	unsigned char l_mask, h_rightest_mask, l_rightest_mask;
	unsigned char r_mask, leftest_mask;	/* byte 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;
	}

#ifdef	USE_COLOR
	if(	((f_ccolor > 0  && f_spcolor)|| f_rpcolor == 4 || f_ttout)
	  && PutColoredChar(source_byte, s_linewidth_byte, s_width_bit,	
			   s_height_bit, d_offset_x_bit, d_offset_y_bit, -2, 0) )
		return;
#endif

	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_BUF *)dest_byte += (ulong) d_offset_y_bit * d_linewidth_byte;
		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 */
			(HUGE_BUF*) dest_byte -= 1;
			leftest_mask = 0x00;/* trim away leftest bytes */
		}
	}

	r_mask = (unsigned char)0xff >> d_offset_x_bit;
	l_mask = ~r_mask;
	/* 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) */

	x = ~((unsigned int)0xffff >> s_width_bit);
	h_rightest_mask = ((unsigned int)x >> 8);
	l_rightest_mask = x & 0xff;
	/* rightest word: get left bits */

	d_offset_x_bit = 8 - d_offset_x_bit;

	while (s_height_bit-- > 0) {
		s = source_byte;
		d = dest_byte;
		x = word_count;
		if(x-- > 0) goto leftest;
		ch = *d & l_mask;
		goto noleftest;
leftest:
		ha = *s << d_offset_x_bit;
		*d &= l_mask;
		*d |= (ha >> 8) & leftest_mask;
		la = *(++s) << d_offset_x_bit;
		*(++d) = ((la >> 8) | ha) & 0xff;
		d++;
		s++;
		ch = la & l_mask;
		while(x-- > 0){
			ha = *s << d_offset_x_bit;
			*d = (ha >> 8) | ch;
			la = *(++s) << d_offset_x_bit;
			*(++d) = ((la >> 8) | ha) & 0xff;
			d++;
			s++;
			ch = la & l_mask;
		}
noleftest:
		if(s_width_bit){
			ha = *s << d_offset_x_bit;
			*d &= ~h_rightest_mask;
			*d |= ((ha >> 8) | ch) & h_rightest_mask;
			la = *(s+1) << d_offset_x_bit;
			*(d+1) &= ~l_rightest_mask;
			*(d+1) |= ((la >> 8) | ha) & l_rightest_mask;
		}
		(HUGE_BUF*)dest_byte += d_linewidth_byte;
		source_byte += s_linewidth_byte;
	}
	return;
}

/*static BOOL overwrite = FALSE, rotate = FALSE;*/
static BOOL overwrite = FALSE;
BOOL rotate = 0;/* @@@ */
int rot_angle=0;/* @@@ */
#if 0
static PIXEL rot_org_x;/* @@@ */
static PIXEL rot_org_y;/* @@@ */
#endif
int sp_h_dup;
int sp_v_dup;

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 = level_mat = rotate_stack = 0;
		restore_rot_matrix();
	}
	rotate = flag;
	return;
}

int get_rot_matrix(int mode)
{
	int i, j, diag;

	switch(mode){

		case -1:
			if(!rotate)
				return 0;
			i = rot_mat[0][0];
			j = rot_mat[1][0];
			if(i != rot_mat[1][1] || j + rot_mat[0][1] != 0)
				return 3600;
			diag = i*i + j*j;
			if(i > 0 && (diag > ROT_TIMES*(ROT_TIMES+4)
					  || diag < ROT_TIMES*(ROT_TIMES-4)) )
				return 7200;
			diag = (int)
				((asin(((double)j)/ROT_TIMES)+6.28318)*1800/3.14159+0.5);
			if(i < 0)
				diag = 5400 - diag;
			while(diag >= 3600)
				diag -= 3600;
			return diag;

		case -2:
		case 0:
			if(!rotate)
				return 0;		/* without rotation */
			diag = 0;
			for(i = 0; i < 2; i++){
				for(j = 0; j < 2; j++){
					if(rot_mat[i][j] > ROT_ERROR || rot_mat[i][j] < -ROT_ERROR)
						diag |= (i==j)?1:2;
				}
#if	0
				if(diag & 2)
					return -1;
#else
				if(diag & 2)
					return (diag & 1)?-1:2;	/* not diagonal matrix */
#endif
			}
			for(i = 0; i < 2; i++){
				if(  rot_mat[i][i] > (ROT_TIMES + ROT_ERROR)
				  || rot_mat[i][i] < (ROT_TIMES - ROT_ERROR))
					return 1;	/* diagonal matrix & not identity */
			}
			return (mode && (rot_mat[0][2] | rot_mat[1][2]))?4:0;
								/* identity rotation */
		case 1:
			return rot_mat[0][0];
		case 2:
			return rot_mat[1][1];
		case 3:
			return rot_mat[0][1];
		case 4:
			return rot_mat[1][0];
	}
	return -1;
}

int add_rot_level(int mode)
{
	if(mode > 0){
		if(level_mat < MAX_LEVEL_MAT - 1){
			level_mat++;
		}else
			error(WARNING, "Too much nesting of graphic rotations/scalings.");
	}
	else if(mode < 0 && level_mat > 0)
		level_mat--;
	return level_mat;
}

void save_rot_matrix(void)
{
	int i;

	i = level_mat*6;
	if(bitblt == bitblt_rt_ap){
		bitblt = bitblt_ap;
		goto on;
	}else if(bitblt == bitblt_rt_ow){
		bitblt = bitblt_ow;
on:		rotate_stack = TRUE;
		turn_on_trimming();
	}
	
	if(i || rotate){
		rot_mat_stack[i++] = rot_mat[0][0];
		rot_mat_stack[i++] = rot_mat[0][1];
		rot_mat_stack[i++] = rot_mat[0][2];
		rot_mat_stack[i++] = rot_mat[1][0];
		rot_mat_stack[i++] = rot_mat[1][1];
		rot_mat_stack[i]   = rot_mat[1][2];
	}else{
		rot_mat_stack[0] = rot_mat_stack[4] = ROT_TIMES;
		rot_mat_stack[1] = rot_mat_stack[2] = rot_mat_stack[3]
						 = rot_mat_stack[5] = 0;
	}
}

void restore_rot_matrix(void)
{
	int i;

	i = (level_mat-1)*6;
	if(i > 0){
		rot_mat[0][0] = rot_mat_stack[i];
		rot_mat[0][1] = rot_mat_stack[i+1];
		rot_mat[0][2] = rot_mat_stack[i+2];
		rot_mat[1][0] = rot_mat_stack[i+3];
		rot_mat[1][1] = rot_mat_stack[i+4];
		rot_mat[1][2] = rot_mat_stack[i+5];
	}else{
		rot_mat[0][0] = rot_mat[1][1] = ROT_TIMES;
		rot_mat[0][1] = rot_mat[0][2] = rot_mat[1][0] = rot_mat[1][2] = 0;
	}
	if(rotate_stack)
		alter_bitblt_rt(TRUE);
}

static ROT_COEFF unit_mult(ROT_COEFF x, ROT_COEFF y, double u, double v)
{
	return (ROT_COEFF)(x*u + y*v);
}

static void mult_rot_matrix(void)
{
	int tmp;

#if 0
error(14, "%d %d %d %d", 
rot_mat[0][0], rot_mat[0][1], rot_mat[1][0],rot_mat[1][1], rot_mat[0][2], rot_mat[1][2]);
error(14, "x %f %f %f %f %f %f %f %f", 
rot_mult[0][0], rot_mult[0][1], rot_mult[1][0], rot_mult[1][1], rot_mult[0][2], rot_mult[1][2]);
#endif

	rot_mat[0][2] += unit_mult(rot_mat[0][0], rot_mat[0][1],
					rot_mult[0][2], rot_mult[1][2]);
	rot_mat[1][2] += unit_mult(rot_mat[1][0], rot_mat[1][1],
					rot_mult[0][2], rot_mult[1][2]);
	tmp            = unit_mult(rot_mat[0][0], rot_mat[0][1],
					rot_mult[0][0], rot_mult[1][0]);
	rot_mat[0][1]  = unit_mult(rot_mat[0][0], rot_mat[0][1],
					rot_mult[0][1], rot_mult[1][1]);
	rot_mat[0][0]  = tmp;
	tmp			   = unit_mult(rot_mat[1][0], rot_mat[1][1],
						rot_mult[0][0], rot_mult[1][0]);
	rot_mat[1][1]  = unit_mult(rot_mat[1][0], rot_mat[1][1],
					   rot_mult[0][1], rot_mult[1][1]);
	rot_mat[1][0]  = tmp;
#if 0
error(14, "%d %d %d %d %d %d", 
rot_mat[0][0], rot_mat[0][1], rot_mat[1][0], rot_mat[1][1], rot_mat[0][2], rot_mat[1][2]);
error(14, "x %lf %lf %lf %lf", 
rot_mult[0][0], rot_mult[0][1], rot_mult[1][0], rot_mult[1][1]);
#endif
}

static ROT_COEFF unit_lmult(double x, double y, ROT_COEFF u, ROT_COEFF v)
{
	return (ROT_COEFF)(x*u + y*v);
}

static void lmult_rot_matrix(void)
{
	int tmp, tmp2;

#if 0
error(14, "%d %d %d %d %d %d", 
rot_mat[0][0], rot_mat[0][1], rot_mat[1][0],rot_mat[1][1], rot_mat[0][2], rot_mat[1][2]);
error(14, "x %f %f %f %f %f %f", 
rot_mult[0][0], rot_mult[0][1], rot_mult[1][0], rot_mult[1][1], rot_mult[0][2], rot_mult[1][2]);
#endif

	tmp			   = unit_lmult(rot_mult[0][0], rot_mult[0][1],
					 rot_mat[0][2], rot_mat[1][2]);
	rot_mat[1][2]  = unit_lmult(rot_mult[1][0], rot_mult[1][1],
					 rot_mat[0][2], rot_mat[1][2]);
	rot_mat[0][2]  = tmp;

	tmp            = unit_lmult(rot_mult[0][0], rot_mult[0][1],
					 rot_mat[0][0], rot_mat[1][0]);
	rot_mat[1][0]  = unit_lmult(rot_mult[1][0], rot_mult[1][1],
					 rot_mat[0][0], rot_mat[1][0]);
	rot_mat[0][0]  = tmp;

	tmp			   = unit_lmult(rot_mult[0][0], rot_mult[0][1],
					 rot_mat[0][1], rot_mat[1][1]);
	rot_mat[1][1]  = unit_lmult(rot_mult[1][0], rot_mult[1][1],
					   rot_mat[0][1], rot_mat[1][1]);
	rot_mat[0][1]  = tmp;

	tmp = (ROT_COEFF)(ROT_TIMES*rot_mult[0][2]);
	tmp2 = (ROT_COEFF)(ROT_TIMES*rot_mult[1][2]);
	rot_mat[0][2] +=  tmp - unit_lmult(rot_mult[0][0], rot_mult[0][1],
						tmp, tmp2);
	rot_mat[1][2]  = tmp2 - unit_lmult(rot_mult[1][0], rot_mult[1][1],
						tmp, tmp2);
	
#if 0
error(14, "%d %d %d %d %d %d", 
rot_mat[0][0], rot_mat[0][1], rot_mat[1][0], rot_mat[1][1], rot_mat[0][2], rot_mat[1][2]);
error(14, "x %lf %lf %lf %lf", 
rot_mult[0][0], rot_mult[0][1], rot_mult[1][0], rot_mult[1][1]);
#endif
}

void push_rot_matrix(int mode)
{
	static int level = 0;
	static ROT_COEFF rot0_mat[2][3];
	int i, j;

	if(mode == 1 && level == 0){
		level = 1;
		for(i = 0; i < 2; i++){
			for(j=0; j < 3; j++){
				rot0_mat[i][j] = rot_mat[i][j];
				rot_mat[i][j] = (i==j)?10000:0;
			}
		}
	}else{
		if(mode == 0 && level == 1){
			for(i = 0; i < 2; i++){
				for(j = 0; j < 3; j++){
					rot_mat[i][j] = rot0_mat[i][j];
				}
			}
			level = 0;
		}
	}
}

static void set_rot_width(void)
{
	sp_h_dup = (((rot_mat[0][0]>0)?rot_mat[0][0]:-rot_mat[0][0])
			+((rot_mat[0][1]>0)?rot_mat[0][1]:-rot_mat[0][1])
			+ DOT_TIMES)/ROT_TIMES;
	if(sp_h_dup <= 0)
		sp_h_dup = 1;
	sp_v_dup = (((rot_mat[1][0]>0)?rot_mat[1][0]:-rot_mat[1][0])
			+((rot_mat[1][1]>0)?rot_mat[1][1]:-rot_mat[1][1])
			+ DOT_TIMES)/ROT_TIMES;
	if(sp_v_dup <= 0)
		sp_v_dup = 1;
}

void set_affine_matrix(PIXEL origin_x, PIXEL origin_y, 
	 double a_00, double a_01, double a_10, double a_11)
{
	rot_mult[0][0] = a_00;
	rot_mult[0][1] = a_01;
	rot_mult[1][0] = a_10;
	rot_mult[1][1] = a_11;
	rot_mult[0][2] = origin_x*(1.0 - a_00) - origin_y*a_01;
	rot_mult[1][2] = origin_y*(1.0 - a_11) - origin_x*a_10;
	mult_rot_matrix();
	set_rot_width();
}

void set_scl_matrix(PIXEL origin_x, PIXEL origin_y, double h, double v)
{
	rot_mult[0][0] = h;
	rot_mult[1][1] = v;
	rot_mult[0][1] = rot_mult[1][0] = 0;
	rot_mult[0][2] = origin_x * (1.0 - h);
	rot_mult[1][2] = origin_y * (1.0 - v) ;
	mult_rot_matrix();
	set_rot_width();
}

void set_rot_matrix(PIXEL origin_x, PIXEL origin_y, double angle)
{
	double c, s;

	c = cos(angle);
	s = sin(angle);
	rot_mult[0][0] = rot_mult[1][1] = c;
	rot_mult[0][1] = -s;
	rot_mult[1][0] = s;
	rot_mult[0][2] = origin_x * (1.0 - c) + origin_y * s;
	rot_mult[1][2] = origin_y * (1.0 - c) - origin_x * s;
	mult_rot_matrix();
	rot_angle = ROUND(angle * 180.0 / 3.14159265) % 360;
	set_rot_width();
	return;
}


void set_lrot_matrix(PIXEL origin_x, PIXEL origin_y, double angle)
{
	double c, s;

	c = cos(angle);
	s = sin(angle);
	rot_mult[0][0] = rot_mult[1][1] = c;
	rot_mult[0][1] = -s;
	rot_mult[1][0] = s;
	rot_mult[0][2] = origin_x;
	rot_mult[1][2] = origin_y;
	lmult_rot_matrix();
	rot_angle = ROUND(angle * 180.0 / 3.14159265) % 360;
	set_rot_width();
	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);
}

static
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, d_xx, d_yy;
#ifdef	USE_COLOR
	int count;
#endif
	int xs[3], ys[3], w_x, w_y, p_x, p_y, w_xb, w_yb, i;
	unsigned char sbit_value;
	PIXEL hmax0;
	ROT_COEFF d_xsum0, d_ysum0, d_xsum, d_ysum;
	BUFFER *s_byte;	/* huge ptr */
	BUFFER *d_byte;	/* huge ptr */
	BUFFER *dest_buf;
	BUFFER *source_buf;
#ifdef	USE_COLOR
	int bit_and = 0;
#endif

#ifdef	USE_COLOR
	count = 0;
	if(f_ccolor > 0 && f_spcolor){
		if(bmp_pt==0 && f_rpcolor!= 4 && !f_ttout && f_bcolor < 0){
			if((f_ccolor & 0xffffff) == 0xffffff){
				bit_and = 1;
				goto again;
			}else if(!(f_ccolor & 0xffffff)){
				bit_and = -1;
				goto again;
			}
		}
fcc:	xs[0] = rot_mat[0][0] * s_width_bit;
		xs[1] = rot_mat[0][1] * s_height_bit;
		xs[2] = xs[0] + xs[1];
		ys[0] = rot_mat[1][0] * s_width_bit;
		ys[1] = rot_mat[1][1] * s_height_bit;
		ys[2] = ys[0] + ys[1];
		for(p_x = p_y = w_x = w_y = i = 0; i < 3; i++)
		{
			if(xs[i] < p_x)
				p_x = xs[i];
			if(xs[i] > w_x)
				w_x = xs[i];
			if(ys[i] < p_y)
				p_y = ys[i];
			if(ys[i] > w_y)
				w_y = ys[i];
		}
		w_x -= p_x;
		hmax0 = w_x/ROT_TIMES + sp_h_dup;
		w_xb = (hmax0 + 7)/8;
		w_y -= p_y;
		w_yb = w_y/ROT_TIMES + sp_v_dup;
		d_xsum0 = -p_x;
		d_ysum0 = -p_y;
		dest_buf = (char*)marea(i = w_xb*w_yb);
		while(--i >= 0)
			dest_buf[i] = 0;
	}else
#endif
	{
again:	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];
		dest_buf = dest_byte;
		hmax0 = hmax;
		w_xb = d_linewidth_byte;
		w_yb = vmax;
	}
	for (s_y = 0, source_buf = source_byte; s_y < s_height_bit; 
	  s_y++, source_buf += 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_buf, 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_xx = (int)(d_xsum / ROT_TIMES);
				d_yy = (int)(d_ysum / ROT_TIMES);
				for(d_x = d_xx + sp_h_dup; --d_x >= d_xx; ){
					for(d_y = d_yy + sp_v_dup; --d_y >= d_yy; ){
						if (d_y < 0 || d_y >= w_yb || d_x < 0 || d_x >= hmax0)
							continue;	/* trim */
						(HUGE_BUF*)d_byte = 
							(HUGE_BUF*)dest_buf + 
							+ (ulong) d_y * w_xb;
						d_byte += (d_x >> 3);
						dbit = 7 - (d_x & 0x07);
#ifdef	USE_COLOR
						if(bit_and > 0)
							*d_byte &= ~((sbit_value << dbit));
						else
#endif
							*d_byte |= (sbit_value << dbit);
					}
				}
			}
		}
	}
#ifdef	USE_COLOR
	if(!count++ && f_ccolor > 0  && f_spcolor && !bit_and){
		d_xsum0 = rot_mat[0][0] * d_offset_x_bit
			+ rot_mat[0][1] * d_offset_y_bit + rot_mat[0][2] + p_x;
		d_ysum0 = rot_mat[1][0] * d_offset_x_bit
			+ rot_mat[1][1] * d_offset_y_bit + rot_mat[1][2] + p_y;
		s_x = PutColoredChar(dest_buf, w_xb, hmax0, w_yb, 
			  d_xsum0/ROT_TIMES, d_ysum0/ROT_TIMES, -1, 0);
		Free0(dest_buf);
		if(!s_x)
			goto again;
	}
#endif
	return;
}

static
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, d_xx, d_yy;
	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_xx = (int)(d_xsum / ROT_TIMES);
			d_yy = (int)(d_ysum / ROT_TIMES);
			for(d_x = d_xx + sp_h_dup; --d_x >= d_xx; ){
				for(d_y = d_yy + sp_v_dup; --d_y >= d_yy; ){
					if (d_y < 0 || d_y >= vmax || d_x < 0 || d_x >= hmax)
						continue;		/* trim */
					(HUGE_BUF*)d_byte = 
						(HUGE_BUF*)dest_byte + (ulong) d_y * d_linewidth_byte;
					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 */
