/*
	ARTemis (Graphic Editor for FM-TOWNS)
	(c) MATSUUCHI Ryosuke 1992,1993

	imageman.c
		Editing Image Manager(編集画像管理部)

	EIMnew			EIM の初期化
	EIMdelete		EIM の終了

	EIMresize		編集画像のサイズの変更
	EIMgetxbytes	編集画像バッファの１ラインのバイト数
	EIMadrs			指定座標に対応する編集画像バッファのアドレス
	EIMgetxsize		編集画像の大きさを得る
	EIMgetysize		編集画像の大きさを得る
	EIMdispon		画面表示も同時に行う
	EIMdispoff		画面表示は行わない
	
	EIMpset			編集画像上に点を打つ
	EIMline			直線を描く
	EIMhline		水平直線を描く
	EIMvline
	EIMboxline		矩形（枠）を描く
	EIMrboxline
	EIMboxfill
	EIMrboxfill		filled 矩形を描く
	EIMgetblock		矩形イメージを切り出す
	EIMputblock		矩形イメージを貼りつける
	EIMpoint		指定した座標のカラーコード(パレット番号)を得る
	EIMpoint_back	アンドゥーバッファ上のカラーコードを得る

	EIMgraypset
	EIMgrayhline
	EIMgrayhline_map

	EIMbackup		現在の編集画像をアンドゥーバッファに転送する
	EIMloadbackup	アンドゥーバッファを現在の編集画像に
*/

#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <msdos.cf>
#include <stdlib.h>
#include "ge.h"
#include "dispman.h"
#include "imageman.h"
#include "mask.h"
#include "subgrp.h"


int graycol(int col1, int col2, int rate)
{
	if(rate<0) rate=0; else if(rate>256) rate=256;
	if(rate==0)
		return col1;
	if(rate==256)
		return col2;
	short r1,g1,b1, r2,g2,b2, r3,g3,b3;
	r1 = (col1 >> 5) & 31;
	g1 = (col1 >> 10) & 31;
	b1 =  col1 & 31;
	r2 = (col2 >> 5) & 31;
	g2 = (col2 >> 10) & 31;
	b2 =  col2 & 31;
	short nr=256-rate;
	r3 = ((r1 * nr + r2 * (short)rate + 128) >> 8);
	g3 = ((g1 * nr + g2 * (short)rate + 128) >> 8);
	b3 = ((b1 * nr + b2 * (short)rate + 128) >> 8);
	if (r1==r3 && g1==g3 && b1==b3)
	{
		if (r2<r3) r3--; else if (r2>r3) r3++;
		if (g2<g3) g3--; else if (g2>g3) g3++;
		if (b2<b3) b3--; else if (b2>b3) b3++;
	}
	return (g3 << 10) + (r3 << 5) + b3;
}



static	char	*eibuf = NULL, *eibufbak = NULL;
static	int		xsize,ysize;
static	int		xbytes;
#define	IMAGE32K		0
#define	IMAGE16			1
static	int		imagetype;	// IMAGE32K, IMAGE16
static	bool	dispsw;


int EIMnew(int type, int xlen, int ylen)
// type: 0=32Kcolors 1=16colors
// xlen,ylen: 編集画像の大きさ(最小(320,240)(このときは1倍表示不可))
{
	eibuf = malloc(xlen*ylen*2);
	eibufbak = malloc(xlen*ylen*2);
	memset(eibuf,0,xlen*ylen*2);
	memset(eibufbak,0,xlen*ylen*2);
	xsize = xlen, ysize = ylen;
	xbytes = xsize*2;
	imagetype = IMAGE32K;
	dispsw = YES;
	return 0;
}

void EIMdelete()
{
}


int EIMresize(int xlen,int ylen)
{
	return 0;
}

int EIMgetxbytes()
{
	return xbytes;
}


char *EIMadrs(int x,int y)
{
	if (imagetype == IMAGE32K)
		return eibuf + xbytes*y + x*2;
	else if (imagetype == IMAGE16)
		return eibuf + xbytes*y + x/2;
}

char *EIMadrs_back(int x,int y)
{
	if (imagetype == IMAGE32K)
		return eibufbak + xbytes*y + x*2;
	else if (imagetype == IMAGE16)
		return eibufbak + xbytes*y + x/2;
}

int	EIMgetxsize()
{
	return xsize;
}

int EIMgetysize()
{
	return ysize;
}


void EIMdispon()
{
	dispsw = YES;
	DMimage_refresh();
}


void EIMdispoff()
{
	dispsw = NO;
}


void EIMpset(int x, int y, int col, int op)
{
	if (x < 0 || xsize <= x || y < 0 || ysize <= y)
		return;
	if (mask_chkxy(x,y))
		return;
	if (op == DrawNORMAL)
	{
		*(unsigned short*)(eibuf + xbytes*y + x*2) = col;
		if (dispsw)
			DMimage_pset(x,y,col,DrawNORMAL);
	}
	else if (op == DrawXOR)
	{
		*(unsigned short*)(eibuf + xbytes*y + x*2) ^= col;
		if (dispsw)
			DMimage_pset(x,y,col,DrawXOR);
	}
}


void EIMgraypset(int x,int y,int col,int gray)
{
	if (gray == 256)
	{
		EIMpset(x,y,col,DrawNORMAL);
		return;
	}
	if (mask_chkxy(x,y))
		return;
	short *p = (short*)(eibuf + xbytes*y + x*2);
	col = graycol(*p, col, gray);
	*p = col;
	if (dispsw)
		DMimage_pset(x,y,col,DrawNORMAL);
}

void EIMgrayhline(int x1,int x2,int y,int col,int gray,bool conc_sw)
{
	if (gray == 256)
	{
		EIMhline(x1,x2,y,col,DrawNORMAL);
		return;
	}
	if (x1 > x2)
		swap(x1,x2);
	short *p0 = (short*)(eibuf + xbytes*y + x1*2);
	if (!conc_sw || mixrate==256)
	{
		void hline(int _x1, int _x2, int _y)
		{
			short *p = p0 + _x1 - x1;
			for (int i=_x1; i<=_x2; i++,p++)
				*p = graycol(*p, col, gray);
		}
		hline_func(x1,x2,y,hline);
	}
	else
	{
		short *back_p0 = (short*)(eibufbak + xbytes*y + x1*2);
		char *cbufp;
		int py = -1;
		int mixr0 = getmixrate();
		void hline(int _x1, int _x2, int _y)
		{
			if (_y != py)	// 濃度バッファ内でのアドレスの再計算の必要ありや？
				cbufp = cbuf_adrs(x1,_y), py=_y;
			short *p=p0+_x1-x1, *bp=back_p0+_x1-x1;
			for (int i=_x1; i<=_x2; i++,p++,bp++)
			{
				int t = cbuf_mix(cbuf2conc(cbufp[i-x1]), gray, mixr0);
				cbufp[i-x1] = conc2cbuf(t);
				*p = mixcol(*bp,col,t,NO);
				if (*p==*bp)
				{
					int oc = mixcol(*bp,col,mixr0,NO);
					int r=getR(*p),g=getG(*p),b=getB(*p);
					if(r<getR(oc)) r++; else if(r>getR(oc)) r--;
					if(g<getG(oc)) g++; else if(g>getG(oc)) g--;
					if(b<getB(oc)) b++; else if(b>getB(oc)) b--;
					*p = r*32+g*1024+b;
				}
			}
		}
		hline_func(x1,x2,y,hline);
	}
	if (dispsw)
		DMimage_hline_map(x1,x2,y,(char*)p0);
}

void EIMgrayhline_map(int x1,int x2,int y,int col,short int *graymap,
					  bool conc_sw)
{
	if (x1 > x2)
		swap(x1,x2);
	short *p0      = (short*)(eibuf + xbytes*y + x1*2);
	short *back_p0 = (short*)(eibufbak + xbytes*y + x1*2);
	if (!conc_sw)
	{
		void hline(int _x1, int _x2, int _y)
		{
			short *p = p0 + _x1-x1, *gp = graymap + _x1-x1;
			for (int i=_x1; i<=_x2; i++,p++,gp++)
				*p = (*gp==256?col:*gp==0?*p:graycol(*p, col, *gp));
		}
		hline_func(x1,x2,y,hline);
	}
	else
	{
		char *cbufp;
		int py = -1;
		int mixr0 = getmixrate();
		void hline(int _x1, int _x2, int _y)
		{
			if (_y != py)	// 濃度バッファ内でのアドレスの再計算の必要ありや？
				cbufp = cbuf_adrs(x1,_y), py = _y;
			short *p = p0 + _x1-x1, *bp = back_p0 + _x1-x1;
			short *gp = graymap + _x1-x1;
			for (int i=_x1; i<=_x2; i++,p++,bp++,gp++)
			{
				if(*gp==0) continue;
				int t = cbuf_mix(cbuf2conc(cbufp[i-x1]), *gp, mixr0);
				cbufp[i-x1] = conc2cbuf(t);
				*p = mixcol(*bp,col,t,NO);
				if (*gp != 0 && *p == *bp)
				{
					int oc = mixcol(*bp,col,mixr0,NO);
					int r=getR(*p),g=getG(*p),b=getB(*p);
					if      (r<getR(oc))	r++;
					else if (r>getR(oc))	r--;
					if      (g<getG(oc))	g++;
					else if (g>getG(oc))	g--;
					if      (b<getB(oc))	b++;
					else if (b>getB(oc))	b--;
					*p = r*32+g*1024+b;
				}
			}
		}
		hline_func(x1,x2,y,hline);
	}
	if (dispsw)
		DMimage_hline_map(x1,x2,y,(char*)p0);
}


void EIMline(int x1,int y1,int x2,int y2,int col,int op)
{
	int x,y,xr,yr,r;
	xr = abs(x2-x1);  yr = abs(y2-y1);
	if (xr == 0 && yr == 0)
		EIMpset(x1,y1,col,op);
	else if (xr > yr)
	{
		r = (yr * 65536) / xr;
		if (x2 < x1)
			{ swap(x1,x2);   swap(y1,y2); }
		if (y1 > y2)
			r = -r;
		x2 = _min(x2,xsize-1);
		for (x=x1,y=(y1<<16)+0x8000; x<=x2; x++,y+=r)
			EIMpset(x,y>>16,col,op);
	}
	else
	{
		r = (xr << 16) / yr;
		if (y2 < y1)
			{ swap(x1,x2); swap(y1,y2); }
		if (x1 > x2)
			r = -r;
		y2 = _min(y2,ysize-1);
		for (y=y1,x=(x1<<16)+0x8000; y<=y2; y++,x+=r)
			EIMpset((x>>16),y,col,op);
	}
}


void EIMhline(int x1,int x2,int y,int col,int op)	// 水平直線を描く
{
	if (x1>x2)
		swap(x1,x2);
	int ix = x1;
	short *p = (short*)EIMadrs(x1,y);
	void hline(int _x1,int _x2,int _y)
	{
		if (op == DrawXOR)
			MEMstoreword_xor(getds(),(char*)(p+_x1-x1),col,(_x2-_x1+1));
		else
			MEMstoreword(getds(),(char*)(p+_x1-x1),col,(_x2-_x1+1));
		if (dispsw)
			DMimage_hline(_x1,_x2,_y,col,op);
	}
	hline_func(x1,x2,y,hline);
}


void EIMvline(int x,int y1,int y2,int col,int op)	// 垂直直線を描く
{
	if (y1>y2)
		swap(y1,y2);
	char *p = EIMadrs(x,y1);
	int i;
	if (op == DrawXOR)
	{
		int l = y2-y1+1;
		for (i=0; i<l; i++,p+=xbytes)
			if (!mask_chkxy(x,y1+i))
				{ *(short*)p ^= col; if (dispsw) DMimage_pset(x,y1+i,col,op); }
	}
	else
	{
		int l = y2-y1+1;
		for (i=0; i<l; i++,p+=xbytes)
			if (!mask_chkxy(x,y1+i))
				{ *(short*)p = col; if (dispsw) DMimage_pset(x,y1+i,col,op); }
	}
	//if (dispsw)
	//	DMimage_vline(x,y1,y2,col,op);
}


void EIMboxline(int x1,int y1,int x2,int y2,int col,int op)	// 矩形(枠)を描く
{
	if (x1 > x2)
		swap(x1,x2);
	if (y1 > y2)
		swap(y1,y2);
	EIMhline(x1,x2,y1,col,op);
	if (y2 > y1)
		EIMhline(x1,x2,y2,col,op);
	if (y2 > y1+1)
		EIMvline(x1,y1+1,y2-1,col,op),  EIMvline(x2,y1+1,y2-1,col,op);
}


void EIMrboxline(int x,int y,int xlen,int ylen,int col,int op)	// 矩形(枠)を描く
{
	EIMboxline(x,y,x+xlen-1,y+ylen-1,col,op);
}


void EIMrboxfill(int x1,int y1,int xlen,int ylen,int col,int op)
{
	int i;
	for (i=0; i<ylen; i++)
		EIMhline(x1,x1+xlen-1,y1+i,col,op);
}


void EIMboxfill(int x1,int y1,int x2,int y2,int col,int op)
{
	if (x1 > x2)
		swap(x1,x2);
	if (y1 > y2)
		swap(y1,y2);
	EIMrboxfill(x1,y1,x2-x1+1,y2-y1+1,col,op);
}


int EIMpoint(int x,int y)
{
	if (x < 0 || y < 0 || xsize <= x || ysize <= y)
		return backcol;
	return (int)*(unsigned short*)(eibuf + xbytes*y + x*2);
}


int EIMpoint_back2(int x,int y)
// x,y : 16ビット固定小数点
{
	if (x < 0 || y < 0 || xsize <= (x>>16) || ysize <= (y>>16))
		return backcol;
	char *ep = eibufbak + xbytes*(y>>16) + (x>>16)*2;
	int px1,px2,py1,py2,w[4],c[4];
	px2 = (x>>8)&0xff;  px1 = 256-px2;
	py2 = (y>>8)&0xff;  py1 = 256-py2;
	x>>=16,y>>=16;
	w[0]=w[1]=w[2]=w[3]=0;
	if (0<=y && y<ysize)
	{
		if (0<=x && x<xsize)
			{ if ((w[0] = (px1*py1+128)>>8) > 0)   c[0] = *(short*)ep; }
		if (0<=x+1 && x+1<xsize)
			{ if ((w[1] = (px2*py1+128)>>8) > 0)   c[1] = *(short*)(ep+2); }
	}
	if (0<=y+1 && y+1<ysize)
	{
		if (0<=x && x<xsize)
			{ if ((w[2] = (px1*py2+128)>>8) > 0) c[2] = *(short*)(ep+xbytes); }
		if (0<=x+1 && x+1<xsize)
			{ if ((w[3] = (px2*py2+128)>>8)>0) c[3] = *(short*)(ep+xbytes+2); }
	}
	int mag,rgb[3],i;
	mag = 0, rgb[0]=rgb[1]=rgb[2]=0;
	for (i=0; i<4; i++)
	{
		if (w[i] > 0)
		{
			int ci=c[i],wi=w[i];
			mag+=wi;
			rgb[0]+=getR(ci)*wi, rgb[1]+=getG(ci)*wi, rgb[2]+=getB(ci)*wi;
		}
	}
	int h = mag/2;
	return GRB((rgb[1]+h)/mag, (rgb[0]+h)/mag, (rgb[2]+h)/mag);
}


int EIMpoint_back(int x,int y)
{
	if (x < 0 || y < 0 || xsize <= x || ysize <= y)
		return backcol;
	return (int)*(unsigned short*)(eibufbak + xbytes*y + x*2);
}


void EIMgetblock(char *buf, int x,int y,int xlen,int ylen)
{
	int i;  char *p;
	p = buf;
	for (i=0; i<ylen; i++,p+=xlen*2)
	{
		// short *ep = (short*)EIMadrs(x,y+i);
		// for (j=0; j<xlen; j++)
		// 	*(short*)(p+j*2) = *(ep+j);
		memcpy(p, EIMadrs(x,y+i), xlen*2);
	}
}

void EIMputblock(int x,int y,int xlen,int ylen, char *buf, int op)
{
	int i; char *p;
	p = buf;
	for (i=0; i<ylen; i++,p+=xlen*2)
	{
		int yi = y+i;
		if (yi < 0 || ysize <= yi)
			continue;
		int x1 = _max(x,0), x2 = _min(x+xlen-1,xsize-1);
		short *pp = (short*)EIMadrs(x1,yi);
		void hline(int _x1, int _x2, int _y)
			{ memcpy(pp + _x1-x1, p + (_x1-x)*2, (_x2-_x1+1)*2); }
		hline_func(x1,x2,yi, hline);
	}
	if (dispsw)
		DMimage_refresh();
}


void EIMbackup()		// 現在の編集画像をアンドゥーバッファに転送する
{
	memcpy(eibufbak, eibuf, xsize*ysize*2);
}


void EIMloadbackup()	// アンドゥーバッファを現在の編集画像に
{
	memcpy(eibuf, eibufbak, xsize*ysize*2);
	if (dispsw)
		DMimage_refresh();
}
