/*
	ARTemis (Graphic Editor for FM-TOWNS)
	(c) MATSUUCHI Ryosuke 1992,1993

	subgrp.c

	補助グラフィック関数群


★描画
*	void	mist(int x,int y,int xlen,int ylen,int col)
	void	aapset(int x, int y, int col, int gray)	//(x,y):16bit fixed-point
*	void	aaline(int x1,int y1,int x2,int y2,int col,pen_t *pen,int gray)
*	void	do_paint(int x,int y)
*	void	psetWithPen(int x,int y,int col,pen_t *pen,bool conc_sw)
*	void	lineWithPen(int x1,int y1,int x2,int y2,int col,pen_t *pen,
						bool first)
*	void	inverse(int x,int y,int xlen,int ylen)

★特殊描画
	void	plot_mat3(int x, int y, int mat[3][3])
	void	plot_diffuse(int x, int y)
	void	plot_sharp(int x,int y)
	void	plot_sand(int x,int y)
	void	plot_airbrush_start(void)
	void	plot_airbrush(int x,int y)
*	void	plot_pen_func(int x,int y,pen_t *pen, void (*func)(int x,int y))
*	void	plot_pen_diffuse(int x, int y, pen_t *pen)
*	void	plot_pen_sand(int x,int y, pen_t *pen)
*	void	blot_plot(int x, int y, int color, int branch, int depth)
*	void	blot2_plot(int x0, int y0, int color, int branch, int depth)

★描画アルゴリズム
*	void	do_line(int x1,int y1,int x2,int y2,void func(int x,int y)!)
*	void	do_boxline(int x1,int y1,int x2,int y2,void func(int x,int y)!)
*	void	do_boxfill(int x1,int y1,int x2,int y2,
					   void func(int x1,int x2,int y)!)
*	void	do_ellipse(int x,int y,int rx,int ry,void func(int x,int y)!)
*	void	do_ellipsefill(int x,int y,int rx,int ry,
						   void func(int x1,int x2,int y)!)

★各種コマンド
	void	commandPFline_sub(int cmd)
*	void	commandPFline()
*	void	commandPFPset()
*	void	commandBlot()
*	void	commandDiffuse()
*	void	commandSand()
*	void	commandGoshi()
*	void	commandPaint()
*	void	commandFreeTest()
	void	commandPolygon_sub(void	func()!, bool eachbackup)
			// func : (x1,x2,y) の引数で呼び出される
*	void	commandDiffuseArea()
*	void	commandSharp()
*	void	commandSandArea()
*	void	cmd_polygon()

★文字描画
	void	grp_putchar_sub(char *dest, char *src, bool kanji, int col)
	void	grp_putchar(int x, int y, int c, int col, int style)
*	void    ART_putstr_style(int x, int y, char *str, int col, int style)
*	void    ART_putstr(int x, int y, char *str, int col)
*	void	ARTputstr12(int x,int y, char *str, int forecol, int backcol)

★計算
*	int		mixcol(int col1, int col2, int rate, bool rnd)
*	int		my_rand()

*/


// アンチエイリアス描画をするかしないか
#define	NO_AA

#include <stdio.h>
#include <egb.h>
#include <stdlib.h>
#include <msdos.cf>
#include <fnt.h>
#include <ctype.h>
#include <memory.h>
#include <limits.h>

#include "ge.h"
#include "plt16.h"
#include "math2.h"
#include "decimal.h"
#include "imageman.h"
#include "dispman.h"

/*--------------------------------------------------------*/
/*                     網目模様の描画                     */
/*--------------------------------------------------------*/


void mist(int x,int y,int xlen,int ylen,int col)	// 網目模様の描画
{
	int i;
	for (i=0; i<ylen-xlen; i+=2)
		gline(x,y+i,x+xlen-1,y+xlen-1+i,col,DrawNORMAL);
	for ( ; i<ylen; i+=2)
		gline(x,y+i,x+ylen-1-i,y+ylen-1,col,DrawNORMAL);
	for (i=2; i<xlen-ylen; i+=2)
		gline(x+i,y,x+ylen-1+i,y+ylen-1,col,DrawNORMAL);
	for ( ; i<xlen; i+=2)
		gline(x+i,y,x+xlen-1,y+xlen-1-i,col,DrawNORMAL);
}


void	inverse(int x,int y,int xlen,int ylen)	// 白黒反転
{
	if(DMgetifonepage())
	{
		grboxfill(x,y,xlen,ylen,menu_plt(White),DrawXOR);
	}
	else
	{
		int white = menu_plt(White), black = menu_plt(Black);
		int i,j;
		for(i=0; i<ylen; i++)
		{
			for(j=0; j<xlen; j++)
			{
				int c = gpoint(x+j,y+i);
				if (c==white) gpset(x+j,y+i,black,DrawNORMAL);
				if (c==black) gpset(x+j,y+i,white,DrawNORMAL);
			}
		}
	}
}


/*--------------------------------------------------------*/
/*              アンチエイリアシング直線描画              */
/*--------------------------------------------------------*/


#ifndef NO_AA

static void aapset(int x, int y, int col, int gray)
// (x,y) : 16bit fixed-point 座標
{
	x = (x+128)>>8;		// 8bit fixed-point へ変換
	y = (y+128)>>8;
	int x1,x2,y1,y2,h1,h2,v1,v2,a11,a12,a21,a22;
	x1 =  x &  0xffffff00;
	x2 = x1 + 0x100;
	y1 =  y &  0xffffff00;
	y2 = y1 + 0x100;
	h1 = x2 - x;
	h2 = x - x1;
	v1 = y2 - y;
	v2 = y - y1;
	a11 = (h1*v1)>>8;
	a12 = (h1*v2)>>8;
	a21 = (h2*v1)>>8;
	a22 = (h2*v2)>>8;
	x1 >>= 8;
	y1 >>= 8;
	x2 >>= 8;
	y2 >>= 8;
	if (a11 > 0)
	{
		if (a11 >= 256)
			gpset(x1,y1,col,DrawNORMAL);
		else
			EIMgraypset(x1,y1,col,a11);
	}
	if (a21 > 0)
	{
		if (a21 >= 256)
			gpset(x2,y1,col,DrawNORMAL);
		else
			EIMgraypset(x2,y1,col,a21);
	}
	if (a12 > 0)
	{
		if (a12 >= 256)
			gpset(x1,y2,col,DrawNORMAL);
		else
			EIMgraypset(x1,y2,col,a12);
	}
	if (a22 > 0)
	{
		if (a22 >= 256)
			gpset(x2,y2,col,DrawNORMAL);
		else
			EIMgraypset(x2,y2,col,a22);
	}
}

#endif


void aaline(int x1, int y1, int x2, int y2, int col, pen_t *pen, int gray)
{
#ifdef NO_AA
	lineWithPen(x1,y1,x2,y2,col,pen,YES);
#else
	int dx,dy,r;
	dx = _abs(x2-x1);
	dy = _abs(y2-y1);
	if (dx==0 && dy==0)
	{
		gpset(x1,y1,col,DrawNORMAL);
		return;
	}
	if (dx < dy)
	{
		r = (dx << 16) / dy;
		if (y1 > y2)
			{ swap(y1,y2);  swap(x1,x2); }
		if (x1 > x2)
			r = -r;
		int iy,ix;
		ix = x1<<16;
		for (iy = y1; iy <= y2; iy++,ix+=r)
			aapset(ix,iy<<16,col,256);
	}
	else
	{
		r = (dy << 16) / dx;
		if (x1 > x2)
			{ swap(y1,y2);  swap(x1,x2); }
		if (y1 > y2)
			r = -r;
		int iy,ix;
		iy = y1<<16;
		for (ix = x1; ix <= x2; ix++,iy+=r)
			aapset(ix<<16,iy,col,256);
	}
#endif
}


/*--------------------------------------------------------*/
/*                    ペイントルーチン                    */
/*--------------------------------------------------------*/

typedef struct {
	short int  x,y;
	short int  px1,px2,py;
	bool mark;
} STACK;

static STACK *check(int px1,int px2,int py,int sx1,int sx2,int sy,int y,int border, STACK *stack, int stacksize, STACK *sp)
// 新しい sp を返す
{
	int x;
	bool in_border;
	if (y<0 || EIMgetysize()<=y)
		return sp;
	in_border = YES;
	for (x=sx1; x<=sx2; x++) {
		if (in_border) {
			if (EIMpoint(x,y)==border) {
				if (!(y==py&&px1<=x&&x<=px2) && sp+1 < stack+stacksize) {
					sp++;
					sp->x = x;
					sp->y = y;
					sp->px1 = sx1;
					sp->px2 = sx2;
					sp->py = sy;
					sp->mark = TRUE;
				}
				in_border = NO;
			}
		} else {
			if (EIMpoint(x,y) != border)
				in_border = YES;
		}
	}
	return sp;
}

static void delete(int sx1,int sx2,int sy,STACK *oldsp, STACK *stack, STACK *sp)
{
	STACK *p,*q;
	for (p=stack; p<=oldsp; p++) {
		if (p->y == sy && sx1 <= p->x && p->x <= sx2) {
			p->mark = NO;
			for (q = oldsp+1; q<=sp; q++) {
				if (q->y == p->py && p->px1 <= q->x && q->x <= p->px2)
					q->mark = FALSE;
			}
		}
	}
}

static void _do_paint(int x,int y,int col)
{
	#define STACKSIZE 600
	STACK stack[STACKSIZE];
	STACK *sp;

	STACK *oldsp;
	int sx,sx1,sx2,sy;
	int px1,px2,py;
	int border;
	int mix = getmixrate();
	
	border = EIMpoint(x,y);
	sp = stack;
	sp->x = x;
	sp->y = y;
	sp->px1 = 0;
	sp->px2 = 0;
	sp->py = 0;
	sp->mark = YES;
	while (sp >= stack) {
		if (sp->mark) {
			sx = sp->x;
			sy = sp->y;
			px1 = sp->px1;
			px2 = sp->px2;
			py = sp->py;
			short *ip;
			for (sx1=sx,ip=(short*)EIMadrs(sx1,sy);
				 sx1 > 0 && *(ip-1) == border;
				 sx1--,ip--)
				;
			for (sx2=sx,ip=(short*)EIMadrs(sx2,sy);
				 sx2 < EIMgetxsize()-1 && *(ip+1) == border;
				 sx2++,ip++)
				;
			EIMgrayhline(sx1,sx2,sy,col,mix,NO);
			oldsp = --sp;
			sp=check(px1,px2,py,sx1,sx2,sy,sy+1,border, stack, STACKSIZE, sp);
			sp=check(px1,px2,py,sx1,sx2,sy,sy-1,border, stack, STACKSIZE, sp);
			delete(sx1,sx2,sy,oldsp, stack,sp);
		} else
			sp--;
	}
}

void do_paint(int x,int y)
{
	_do_paint(x,y,forecol);
}


/*--------------------------------------------------------*/
/*                        混色処理                        */
/*--------------------------------------------------------*/


	/* 新しいrand()が合わなくなったんで自家製に切り換え 1993 3/6 */
int my_rand()	// by 戸田 浩
{
	static unsigned int next = 1 ;
	next = next * 1103515245 + 12345 ;
	return (unsigned int)( next / 65536 ) % 32768 ;
}


int mixcol(int col1, int col2, int rate, bool rnd)
// rnd : 乱数によりディザリングするかどうかのスイッチ
{
	short int r1,g1,b1, r2,g2,b2;
	if (rate == 0)
		return col1;
	if (rate == 256)
		return col2;
	r1 = (col1 >> 5) & 31;
	g1 = (col1 >> 10) & 31;
	b1 =  col1 & 31;
	r2 = (col2 >> 5) & 31;
	g2 = (col2 >> 10) & 31;
	b2 =  col2 & 31;
	if (!rnd)
	{
		short int nr = 256-rate;
		r1 = ((r1 * nr + r2 * (short int)rate + 128) >> 8);
		g1 = ((g1 * nr + g2 * (short int)rate + 128) >> 8);
		b1 = ((b1 * nr + b2 * (short int)rate + 128) >> 8);
	}
	else
	{
#define	R	((short int)my_rand() & 255)
		short int nr = 256-rate;
		r1 = ((r1 * nr + r2 * (short int)rate + R) >> 8);
		g1 = ((g1 * nr + g2 * (short int)rate + R) >> 8);
		b1 = ((b1 * nr + b2 * (short int)rate + R) >> 8);
#undef R
	}
	return (g1 << 10) + (r1 << 5) + b1;
}


/*--------------------------------------------------------*/
/*                      濃淡付きPSET                      */
/*--------------------------------------------------------*/


void psetWithPen(int x,int y,int col,pen_t *pen,bool conc_sw)
{
	if (mixrate == 256)
		conc_sw = NO;
	void hdraw(int x0,int y0,int dx,int xlen,int dy, short *graymap)
	{
		EIMgrayhline_map(x0+dx,x0+dx+xlen-1,y0+dy,col,graymap,conc_sw);
	}
	pen_fill(pen, hdraw, x,y,YES);
}


/*--------------------------------------------------------*/
/*                       ぼかし処理                       */
/*--------------------------------------------------------*/


static void plot_mat3(int x, int y, int mat[3][3])
{
	if (mode != MODE32K)
		return;
	int max_x, max_y;
	max_x = EIMgetxsize() - 1;
	max_y = EIMgetysize() - 1;
	if (x < 0 || y < 0 || max_x < x || max_y < y)
		return;
	int total[3]; // ＧＲＢ各要素の強さの合計
	int ix,iy;    // x,y について -1,0,1 と変化する変位
	int ix1,ix2,iy1,iy2;
	total[0] = total[1] = total[2] = 0;
	ix1 = _max(x-1,0    ) - x;
	ix2 = _min(x+1,max_x) - x;
	iy1 = _max(y-1,0    ) - y;
	iy2 = _min(y+1,max_y) - y;
	char *ep0,*ep;  int linsiz;
	ep0 = EIMadrs_back(x+ix1, y+iy1);
	linsiz = EIMgetxbytes();
	for (iy=iy1; iy<=iy2; iy++,ep0+=linsiz)
	{
		ep = ep0;
		for (ix=ix1; ix<=ix2; ix++,ep+=2)
		{
			int c = *(short*)ep;
			int p = mat[iy+1][ix+1];
			total[0] += getG(c) * p;
			total[1] += getR(c) * p;
			total[2] += getB(c) * p;
		}
	}
#define	COL_AVR(a)	((total[a] + 128) >> 8)
	int t1,t2,t3;
	t1 = _lim(COL_AVR(0),0,31);
	t2 = _lim(COL_AVR(1),0,31);
	t3 = _lim(COL_AVR(2),0,31);
	EIMpset(x, y, GRB(t1,t2,t3), DrawNORMAL);
#undef COL_AVR
}


static void plot_diffuse(int x, int y)
// １ドットに対してボカシを実行する
{
	static int mat[3][3] = {{23,23,23},{23,72,23},{23,23,23}};
	plot_mat3(x,y,mat);
}


static void plot_sharp(int x,int y)
{
	static int mat[3][3] = {{-16,-16,-16},{-16,384,-16},{-16,-16,-16}};
	plot_mat3(x,y,mat);
}


/*--------------------------------------------------------*/
/*                         砂処理                         */
/*--------------------------------------------------------*/


static void plot_sand(int x,int y)
{
	int i,c,grb[3];
	c = EIMpoint_back(x,y);
	grb[0]=getG(c), grb[1]=getR(c), grb[2]=getB(c);
	int r;
	r = rand() % 3;
	for (i=0; i<3; i++)
	{
		switch(r)
		{
		case 0:
			if (grb[i] > 0)
				grb[i]--;
			break;
		case 1:
			if (grb[i] < 31)
				grb[i]++;
			break;
		}
	}
	c = GRB(grb[0],grb[1],grb[2]);
	EIMpset(x,y,c,DrawNORMAL);
}


/*--------------------------------------------------------*/
/*             ペン先について任意の処理を行う             */
/*--------------------------------------------------------*/


static void plot_pen_func(int x, int y, pen_t *pen, void (*func)(int x,int y))
{
	void hline(int x0,int y0,int dx,int xlen,int dy, short *graymap)
	{
		int i;
		for (i=0; i<xlen; i++)
			func(x0+dx+i, y0+dy);
	}
	pen_fill(pen, hline, x,y, YES);
}


void plot_pen_diffuse(int x, int y, pen_t *pen)
// ペンの形状にあわせた「ボカシ」を実行する
{
	plot_pen_func(x,y,pen,plot_diffuse);
}


void plot_pen_sand(int x,int y, pen_t *pen)
// ペンの形状にあわせた「ザラザラ化」を実行する
{
	plot_pen_func(x,y,pen,plot_sand);
}


void lineWithPen(int x1,int y1,int x2,int y2,int col,pen_t *pen,bool first)
// first : 最初の点を描画するかどうか
{
	int xr,yr,r,x,y;
	xr = _abs(x2-x1), yr = _abs(y2-y1);
	if (first)
		psetWithPen(x1,y1,col,pen,YES);
	if (xr==0 && yr==0)
		;
	else if (xr > yr)
	{
		r = (yr<<16) / xr;
		if (y1 > y2)
			r = -r;
		if (x1 < x2)
		{
			for (x=x1+1,y=(y1<<16)+0x8000+r; x<=x2; x++,y+=r)
				psetWithPen(x,(y>>16),col,pen,YES);
		}
		else 
		{
			for (x=x1-1,y=(y1<<16)+0x8000+r; x>=x2; x--,y+=r)
				psetWithPen(x,(y>>16),col,pen,YES);
		}
	}
	else
	{
		r = (xr << 16) / yr;
		if (x1 > x2)
			r = -r;
		if (y1 < y2)
		{
			for (y=y1+1,x=(x1<<16)+0x8000+r; y<=y2; y++,x+=r)
				psetWithPen((x>>16),y,col,pen,YES);
		}
		else
		{
			for (y=y1-1,x=(x1<<16)+0x8000+r; y>=y2; y--,x+=r)
				psetWithPen((x>>16),y,col,pen,YES);
		}
	}
}


/*--------------------------------------------------------*/
/*                  「にじみペン」手続き                  */
/*--------------------------------------------------------*/

#define	BLOT_METHOD		1
	// 0=従来どおり（画面上で混色）  1=編集バッファと混色

void blot_plot(int x, int y, int color, int branch, int depth)
// ３万色編集時専用。
// branch : 枝分かれする数
// depth : 何回再帰するか
{
	if (mode != MODE32K || depth <= 0)
		return;
	int mixr,mixr0;
	mixr0 = getmixrate();
	int max_x, max_y;
	max_x = EIMgetxsize() - 1;
	max_y = EIMgetysize() - 1;
	void blot_plot_sub(int x, int y, int color, int branch, int depth,
					   deci grad, deci gradsub)
	{
		if (depth <= 0 || grad <= 0 ||
		    x < 0 || y < 0 || max_x < x || max_y < y)
		{
			return;
		}
		if (mixr0 == 256)
			EIMgraypset(x,y,color,DeciToInt(grad));
		else
		{
			char *cp;  int conc,t;
			conc = DeciToInt(grad);
			cp = cbuf_adrs(x,y);
			t = cbuf_mix(cbuf2conc(*cp), conc, mixr0);
			*cp = conc2cbuf(t);
			EIMpset(x,y,
					mixcol(EIMpoint_back(x,y),color,t,NO),
					DrawNORMAL);
		}
		int b;
		for (b = 0; b < branch; b++)
		{
			int nx,ny;
			nx = x + rand() % 3 - 1;
			ny = y + rand() % 3 - 1;
			blot_plot_sub(nx,ny,color,branch,depth-1,grad-gradsub,gradsub);
		}
	}
	if (mixr0 == 256)
		blot_plot_sub(x,y,color,branch,depth,IntToDeci(50),IntToDeci(50)/depth);
	else
	{
		mixr = mixr0 / 5;
		blot_plot_sub(x,y,color,branch,depth,IntToDeci(mixr),IntToDeci(mixr)/depth);
	}
}

/*--------------------------------------------------------*/
/*                   にじみペン part 2                    */
/*--------------------------------------------------------*/

void blot2_plot(int x0, int y0, int color, int branch, int depth)
// ３万色編集時専用。
// branch : 枝分かれする数
// depth : 何回再帰するか
{
	static int dxy[][2] = {
		{0,-1},	{1,-1},	{1,0},	{1,1},	{0,1},	{-1,1},	{-1,0},	{-1,-1}
	};
	if (mode != MODE32K || depth <= 0)
		return;
	int mixr,mixr0;
	mixr0 = getmixrate();
	int max_x, max_y;
	max_x = EIMgetxsize() - 1;
	max_y = EIMgetysize() - 1;
	void blot_plot_sub(int x, int y, int color, int branch, int depth,
					   deci grad, deci gradsub, int dir)
	{
		if (depth <= 0 || grad <= 0 ||
		    x < 0 || y < 0 || max_x < x || max_y < y)
		{
			return;
		}
		if (mixr0 == 256)
			EIMgraypset(x,y,color,DeciToInt(grad));
		else
		{
			char *cp;  int conc,t;
			conc = DeciToInt(grad);
			cp = cbuf_adrs(x,y);
			t = cbuf_mix(cbuf2conc(*cp), conc, mixr0);
			*cp = conc2cbuf(t);
			EIMpset(x,y,
					mixcol(EIMpoint_back(x,y),color,t,NO),
					DrawNORMAL);
		}
		int b;
		for (b = 0; b < branch; b++)
		{
			int d;
			do { d = rand() & 7; } while (d == dir);
			if (dir<0) dir=d;
			blot_plot_sub(x+dxy[d][0],y+dxy[d][1],
						  color, branch, depth-1, grad-gradsub, gradsub, dir);
		}
	}
	if (mixr0 == 256)
		blot_plot_sub(x0,y0,color,branch,depth,IntToDeci(50),IntToDeci(50)/depth,-1);
	else
	{
		mixr = mixr0 / 5;
		blot_plot_sub(x0,y0,color,branch,depth,IntToDeci(mixr),IntToDeci(mixr)/depth,-1);
	}
}

// ●エアブラシ

// static int ab_r,ab_x,ab_y,ab_c;
static short *airbrush_buf = NULL;

static int airbrush_init(void)
{
	if (airbrush_buf != NULL)
		free(airbrush_buf);
	airbrush_buf = calloc(2,(spray_r*2+1)*(spray_r*2+1));
	if (airbrush_buf == NULL)
		return -1;
	int x,y;
	int idx=0;
	if (spray_r > 0)
	{
		for (y=-spray_r; y<=spray_r; y++)
			for(x=-spray_r; x<=spray_r; x++,idx++)
			{
				deci r = ID(x*x+y*y) / (spray_r*spray_r);
				if (r<=ID(1))
				{
					airbrush_buf[idx] = 32 - DI(32*r);
				}
			}
	}
	else
		airbrush_buf[0] = 32;
	return 0;
}

static void airbrush_end(void)
{
	free(airbrush_buf);
	airbrush_buf = NULL;
}

void plot_airbrush_start(void)
{
}

void plot_airbrush(int x, int y)
{
	int maxx,maxy;
	maxx = EIMgetxsize()-1;
	maxy = EIMgetysize()-1;
	int x1,y1,x2,y2,xl,x0,y0;
	x1 = _max(0, (x0 = x - spray_r));
	y1 = _max(0, (y0 = y - spray_r));
	x2 = _min(maxx, x + spray_r);
	y2 = _min(maxx, y + spray_r);
	xl = spray_r * 2 + 1;
	int i;
	int idx = xl * (y1-y0) + (x1-x0);
	for (i=y1; i<=y2; i++,idx+=xl)
	{
		EIMgrayhline_map(x1,x2,i,forecol,&airbrush_buf[idx],YES);
	}
}

/*--------------------------------------------------------*/
/*                    描画アルゴリズム                    */
/*--------------------------------------------------------*/

void do_line(int x1,int y1,int x2,int y2,void func(int x,int y)!)
{
	int xr,yr,r,x,y;
	xr=abs(x2-x1),yr=abs(y2-y1);
	func(x1,y1);	// 最初の点
	if (xr==0 && yr==0)
		;
	else if (xr > yr)
	{
		r=(yr<<16)/xr; if(y1>y2) r=-r;
		if (x1 < x2)
			for (x=x1+1,y=(y1<<16)+0x8000+r; x<=x2; x++,y+=r)
				func(x,(y>>16));
		else 
			for (x=x1-1,y=(y1<<16)+0x8000+r; x>=x2; x--,y+=r)
				func(x,(y>>16));
	}
	else
	{
		r=(xr<<16)/yr; if(x1>x2) r=-r;
		if (y1 < y2)
			for (y=y1+1,x=(x1<<16)+0x8000+r; y<=y2; y++,x+=r)
				func((x>>16),y);
		else
			for (y=y1-1,x=(x1<<16)+0x8000+r; y>=y2; y--,x+=r)
				func((x>>16),y);
	}
}

void do_boxline(int x1,int y1,int x2,int y2,void func(int x,int y)!)
{
	int i;
	if (x2<x1) swap(x1,x2);
	if (y2<y1) swap(y1,y2);
	for(i=x1;i<=x2;i++) func(i,y1);
	if(y1<y2)
		for(i=x1;i<=x2;i++) func(i,y2);
	for(i=y1+1;i<y2;i++) func(x1,i);
	if(x1<x2)
		for(i=y1+1;i<y2;i++) func(x2,i);
}

void do_boxfill(int x1,int y1,int x2,int y2,void func(int x1,int x2,int y)!)
{
	int i;
	if (x2<x1) swap(x1,x2);
	if (y2<y1) swap(y1,y2);
	for(i=y1;i<=y2;i++) func(x1,x2,i);
}

void do_ellipse(int x,int y,int rx,int ry,void func(int x,int y)!)
{
#if 0
	#define	ELLIPSE(RX,RY,X0,Y0,X1,Y1)							\
		{														\
			int x0=RX,s=x0;										\
			int y0=0;											\
			int px0=-1,py0=-1,px1=-1,py1=-1;					\
			while(x0>=y0)										\
			{													\
				int x1=x0*RY/RX;						\
				int y1=y0*RY/RX;						\
				if(px0!=X0||py1!=Y1)					\
				{													\
					func(x+X0,y+Y1);if(X0!=0)func(x-X0,y+Y1);		\
					if (Y1!=0)										\
					 { func(x+X0,y-Y1);if(X0!=0)func(x-X0,y-Y1); }	\
				}													\
				if (py0!=Y0||px1!=X1)								\
				{												\
					func(x+Y0,y+X1);if(Y0!=0)func(x-Y0,y+X1);		\
					if (X1!=0)										\
					 { func(x+Y0,y-X1);if(Y0!=0)func(x-Y0,y-X1); }	\
				}												\
				s-=2*y0+1; if(s<0)s+=2*(x0-1),x0--;				\
				y0++;											\
				px0=X0,py0=Y0,px1=X1,py1=Y1;					\
			}													\
		}
	#
	if (rx==0 && ry==0)
		func(x,y);
	else if (rx>ry)
		ELLIPSE(rx,ry,x0,y0,x1,y1)
	else
		ELLIPSE(ry,rx,x1,y1,x0,y0)
	#undef ELLIPSE
#endif
	if (rx==0 && ry==0)
		{ func(x,y); return; }
	int px,py,prey;
	int a2,b2;
	int c1,c2;
	int e;
	a2=rx*rx,b2=ry*ry;
	px=0,py=ry;
	c1=4*b2,c2=8*a2;
	e=a2*(1-4*ry);
	while(b2*px<=a2*py)
	{
		func(x+px,y+py); if(px!=0)func(x-px,y+py);
		func(x+px,y-py); if(px!=0)func(x-px,y-py);
		prey=py;
		e+=c1*(2*px+1),px++; if(e>0) e-=c2*py,py--;
	}
	px=rx,py=0;
	c1=4*a2,c2=8*b2;
	e=b2*(1-4*rx);
	while(a2*py<=b2*px && py<prey)
	{
		func(x+px,y+py); if(px!=0)func(x-px,y+py);
		if(py!=0) { func(x+px,y-py); if(px!=0)func(x-px,y-py); }
		e+=c1*(2*py+1),py++; if(e>0) e-=c2*px,px--;
	}
}

void do_ellipsefill(int x,int y,int rx,int ry,void func(int x1,int x2,int y)!)
{
#if 0
	#define	ELLIPSE(RX,RY,X0,Y0,X1,Y1)					\
		{												\
			int x0=RX,s=x0;								\
			int y0=0;									\
			int py0,py1;								\
			py0=py1=INT_MIN;							\
			while(x0>=y0)								\
			{											\
				int x1=x0*RY/RX;						\
				int y1=y0*RY/RX;						\
				if (y+Y1 != py0)						\
				{										\
					py0=y+Y1;							\
					func(x-X0,x+X0,py0);				\
					func(x-X0,x+X0,y-Y1);				\
				}										\
				if (y+X1 != py1)						\
				{										\
					py1=y+X1;							\
					func(x-Y0,x+Y0,py1);				\
					func(x-Y0,x+Y0,y-X1);				\
				}										\
				s-=2*y0-1; if(s<0)s+=2*(x0-1),x0--;		\
				y0++;									\
			}											\
		}
	#
	if (rx==0&&ry==0)
		func(x,x,y);
	else if (rx>ry)
		ELLIPSE(rx,ry,x0,y0,x1,y1)
	else
		ELLIPSE(ry,rx,x1,y1,x0,y0)
	#undef ELLIPSE
#endif
	if (rx==0 && ry==0)
		{ func(x,x,y); return; }
	int px,py,prey;
	int a2,b2;
	int c1,c2;
	int e;
	a2=rx*rx,b2=ry*ry;
	px=0,py=ry;
	c1=4*b2,c2=8*a2;
	e=a2*(1-4*ry);
	prey=-1;
	while(b2*px<=a2*py)
	{
		e+=c1*(2*px+1);
		if(e>0)
		{
			func(x-px,x+px,y+py); func(x-px,x+px,y-py);
			prey=py;
		 	e-=c2*py,py--;
		}
		px++;
	}
	px=rx,py=0;
	c1=4*a2,c2=8*b2;
	e=b2*(1-4*rx);
	while(a2*py<=b2*px && py<prey)
	{
		func(x-px,x+px,y+py);
		if(py!=0) func(x-px,x+px,y-py);
		e+=c1*(2*py+1),py++; if(e>0) e-=c2*px,px--;
	}
}

/*--------------------------------------------------------*/
/*                   「自由線」コマンド                   */
/*--------------------------------------------------------*/


extern void put_pencol(), get_pencol();


static void commandPFline_sub(int cmd)
// cmd 0:fline 1:pset
{
	if (cmd == 8)
	{
		if (airbrush_init() != 0)
			return;
	}
	bool drawing;
	int px,py;
	drawing = NO;
	for (;;)
	{
		DMdispcsr(ms.x,ms.y);
		do
		{
			ms_get(&ms);
		} while (ms.dx==0 && ms.dy==0 && ms.btn1==OFF && ms.btn2==OFF &&
				 key_chk() == 0);
		DMerasecsr();
		scrollForCsr(1,1);
		if (ms.btn2==OFFON)
			break;
		else if (ms.btn1==OFFON)
		{
			EIMbackup();
			drawing = YES;
			px = DMimage_getx(ms.x),  py = DMimage_gety(ms.y);
			switch (cmd)
			{
			case 0:
			case 1:
				cbuf_clear();
				psetWithPen(px,py,forecol,getcurpen(),YES);
				break;
			case 2:
				cbuf_clear();
				blot_plot(px,py,forecol,1,blot_depth);
				break;
			case 3:
				plot_pen_diffuse(px,py,getcurpen());
				break;
			case 4:
				plot_pen_sand(px,py,getcurpen());
				break;
			case 5:
				get_pencol(px,py,getcurpen());
				break;
			case 6:
				do_paint(px,py);
				break;
			case 7:	// freetest
				cbuf_clear();
				blot2_plot(px,py,forecol,1,blot_depth);
				break;
			case 8:
				cbuf_clear();
				plot_airbrush_start();
				plot_airbrush(px,py);
				break;
			}
		}
		else if (ms.btn1==ON)
		{
			if (drawing)
			{
				int nx,ny;
				nx = DMimage_getx(ms.x),  ny = DMimage_gety(ms.y);
				switch (cmd)
				{
				case 0:
					if (px!=nx||py!=ny)
					{
						if (ryosuke && pen_isdottype(getcurpen()))
							aaline(px,py,nx,ny,forecol,getcurpen(),256);
						else
							lineWithPen(px,py,nx,ny,forecol,getcurpen(),NO);
					}
					break;
				case 1:
					if (px!=nx||py!=ny)
						psetWithPen(nx,ny,forecol,getcurpen(),YES);
					break;
				case 2:	// にじみペン
					blot_plot(nx,ny,forecol,1,blot_depth);
					break;
				case 3: // ぼかしペン
					if (px!=nx||py!=ny)
						plot_pen_diffuse(nx,ny,getcurpen());
					break;
				case 4: // ザラザラペン
					if (px!=nx||py!=ny)
						plot_pen_sand(nx,ny,getcurpen());
					break;
				case 5: // 摩筆
					if (px!=nx||py!=ny)
						put_pencol(nx,ny,getcurpen());
					break;
				case 7:	// freetest
					blot2_plot(px,py,forecol,1,blot_depth);
					break;
				case 8:
					plot_airbrush(px,py);
					break;
				}
				px=nx;
				py=ny;
			}
		}
		else
			drawing = NO;
	}
	if (cmd == 8)
		airbrush_end();
}


void commandPFline()
{
	commandPFline_sub(0);
}


void commandPFPset()
{
	commandPFline_sub(1);
}


void commandBlot()
{
	commandPFline_sub(2);
}


void commandDiffuse()
{
	commandPFline_sub(3);
}


void commandSand()
{
	commandPFline_sub(4);
}


void commandGoshi()
{
	commandPFline_sub(5);
}


void commandPaint()
{
	commandPFline_sub(6);
}


void commandFreeTest()
{
	// commandPFline_sub(7); // blot2
	commandPFline_sub(8); // air-brush
}


static void commandPolygon_sub(void func()!, bool eachbackup)
// func : (x1,x2,y) の引数で呼び出される
{
	bool first = YES;
	for (;;)
	{
		if (area_input(AREA_POLYGON) != 0)
			break;
		if (eachbackup)
			EIMbackup();
		else
		{
			if (first)
				EIMbackup();
			first = NO;
		}
		int ax1,ay1,ax2,ay2;
		area_getboundxy(&ax1,&ay1,&ax2,&ay2);
		int x,y;
		for (y=ay1; y<=ay2; y++)
		{
			x = ax1 + area_chkxylen(ax1,ax2,y,YES);
			while (x<=ax2)
			{
				int l;
				if ((l = area_chkxylen(x,ax2,y,NO)) == 0)
					break;
				func(x,x+l-1,y);
				x += l + area_chkxylen(x+l,ax2,y,YES);
			}
		}
	}
}


void commandDiffuseArea()
{
	void diffuse_hline(int x1,int x2,int y)
	{
		int i;
		for (i=x1; i<=x2; i++)
			plot_diffuse(i,y);
	}
	commandPolygon_sub(diffuse_hline,YES);
}


void commandSharp()
{
	void sharp_hline(int x1,int x2,int y)
	{
		int i;
		for (i=x1; i<=x2; i++)
			plot_sharp(i,y);
	}
	commandPolygon_sub(sharp_hline,NO);
}


void commandSandArea()
{
	void sand_hline(int x1,int x2,int y)
	{
		int i;
		for (i=x1; i<=x2; i++)
			plot_sand(i,y);
	}
	commandPolygon_sub(sand_hline,YES);
}


void cmd_polygon()
{
	int mix = getmixrate();
	void hline(int x1,int x2,int y)
	{
		EIMgrayhline(x1,x2,y,forecol,mix,NO);
	}
	commandPolygon_sub(hline,YES);
}


/*--------------------------------------------------------*/
/*                        文字表示                        */
/*--------------------------------------------------------*/


#if 0
static void grp_putchar_sub(char *dest, char *src, bool kanji, int col)
/*
	1 bit pixel 列を 4 bit pixel 列に変換する
*/
{
	int i,j;
	uint col_a[8];
	static char bit[] = {128,64,32,16,8,4,2,1};
	col &= 0xf;
	for (i=0; i<8; i++)
		col_a[i] = col << (i*4);
	if (kanji)
	{
		for (i=0; i<16; i++,dest+=8,src+=2)
		{
			*(uint*)dest = *(uint*)(dest+4) = 0;
			for (j=0; j<16; j++)
				if (*(src+j/8) & bit[j%8])
					*(uint*)(dest+(j/8)*4) |= col_a[j%8];
		}
	}
	else
	{
		for (i=0; i<16; i++,dest+=4,src++)
		{
			*(uint*)dest = 0;
			for (j=0; j<8; j++)
				if (*src & bit[j])
					*(uint*)dest |= col_a[j%8];
		}
	}
}

static void grp_putchar(int x, int y, int c, int col, int style)
{
	char font[16*16/8];
	para_putBlock par;
	int xlen,ylen,i;
	bool kanji;
	if (c <= 0xff /* 半角文字？ */)
	{
		kanji = NO;
		xlen=8, ylen=16;
		FNT_ankRead(xlen,ylen, c, getds(), font);
		if (style & FNT_bold)
		{
			for (i=0; i<16; i++)
				font[i] |= font[i] >> 1;
		}
	}
	else // 全角文字
	{
		kanji = YES;
		xlen=ylen=16;
		FNT_kanjiRead(xlen,ylen, FNT_sjisToJis(c), getds(), font);
		if (style & FNT_bold)
		{
			for (i=0; i<16; i++)
			{
				unsigned int pat;
				pat = (font[i*2] << 8) | font[i*2+1];
				pat |= pat >> 1;
				font[i*2] = pat >> 8,  font[i*2+1] = pat & 0xff;
			}
		}
	}
	EGB_writeMode(EGB_work, DrawMATTE);
	EGB_color(EGB_work,3,0);
#if 0
	par.buf = font;
	par.bufsel = getds();
	par.x1 = x;
	par.y1 = y;
	par.x2 = x + xlen - 1;
	par.y2 = y + ylen - 1;
	if (style & FNT_shadow)
	{
		EGB_color(EGB_work, 0, Black);
		par.x1++, par.y1++, par.x2++, par.y2++;
		EGB_putBlockColor(EGB_work, 0, (char*)&par);
		par.x1--, par.y1--, par.x2--, par.y2--;
	}
	EGB_color(EGB_work, 0, col);
	EGB_putBlockColor(EGB_work, 0, (char*)&par);
#else
	char grpbuf[16*16/2],grpbuf2[16*16/2];
	grp_putchar_sub(grpbuf,font,kanji,col);
	if (style & FNT_shadow)
		grp_putchar_sub(grpbuf2,font,kanji,Black);
	par.buf = grpbuf;
	par.bufsel = getds();
	par.x1 = x;
	par.y1 = y;
	par.x2 = x + xlen - 1;
	par.y2 = y + ylen - 1;
	if (style & FNT_shadow)
	{
		par.buf = grpbuf2;
		par.x1++, par.y1++, par.x2++, par.y2++;
		EGB_putBlock(EGB_work, 0, (char*)&par);
		par.x1--, par.y1--, par.x2--, par.y2--;
		par.buf = grpbuf;
	}
	EGB_putBlock(EGB_work, 0, (char*)&par);
#endif
}

#endif


void	ART_putstr_style(int x, int y, char *str, int col, int style)
{
#if 1
	EGB_fontStyle(EGB_work, style);
	EGB_color(EGB_work, 1, Black);
	grp_putstr(x,y,str,col);
#else
	int c;
	for (;;)
	{
		if (*str == 0)
			break;
		if (iskanji(*str))
		{
			c = *str * 256 + *(str+1);
			grp_putchar(x,y,c,col,style);
			str++,str++;
			x += 16;
		}
		else
		{
			c = *str;
			grp_putchar(x,y,c,col,style);
			str++;
			x += 8;
		}
	}
#endif
}


void	ART_putstr(int x, int y, char *str, int col)
{
	ART_putstr_style(x,y,str,col,0);
}


void ARTputstr12(int x,int y, char *str, int forecol, int backcol)
{
	#define	PUTBITBLOCK(x1,y1,x2,y2,dat) { \
		char para[16];   DWORD(para)=(unsigned int)dat; WORD(para+4)=getds(); \
		WORD(para+6)=x1; WORD(para+8)=y1; WORD(para+10)=x2; WORD(para+12)=y2; \
		EGB_putBlockColor(EGB_work, 0, para); }
	int ds = getds();
	char fontbuf[24];
	EGB_color(EGB_work, 0, forecol);
	EGB_writeMode(EGB_work, DrawNORMAL);
	while (*str != 0)
	{
		if (*str == '\a')
		{
			putpict(x,y,Partemis);
			while (*str == '\a')  str++;
			x += 52;
		}
		else if (_iskanji(*str))
		{
			int ofs, sjis = (*str)*256 + *(str+1), jis;
			jis = FNT_sjisToJis(sjis);
			if (_iskanji1(sjis))
			{
				ofs = ( ((jis>>8)-0x21)*94 + (jis&0xff) - 0x21)*24 + 0xc00;
				_movedata(font12seg, ofs, ds, (uint)fontbuf, 24);
			}
			else	// 第１水準じゃない場合
			{
				char font16buf[32];
				FNT_kanjiRead(16,16,jis,ds,font16buf);
				memset(fontbuf,0,24);
				int di=0;
				for (int i=0; i<16; i++)
				{
					ushort p;
					p = ((ushort)font16buf[i*2]<<8) | font16buf[i*2+1];
					p =  (p&0xc000) | ((p&0x3c00)<<1) | ((p&0x3c0)<<2) |
					    ((p&0x3c)<<3) | ((p&0x3)<<4);
					fontbuf[di*2]   |= (p>>8) & 0xff;
					fontbuf[di*2+1] |= p & 0xff;
					if (i!=1 && i!=4 && i!=7 && i!=10)
						di++;
				}
			}
			PUTBITBLOCK(x,y,x+11,y+11,fontbuf);
			str++,str++;
			x += 12;
		}
		else
		{
			if (*str != ' ')
			{
				int ofs = (int)*str * 12;
				_movedata(font12seg, ofs, ds, (uint)fontbuf, 12);
				PUTBITBLOCK(x,y,x+5,y+11,fontbuf);
			}
			str++;
			x += 6;
		}
	}
}


/* end of subgrp.c */
