/*
	ARTemis (Graphic Editor for FM-TOWNS)  (c) MATSUUCHI Ryosuke 1992,1993

	dispman.c	Display Manager(表示管理部; 略称ＤＭ)
*/

#include <stdio.h>
#include <msdos.cf>
#include <malloc.h>
#include "ge.h"
#include "imageman.h"
#include "arealist.h"

static	int			menuplt[16];
static	Arealist	*menu1box;
static	Arealist	*menu2box;
#define	HIGH16LOW32K	0
#define	HIGH32K			1
static	int		scrtype; 			// HIGH16LOW32K / HIGH32K
static	int		zoomrate;
static	int		dispx,dispy;
static	int		_dispxlen,_dispylen;	// 表示されている範囲
static	int		vx,vy;				// VRAM 上に存在するimageの全体でのoffset
static	bool	_lat1disp,_lat2disp;
static	int		lat2xstep,lat2ystep;
static	int		lat2xofs=0,lat2yofs=0;
static	bool	maskdisp = NO;

typedef struct {
	int x,y,xlen,ylen;	// その時点の zoomrate での範囲
	char *gbuf1;		// imagebuf の画像の保存用
	char *gbuf2;		// menu2 レイヤの画像の保存用(スクロール時)
} Menu2box;

/*
★関数一覧

	DMnew						DM の初期化
	DMdelete					DM の終了
	
	DMgetifonepage				レイヤ構成が1ページ上かどうかを得る
	DMgetxsize					menu1レイヤの横幅
	DMgetysize					menu1レイヤの縦幅
	DMgetmenuplt				menu1レイヤにおけるパレット設定を得る
	DMdispcsr					menu1レイヤに重ねてカーソルを表示
	DMerasecsr					menu1レイヤ上のカーソルを消去
	DMchangecsrtype				カーソルの種別の変更

	DMmenu1_addbox				menu1レイヤに新たに矩形領域を設定する
	DMmenu1_deletebox			menu1レイヤの矩形領域を削除する
	DMmenu2_addbox				menu2レイヤに新たに矩形領域を設定する
	DMmenu2_deletebox			menu2レイヤの矩形領域を削除する

	DMimage_setdispxy			編集画像のどこをimageレイヤに表示するか
	DMimage_limitdispxy			ある座標の組を、dispx,dispy の設定限界内に制限
								する
	DMimage_refresh				imageレイヤの内容を(編集画像に従って)更新する
	DMimage_setzoomrate			imageレイヤの拡大率はなん倍か
	DMimage_getzoomrate			現在設定されている拡大率を得る
	DMimage_setlatticeswitch	imageレイヤに重ねる格子を表示する・しない
	DMimage_setmaskdisp			マスク部分を反転表示する・しない
	DMimage_getxbytes			imageレイヤの１ラインのバイト数を得る
	DMimage_pset				imageレイヤに点を打つ
	DMimage_line				imageレイヤに直線を描く
	DMimage_hline				imageレイヤに水平直線を描く
	DMimage_vline				imageレイヤに垂直直線を描く
	DMimage_boxline				imageレイヤに矩形（枠）を描く
	DMimage_boxfill				imageレイヤに矩形フィルする
*/

/*--------------------------------------------------------*/
/*                 領域分割の補助ルーチン                 */
/*--------------------------------------------------------*/

#define	INTMIN	(-100000)
#define	INTMAX	( 100000)

static int andarea(Area *a1, Area *a2, Area *outarea)
// Area a1,a2 の積の領域を outarea に代入
// 返値: 積がφでなければ 1, φなら 0
{
	outarea->x1 = _max(a1->x1, a2->x1);
	outarea->y1 = _max(a1->y1, a2->y1);
	outarea->x2 = _min(a1->x2, a2->x2);
	outarea->y2 = _min(a1->y2, a2->y2);
	if (outarea->x1 <= outarea->x2 && outarea->y1 <= outarea->y2)
		return 1;
	else
		return 0;
}

static void splitarea(Area *obj, Area *sbj, Arealist *outal)
// obj を sbj で 0〜4個の Area に分割し、それを outal に追加
{
	Area a,a2;
	// sbj の上の領域
	a.x1=INTMIN, a.y1=INTMIN, a.x2=INTMAX, a.y2=sbj->y1-1;
	if (andarea(&a, obj, &a2))
		ALaddarea(outal, &a2);
	// sbj の左の領域
	a.x1=INTMIN, a.y1=sbj->y1, a.x2=sbj->x1-1, a.y2=sbj->y2;
	if (andarea(&a, obj, &a2))
		ALaddarea(outal, &a2);
	// sbj の右の領域
	a.x1=sbj->x2+1, a.y1=sbj->y1, a.x2=INTMAX, a.y2=sbj->y2;
	if (andarea(&a, obj, &a2))
		ALaddarea(outal, &a2);
	// sbj の下の領域
	a.x1=INTMIN, a.y1=sbj->y2+1, a.x2=INTMAX, a.y2=INTMAX;
	if (andarea(&a, obj, &a2))
		ALaddarea(outal, &a2);
}

static Arealist *makearealist()	// menu1box に従い、画面を分割する
{
	Arealist *al;
	Area a,*q;
	if ((al = ALnew()) == NULL)
		return NULL;
	a.x1=a.y1=0, a.x2=(scrtype==HIGH32K?511:639), a.y2=479;
	ALaddarea(al,&a);
	for (q=ALgetfirstarea(menu1box); q!=NULL; q=ALgetnextarea(menu1box))
	{
		Arealist *al2; Area *p;
		if ((al2 = ALnew()) == NULL)
			return NULL;
		for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
			splitarea(p, q, al2);
		ALdelete(al);
		al = al2;
	}
	return al;
}

/*--------------------------------------------------------*/
/*                 格子表示の補助ルーチン                 */
/*--------------------------------------------------------*/

static void _drawlattice(Area *area, bool lat1, int col1, bool lat2, int col2)
{
	int t,x,xi,y,yi;
	if (zoomrate==1)
		return;
	if (zoomrate == 2)
		lat1 = NO;
	// 小格子の表示
	if (lat1)
	{
		for (t=area->x1+zoomrate-1, x=t-t%zoomrate+zoomrate-1, xi=t/zoomrate;
			 x <= area->x2;  x+=zoomrate, xi++)
			gvline(x,area->y1,area->y2,col1, DrawNORMAL);
		for (t=area->y1+zoomrate-1, y=t-t%zoomrate+zoomrate-1, yi=t/zoomrate;
			 y <= area->y2;  y+=zoomrate, yi++)
			ghline(area->x1,area->x2,y,col1,DrawNORMAL);
	}
	// 大格子の表示
	if (lat2)
	{
		for (t=area->x1+zoomrate-1, x=t-t%zoomrate+zoomrate-1, xi=t/zoomrate;
			 x <= area->x2;  x+=zoomrate, xi++)
		{
			if (((dispx+xi)-lat2xofs+1)%lat2xstep==0)
				gvline(x,area->y1,area->y2,col2, DrawNORMAL);
		}
		for (t=area->y1+zoomrate-1, y=t-t%zoomrate+zoomrate-1, yi=t/zoomrate;
			 y <= area->y2;  y+=zoomrate, yi++)
		{
			if (((dispy+yi)-lat2yofs+1)%lat2ystep==0)
				ghline(area->x1,area->x2,y,col2,DrawNORMAL);
		}
	}
}

static void dispalllattice()
{
	Arealist *al; Area *p;
	if ((al = makearealist()) == NULL)
		return;
	for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
		_drawlattice(p,_lat1disp,menuplt[Black],_lat2disp,menuplt[LatticeColor]);
	ALdelete(al);
}

static void erasealllattice()
{
	Arealist *al; Area *p;
	if (zoomrate == 1 || scrtype == HIGH32K || (al = makearealist()) == NULL)
		return;
	for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
	{
		gboxfill(p->x1,p->y1,p->x2,p->y2,0,DrawNORMAL);
		// _drawlattice(p,_lat1disp,0,_lat2disp,0);
	}
	ALdelete(al);
}

static void displattice2()
{
	if (!_lat2disp)
		return;
	Arealist *al; Area *p;
	if ((al = makearealist()) == NULL)
		return;
	for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
		_drawlattice(p,NO,0,YES,menuplt[LatticeColor]);
	ALdelete(al);
}

static void eraselattice2()
{
	Arealist *al; Area *p;
	if ((al = makearealist()) == NULL)
		return;
	for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
		_drawlattice(p,NO,0,YES,((_lat1disp && zoomrate > 2) ? menuplt[Black] : 0));
	ALdelete(al);
}

/*--------------------------------------------------------*/
/*              画面モード設定の補助ルーチン              */
/*--------------------------------------------------------*/

static void setscreen()
// scrtype に従って画面モードを設定、パレットの初期化も行う
{
	if (scrtype == HIGH16LOW32K)
	{
		gwrtpage(0);
		gscreen(3);
		gwrtpage(1);
		gscreen(10);
		gdsploc(0,0);
		gscrzoom(zoomrate,zoomrate);
		gdsparea(640/zoomrate,480/zoomrate);
		gwrtpage(0);
		// パレット設定	
		grp_setplt(0,				0x000000);
		grp_setplt(Black,			0x000000);
		grp_setplt(Gray1,			0x202020);
		grp_setplt(Gray2,			0x303030);
		grp_setplt(Gray3,			0x505050);
		grp_setplt(Gray4,			0x707070);
		grp_setplt(Gray5,			0x808080);
		grp_setplt(Gray6,			0xa0a0a0);
		grp_setplt(Gray7,			0xc0c0c0);
		grp_setplt(Gray8,			0xd0d0d0);
		grp_setplt(White,			0xf0f0f0);
		grp_setplt(Yellow,			0xf0f000);
		grp_setplt(COL_menuShade,	0x404050);
		grp_setplt(COL_menu,		0x9090b0);
		grp_setplt(COL_menuLight,	0xe0e0f0);
		grp_setplt(COL_menu2,		0xb0b0c0);
	}
	else if (scrtype == HIGH32K)
	{
		gwrtpage(0);
		gscreen(17);
		gdsploc(0,0);
		gscrzoom(1,1);
		gdsparea(512,480);
		grboxfill(0,0,512,480,0,DrawNORMAL);
	}
}

/*--------------------------------------------------------*/
/*                     menuplt の設定                     */
/*--------------------------------------------------------*/

static void setmenuplt()
{
	int i;
	if (scrtype == HIGH16LOW32K)
	{
		for (i=0; i<16; i++)
			menuplt[i] = i;
	}
	else if (scrtype == HIGH32K)
	{
		for (i=0; i<16; i++)
		{
			switch (i)
			{
			case White:				menuplt[i] = 32767;					break;
			case Black:				menuplt[i] = 0;						break;
			#define G(n) ((n)*1024+(n)*32+(n))
			case Gray1:				menuplt[i] = G(3);					break;
			case Gray2:				menuplt[i] = G(7);					break;
			case Gray3:				menuplt[i] = G(10);					break;
			case Gray4:				menuplt[i] = G(14);					break;
			case Gray5:				menuplt[i] = G(17);					break;
			case Gray6:				menuplt[i] = G(21);					break;
			case Gray7:				menuplt[i] = G(24);					break;
			case Gray8:				menuplt[i] = G(28);					break;
			#undef G
			case Transparent:		menuplt[i] = 0x8000;				break;
			case Yellow:			menuplt[i] = 31*1024+31*32+ 0;		break;
			case COL_menu:			menuplt[i] = 19*1024+19*32+22;		break;
			case COL_menuLight:		menuplt[i] = 28*1024+28*32+31;		break;
			case COL_menuShade:		menuplt[i] = 8*1024+8*32+10;		break;
			case COL_menu2:			menuplt[i] = 22*1024+22*32+24;		break;
			default:				menuplt[i] = 31;					break;
			}
		}
	}
}

/*--------------------------------------------------------*/
/*             Display Manager の初期化／終了             */
/*--------------------------------------------------------*/

int	DMnew(int reso)			// DM の初期化
// reso: 0 = 画面を16色高解像度ページ＋32K色低解像度ページの構成で初期化
//		 1 = 画面を32K色高解像度ページのみの構成で初期化
// 返値 0=成功
{
	int i;
	if ((menu1box = ALnew()) == NULL)
		return -1;
	if ((menu2box = ALnew()) == NULL)
		return -1;
	scrtype = (reso==0 ? HIGH16LOW32K : HIGH32K);
	dispx = dispy = 0;
	vx = vy = 0;
	_lat1disp = _lat2disp = NO;
	lat2xstep = lat2ystep = 16;
	lat2xofs = lat2yofs = 0;
	if (scrtype == HIGH16LOW32K)
		zoomrate=2, _dispxlen=640/zoomrate, _dispylen=480/zoomrate;
	else if (scrtype == HIGH32K)
		zoomrate=1, _dispxlen=512, _dispylen=480;
	setmenuplt();
	// 画面モードの設定
	ginit();
	EGB_work = _egbwork;
	setscreen();
}

void DMdelete(void)			// DM の終了
{
	if (scrtype == HIGH16LOW32K)
	{
		gwrtpage(0);
		grboxfill(0,0,640,480,0,DrawNORMAL);
		gwrtpage(1);
		grboxfill(0,0,512,256,0,DrawNORMAL);
		gwrtpage(0);
	}
	else
	{
		gwrtpage(0);
		grboxfill(0,0,512,480,0,DrawNORMAL);
	}
}

/*--------------------------------------------------------*/
/*         Display Manager の各種パラメータの取得         */
/*--------------------------------------------------------*/

int DMgetifonepage(void)	// レイヤ構成が1ページ上かどうかを得る
// 返値: 0=1ページ上ではない  1=1ページ上
{
	return (scrtype == HIGH16LOW32K ? 0 : 1);
}

int DMgetxsize(void)		// menu1レイヤの横幅を得る
{
	return (scrtype == HIGH32K ? 512 : 640);
}

int DMgetysize(void)		// menu1レイヤの縦幅を得る
{
	return 480;
}

void DMimage_getdispxy(int *x,int *y)
{
	*x = dispx;
	*y = dispy;
}

void DMimage_getdispxylen(int *xlen, int *ylen)
{
	*xlen = _dispxlen;
	*ylen = _dispylen;
}

int DMgetpage1x(int x)
{
	if (scrtype == HIGH16LOW32K)
		return (dispx-vx)+x/zoomrate;
	else if (scrtype == HIGH32K)
		return x;
}

int DMgetpage1y(int y)
{
	if (scrtype == HIGH16LOW32K)
		return (dispy-vy)+y/zoomrate;
	else if (scrtype == HIGH32K)
		return y;
}

void DMimage_getvramxy(int *x, int *y)
{
	*x = vx;
	*y = vy;
}

int DMgetmenuplt(int n)		// menu1レイヤにおけるパレット設定を得る
{
	return menuplt[n];
}

int DMimage_getzoomrate()
{
	return zoomrate;
}

int DMimage_getxbytes()
{
	return 1024;
}


static bool csrdisp = NO;
static int  csrx,csry;

void DMerasecsr(void)		// menu1レイヤ上のカーソルを消去
{
	if (!csrdisp)
		return;
	csrdisp = NO;
	int col;
	col = (DMgetifonepage() ? White : csrcol);
	ghline(0,DMgetxsize()-1,csry,DMgetmenuplt(col),DrawXOR);
	gvline(csrx,0,DMgetysize()-1,DMgetmenuplt(col),DrawXOR);
}

void DMdispcsr(int x,int y)	// menu1レイヤに重ねてカーソルを表示
{
	if (csrdisp)
		DMerasecsr();
	if (0 <= x && x < DMgetxsize() && 0<=y && y<DMgetysize())
	{
		csrdisp = YES;
		csrx = x;
		csry = y;
		int col;
		col = (DMgetifonepage() ? White : csrcol);
		ghline(0,DMgetxsize()-1,y,DMgetmenuplt(col),DrawXOR);
		gvline(x,0,DMgetysize()-1,DMgetmenuplt(col),DrawXOR);
	}
}


void DMchangecsrtype(int type)	// カーソルの種別の変更
{
}

/*--------------------------------------------------------*/
/*       menu1, menu2 レイヤに領域を追加／削除する        */
/*--------------------------------------------------------*/

int DMmenu1_addbox(int x,int y,int xlen,int ylen)
// menu1レイヤに新たに矩形領域を設定する
{
	Area a,*ap;  bool sw2nd;
	a.x1=x, a.y1=y, a.x2=x+xlen-1, a.y2=y+ylen-1;
	sw2nd = (ALgetfirstarea(menu1box) == NULL ? NO : YES);	// ２番目以降？
	if ((ap = ALaddarea(menu1box, &a)) == NULL)
		return -1;
	int bufsize;  char *gbuf;
	if (scrtype == HIGH16LOW32K)
		bufsize = (((xlen+7)/8)*8)*ylen/2;
	else if (scrtype == HIGH32K)
		bufsize = xlen*ylen*2;
	if (sw2nd)
	{
		if ((gbuf = malloc(bufsize)) == NULL) {
			ALdeletearea(menu1box);
			return -1;
		}
		ALarea_setdata(ap,gbuf);
		gwrtpage(0);
		grgetblk(gbuf,x,y,xlen,ylen);
	}
	else
		ALarea_setdata(ap,NULL);
	return 0;
}

int DMmenu1_deletebox(void)	// menu1レイヤの矩形領域を削除する
// (最後に設定した領域を削除する)
{
	Area *ap;  char *gbuf;
	if ((ap = ALgetfirstarea(menu1box)) != NULL)
	{
		if ((gbuf = ALarea_getdata(ap)) != NULL)
		{
			grputblk(ap->x1, ap->y1, ap->x2-ap->x1+1, ap->y2-ap->y1+1,
					 gbuf,DrawNORMAL);
			free(gbuf);
		}
		else
			gboxfill(ap->x1,ap->y1,ap->x2,ap->y2,0,DrawNORMAL);
		ALdeletearea(menu1box);
	}
	if (scrtype == HIGH32K)
		DMimage_refresh();
	erasealllattice();
	dispalllattice();
	return 0;
}

int DMmenu2_addbox(int x,int y,int xlen,int ylen)
// menu2レイヤに新たに矩形領域を設定する
{
	Area a,*ap;
	a.x1=x, a.y1=y, a.x2=x+xlen-1, a.y2=y+ylen-1;
	if ((ap=ALaddarea(menu2box, &a)) == NULL)
		return -1;
	if (scrtype == HIGH16LOW32K)
	{
		Menu2box b,*bp;
		b.x = x / zoomrate;
		b.y = y / zoomrate;
		b.xlen = (xlen+zoomrate-1) / zoomrate;
		b.ylen = (ylen+zoomrate-1) / zoomrate;
		if ((bp = (Menu2box*)malloc(sizeof(Menu2box)+b.xlen*b.ylen*4)) == NULL)
		{
			ALdeletearea(menu2box);
			return -1;
		}
		*bp = b;
		bp->gbuf1 = (char*)bp + sizeof(Menu2box);
		bp->gbuf2 = (char*)bp + sizeof(Menu2box) + b.xlen*b.ylen*2;
		gwrtpage(1);
		grgetblk(bp->gbuf1,dispx-vx+b.x,dispy-vy+b.y,b.xlen,b.ylen);
		gwrtpage(0);
		ALarea_setdata(ap,bp);
	}
	return 0;
}

int DMmenu2_deletebox(void)	// menu2レイヤの矩形領域を削除する
{
	if (scrtype == HIGH16LOW32K)
	{
		Area *ap;
		if ((ap = ALgetfirstarea(menu2box)) != NULL)
		{
			Menu2box *bp;
			bp = (Menu2box *)ALarea_getdata(ap);
			gwrtpage(1);
			grputblk(dispx-vx+bp->x,dispy-vy+bp->y,
					 bp->xlen,bp->ylen,bp->gbuf1, DrawNORMAL);
			gwrtpage(0);
			free(bp);
		}
	}
	ALdeletearea(menu2box);
	return 0;
}


/*--------------------------------------------------------*/
/*              image レイヤの表示位置の変更              */
/*--------------------------------------------------------*/

void DMimage_limitdispxy(int x, int y, int *newx, int *newy)
{
	*newx = x, *newy = y;
	if (x < 0)
		*newx = 0;
	else if (x + _dispxlen > EIMgetxsize())
		*newx = EIMgetxsize() - _dispxlen;
	if (y < 0)
		*newy = 0;
	else if (y + _dispylen > EIMgetysize())
		*newy = EIMgetysize() - _dispylen;
}

int DMimage_setdispxy(int x,int y)	// 編集画像のどこをimageレイヤに表示するか
// 結果的に x,y がどうなったかも返すべき？
{
	Area *ap;
	if (x < 0)
		x = 0;
	else if (x+_dispxlen > EIMgetxsize())
		x = EIMgetxsize() - _dispxlen;
	if (y < 0)
		y = 0;
	else if (y+_dispylen > EIMgetysize())
		y = EIMgetysize() - _dispylen;
	if (scrtype == HIGH16LOW32K)
	{
		/*
			画面モード構成が16色高解像度＋32K色低解像度の場合:
		
			新たに表示したい領域が、すでにVRAM上に存在する範囲ならば、
			ハード的に表示範囲を変更するだけ。
			ここで、「新たに表示したい領域」とは、編集画像上のある矩形領域
			であり、その基準座標は(x,y)、大きさは
			(DMgetxsize()/zoomrate, DMgetysize()/zoomrate) という領域である。
		
			この領域が、まだ VRAM上に存在しない領域を含んでいる場合、
			編集画像バッファよりデータを転送する。
			(x,y)-VRAM上に存在しない領域を表示したいという場合は、
		*/
		eraselattice2();
		// menu2box の内容を復帰
		if ((ap = ALgetfirstarea(menu2box)) != NULL)
		{
			gwrtpage(1);
			for ( ; ap!=NULL; ap=ALgetnextarea(menu2box))
			{
				Menu2box *bp;
				bp = (Menu2box*)ALarea_getdata(ap);
				grgetblk(bp->gbuf2,
						 dispx-vx+bp->x,dispy-vy+bp->y,bp->xlen,bp->ylen);
				grputblk(dispx-vx+bp->x,dispy-vy+bp->y,bp->xlen,bp->ylen,
						 bp->gbuf1,DrawNORMAL);
			}
			gwrtpage(0);
		}
		bool vc = NO; // vx,vyを変更したかどうか
		if (x < vx)
			vx = _max(0, x+_dispxlen-512), vc=YES;
		else if (vx+512 < x+_dispxlen)
			vx = _min(EIMgetxsize()-512, x), vc=YES;
		if (y < vy)
			vy = _max(0, y+_dispylen-256), vc=YES;
		else if (vy+256 < y+_dispylen)
			vy = _min(EIMgetysize()-256, y), vc=YES;
		if (vc)
			xymemcpy(0x104,0x40000, getds(),(int)EIMadrs(vx,vy),
					 512*2, 256, DMimage_getxbytes(), EIMgetxbytes());
		dispx = x, dispy = y;
		gwrtpage(1);
		gdsploc(dispx-vx,dispy-vy);
		gwrtpage(0);
		// menu2box の内容を退避
		if ((ap = ALgetfirstarea(menu2box)) != NULL)
		{
			gwrtpage(1);
			for ( ; ap!=NULL; ap=ALgetnextarea(menu2box))
			{
				Menu2box *bp;
				bp = (Menu2box*)ALarea_getdata(ap);
				grgetblk(bp->gbuf1,
						 dispx-vx+bp->x,dispy-vy+bp->y,bp->xlen,bp->ylen);
				grputblk(dispx-vx+bp->x,dispy-vy+bp->y,bp->xlen,bp->ylen,
						 bp->gbuf2,DrawNORMAL);
			}
			gwrtpage(0);
		}
		displattice2();
	}
	else if (scrtype == HIGH32K)
	{
		dispx = x, dispy = y;
		if (zoomrate == 1)
		{
			Arealist *al; Area *p;
			if ((al = makearealist()) != NULL)
			{
				for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
					xymemcpy(0x10c,1024*p->y1+p->x1*2,
							 getds(),(int)EIMadrs(dispx + p->x1,dispy + p->y1),
							 (p->x2 - p->x1 + 1)*2,  p->y2 - p->y1 + 1,
							 DMimage_getxbytes(), EIMgetxbytes());
				ALdelete(al);
			}
		}	
	}
	return 0;
}

/*--------------------------------------------------------*/
/*                image レイヤの内容の更新                */
/*--------------------------------------------------------*/

int DMimage_refresh(void)	// imageレイヤの内容を(編集画像に従って)更新する
{
	if (scrtype == HIGH16LOW32K)
	{
		xymemcpy(0x104,0x40000, getds(),(int)EIMadrs(vx,vy),
				 512*2, 256, DMimage_getxbytes(), EIMgetxbytes());
	}
	else if (scrtype == HIGH32K)
	{
		Arealist *al; Area *p;
		if ((al = makearealist()) != NULL)
		{
			for (p=ALgetfirstarea(al); p!=NULL; p=ALgetnextarea(al))
				xymemcpy(0x10c,1024*p->y1+p->x1*2,
						 getds(),(int)EIMadrs(dispx + p->x1,dispy + p->y1),
						 (p->x2 - p->x1 + 1)*2,  p->y2 - p->y1 + 1,
						 DMimage_getxbytes(), EIMgetxbytes());
			ALdelete(al);
		}
	}
	return 0;
}

/*--------------------------------------------------------*/
/*               image レイヤの拡大率の変更               */
/*--------------------------------------------------------*/

void DMimage_setzoomrate(int rate)
{
	if (zoomrate == rate)
		;
	else if (zoomrate == 1 && rate > 1)
	{
		zoomrate = rate;
		_dispxlen = 640/zoomrate,  _dispylen = 480/zoomrate;
		vx = dispx - (512-_dispxlen)/2;
		vy = dispy - (256-_dispylen)/2;
		vx = _lim(vx, 0, EIMgetxsize()-512);
		vy = _lim(vy, 0, EIMgetysize()-256);
		scrtype = HIGH16LOW32K;
		setscreen();
		grboxfill(0,0,640,480,0,DrawNORMAL);
		xymemcpy(0x104,0x40000, getds(),(int)EIMadrs(vx,vy),
				 512*2, 256, DMimage_getxbytes(), EIMgetxbytes());
		gwrtpage(1);
		gscrzoom(zoomrate,zoomrate);
		gdsparea(320,240);
		gdsploc(dispx-vx,dispy-vy);
		gwrtpage(0);
		setmenuplt();
		dispalllattice();
	}
	else if (zoomrate > 1 && rate == 1)
	{
		zoomrate = rate;
		_dispxlen = 512, _dispylen = 480;
		dispx = _min(dispx, EIMgetxsize()-512);
		dispy = _min(dispy, EIMgetysize()-256);
		scrtype = HIGH32K;
		setscreen();
		xymemcpy(0x10c,0,
				 getds(),(int)EIMadrs(dispx,dispy),
				 _dispxlen*2, _dispylen,
				 DMimage_getxbytes(), EIMgetxbytes());
		setmenuplt();
		dispalllattice();
	}
	else if (zoomrate > 1 && rate > 1)
	{
		zoomrate = rate;
		_dispxlen = 640/zoomrate,  _dispylen = 480/zoomrate;
		dispx = _min(dispx, EIMgetxsize()-_dispxlen);
		dispy = _min(dispy, EIMgetysize()-_dispylen);
		bool vc = NO;
		if (dispx < vx)
			vx = _max(0, dispx+_dispxlen-512), vc=YES;
		else if (vx+512 < dispx+_dispxlen)
			vx = _min(EIMgetxsize()-512, dispx), vc=YES;
		if (dispy < vy)
			vy = _max(0, dispy+_dispylen-512), vc=YES;
		else if (vy+256 < dispy+_dispylen)
			vy = _min(EIMgetysize()-256, dispy), vc=YES;
		if (vc)
			xymemcpy(0x104,0x40000, getds(),(int)EIMadrs(vx,vy),
					 512*2, 256, DMimage_getxbytes(), EIMgetxbytes());
		gwrtpage(1);
		gdsploc(dispx-vx,dispy-vy);
		gscrzoom(zoomrate,zoomrate);
		gwrtpage(0);
		dispalllattice();
	}
}

/*--------------------------------------------------------*/
/*       image レイヤの格子の表示／非表示の切り換え       */
/*--------------------------------------------------------*/

void DMimage_setlatticesize(int xlen,int ylen)
{
	erasealllattice();
	lat2xstep = xlen;
	lat2ystep = ylen;
	dispalllattice();
}

void DMimage_setlatticeswitch(bool lat1, bool lat2)
{
	erasealllattice();
	_lat1disp = lat1;
	_lat2disp = lat2;
	dispalllattice();
}

void DMimage_getlatticeswitch(bool *lat1, bool *lat2)
{
	*lat1 = _lat1disp;
	*lat2 = _lat2disp;
}

void DMimage_getlatticesize(int *xsize,int *ysize)
{
	*xsize = lat2xstep;
	*ysize = lat2ystep;
}

/*--------------------------------------------------------*/
/*         マスク領域の反転表示スイッチの切り換え         */
/*--------------------------------------------------------*/

void DMimage_setmaskdisp(bool disp)
{
	maskdisp = disp;
	DMimage_refresh();
}

/*--------------------------------------------------------*/
/*              image レイヤ内での座標を得る              */
/*--------------------------------------------------------*/

int DMimage_getx(int x)
// x: menu1レイヤでの座標
{
	return dispx+x/zoomrate;
}

int DMimage_gety(int y)
{
	return dispy+y/zoomrate;
}

/*--------------------------------------------------------*/
/*            image レイヤに対する描画コマンド            */
/*--------------------------------------------------------*/

void DMimage_pset(int x,int y,int col,int op) // imageレイヤに点を打つ
// 点を打つときには、menu1レイヤのbox[0]だけを避ける。
{
	if (scrtype == HIGH16LOW32K)
	{
		x -= vx, y -= vy;
		if (0 <= x && x < 512 && 0 <= y && y < 256)
		{
			if (op == DrawXOR)
				MEMstoreword_xor(0x104,(char*)(0x40000+1024*y+x*2), col,1);
			else
				MEMstoreword(0x104,(char*)(0x40000+1024*y+x*2), col,1);
			// gwrtpage(1);
			// gpset(x,y,col,op);
			// gwrtpage(0);
		}
	}
	else if (scrtype == HIGH32K)
	{
		x -= dispx, y -= dispy;
		if (zoomrate == 1)
		{
			Area *ap;
			ap = ALgetfirstarea(menu1box);
			if (0 <= x && x < 512 && 0 <= y && y < 480)
			{
				if (ap == NULL)
				{
					if (op == DrawXOR)
						MEMstoreword_xor(0x10c,(char*)(1024*y+x*2), col,1);
					else
						MEMstoreword(0x10c,(char*)(1024*y+x*2), col,1);
					// gpset(x,y,col,op);
				}
				else if (x < ap->x1 || y < ap->y1 || ap->x2 < x || ap->y2 < y)
				{
					if (op == DrawXOR)
						MEMstoreword_xor(0x10c,(char*)(1024*y+x*2), col,1);
					else
						MEMstoreword(0x10c,(char*)(1024*y+x*2), col,1);
					// gpset(x,y,col,op);
				}
			}
		}
	}
}

void DMimage_line(int x1,int y1,int x2,int y2,int col,int op)
// imageレイヤに直線を描く
{
	int x,y,xr,yr,r;
	xr = _abs(x2-x1), yr = _abs(y2-y1);
	if (xr == 0 && yr == 0)
		DMimage_pset(x1,y1,col,op);
	else if (xr > yr)
	{
		r = (yr<<16) / xr;
		if (x2 < x1)
			{ swap(x1,x2); swap(y1,y2); }
		if (y1 > y2)
			r = -r;
		for (x=x1,y=(y1<<16)+0x8000; x<=x2; x++,y+=r)
			DMimage_pset(x,(y>>16),col,op);
	}
	else
	{
		r = (xr << 16) / yr;
		if (y2 < y1)
			{ swap(x1,x2); swap(y1,y2); }
		if (x1 > x2)
			r = -r;
		for (y=y1,x=(x1<<16)+0x8000; y<=y2; y++,x+=r)
			DMimage_pset((x>>16),y,col,op);
	}
}

void DMimage_hline(int x1,int x2,int y,int col,int op)
// imageレイヤに水平直線を描く
{
	if (scrtype == HIGH16LOW32K)
	{
		gwrtpage(1);
		x1 -= vx, x2 -= vx, y -= vy;
		if (x1>x2)
			swap(x1,x2);
		x1 = _max(0,x1);
		x2 = _min(512-1,x2);
		if (x1 <= x2 && 0<=y && y<256)
			ghline(x1,x2,y,col,op);
		gwrtpage(0);
	}
	else if (scrtype == HIGH32K)
	{
		x1 -= dispx, x2 -= dispx, y -= dispy;
		if (x1>x2)
			swap(x1,x2);
		x1 = _max(0,x1);
		x2 = _min(512-1,x2);
		if (x1 <= x2 && 0<=y && y<480)
		{
			if (zoomrate == 1)
			{
				bool box = YES;
				Area *ap = ALgetfirstarea(menu1box);
				if (ap == NULL)
					box = NO;
				else if (y < ap->y1 || ap->y2 < y)
					box = NO;
				if (box)
				{
					if (x1 < ap->x1)
						ghline(x1,_min(x2,ap->x1-1),y,col,op);
					if (ap->x2 < x2)
						ghline(_max(x1,ap->x2+1),x2,y,col,op);
				}
				else
					ghline(x1,x2,y,col,op);
			}
		}
	}
}

void DMimage_hline_map(int x1,int x2,int y,char *colmap)
// colmap には、カラーコードが順に並んでいるものとする。
// この関数は、その色の並びの水平直線を image レイヤに描く。
{
	if (scrtype == HIGH16LOW32K)
	{
		gwrtpage(1);
		x1 -= vx, x2 -= vx, y -= vy;
		if (x1>x2)
			swap(x1,x2);
		int x10 = x1;
		x1 = _max(0,x1);
		x2 = _min(512-1,x2);
		colmap += (x1-x10)*2;
		if (x1 <= x2 && 0<=y && y<256)
		{
			xmemcpy(0x104,0x40000+1024*y+x1*2,getds(),(int)colmap,(x2-x1+1)*2);
			// ghline(x1,x2,y,col,op);
		}
		gwrtpage(0);
	}
	else if (scrtype == HIGH32K)
	{
		x1 -= dispx, x2 -= dispx, y -= dispy;
		if (x1>x2)
			swap(x1,x2);
		int x10 = x1;
		x1 = _max(0,x1);
		x2 = _min(512-1,x2);
		colmap += (x1-x10)*2;
		if (x1 <= x2 && 0<=y && y<480)
		{
			if (zoomrate == 1)
			{
				bool box = YES;
				Area *ap = ALgetfirstarea(menu1box);
				if (ap == NULL)
					box = NO;
				else if (y < ap->y1 || ap->y2 < y)
					box = NO;
				if (box)
				{
					if (x1 < ap->x1)
					{
						int right = _min(x2,ap->x1-1);
						xmemcpy(0x10c,1024*y+x1*2,getds(),(int)colmap,(right-x1+1)*2);
						// ghline(x1,_min(x2,ap->x1-1),y,col,op);
					}
					if (ap->x2 < x2)
					{
						int left = _max(x1,ap->x2+1);
						xmemcpy(0x10c,1024*y+left*2,getds(),(int)(colmap+(left-x1)*2),(x2-left+1)*2);
						// ghline(_max(x1,ap->x2+1),x2,y,col,op);
					}
				}
				else
				{
					xmemcpy(0x10c,1024*y+x1*2,getds(),(int)colmap,(x2-x1+1)*2);
					// ghline(x1,x2,y,col,op);
				}
			}
		}
	}
}

void DMimage_vline(int x,int y1,int y2,int col,int op)
// imageレイヤに垂直直線を描く
{
	if (scrtype == HIGH16LOW32K)
	{
		gwrtpage(1);
		x -= vx, y1 -= vy, y2 -= vy;
		if (y1>y2)
			swap(y1,y2);
		y1 = _max(0,y1);
		y2 = _min(256-1,y2);
		if (y1 <= y2 && 0<=x && x<512)
			gvline(x,y1,y2,col,op);
		gwrtpage(0);
	}
	else if (scrtype == HIGH32K)
	{
		x -= dispx, y1 -= dispy, y2 -= dispy;
		if (y1>y2)
			swap(y1,y2);
		y1 = _max(0,y1);
		y2 = _min(480-1,y2);
		if (y1 <= y2 && 0<=x && x<512)
		{
			if (zoomrate == 1)
			{
				bool box = YES;
				Area *ap = ALgetfirstarea(menu1box);
				if (ap == NULL)
					box = NO;
				else if (x < ap->x1 || ap->x2 < x)
					box = NO;
				if (box)
				{
					if (y1 < ap->y1)
						gvline(x,y1,_min(y2,ap->y1-1),col,op);
					if (ap->y2 < y2)
						gvline(x,_max(y1,ap->y2+1),y2,col,op);
				}
				else
					gvline(x,y1,y2,col,op);
			}
		}
	}
}

void DMimage_rboxline(int x,int y,int xlen,int ylen,int col, int op)
// imageレイヤに矩形（枠）を描く
{
	DMimage_hline(x,x+xlen-1,y,col,op);
	if (ylen >= 2)
		DMimage_hline(x,x+xlen-1,y+ylen-1,col,op);
	if (ylen >= 3)
		DMimage_vline(x,y+1,y+ylen-2,col,op),
		DMimage_vline(x+xlen-1,y+1,y+ylen-2,col,op);
}

void DMimage_boxfill(int x,int y,int xlen,int ylen,int col, int op)
// imageレイヤに矩形フィルする
{
	int iy;
	for (iy=0; iy<ylen; iy++)
		DMimage_hline(x,x+xlen-1,y+iy,col,op);
}

/*
	ARTemis の画面表示はすべてこのモジュールを通して行う。
	32K色高解像度のときと 32K 低解像度のときの画面構成の違いを吸収するため、
	以下の３つの仮想レイヤを想定し、ARTemis の画面表示はこれらのレイヤに対
	して行うようプログラミングする。

		menu1 レイヤ	メニューを表示するためのレイヤ。

						このレイヤに対して行える操作は、「矩形領域の設定」
						と同じく「解除」の２通りだけである。
						矩形領域というのはメニューを表示するための領域で、
						「設定」された矩形領域については、他のレイヤを操作
						したときにも表示内容をそのままに保つのがＤＭの働き
						である。

						このレイヤにアクセスするのは「メニュー管理部」だけで
						ある。
						メニュー管理部(ＭＭ)は、メニューを表示する必要が生じ
						た場合、このmenu1レイヤとmenu2レイヤ上に矩形領域を
						「借りて」、その領域内でメニュー処理を行う。メニュー
						を移動する際には、借りた矩形領域を「返して」別の座標
						の領域を借りる、という動作をＭＭは行う。

						ＤＭは「設定された＝ＭＭが借りた」領域に対して、そこ
						にＭＭが表示する内容を「守る」はたらきをする。すなわ
						ち、imageレイヤにいろいろな操作(スクロールや格子表示
						のオン・オフなど)を加えても、menu1レイヤに表示されて
						いる内容はそのまま変化せず表示されているようにする。

		menu2 レイヤ	メニューを表示するためのレイヤその２
		
						menu1 レイヤが32K色高解像度の画面モードのときには、こ
						の menu2 レイヤは全くいらない。
						しかし、menu1 レイヤが16色モードの場合、menu1 レイヤ
						には32K色パレットリストが表示できない。その場合、32K
						色パレットリストはこの menu2 レイヤに用意する。

						このレイヤは、ハード構成に則して見ると、画面が16色高
						解像＋32K色低解像度の構成の時に、32K色のページに表示
						される。
						
						この menu2 レイヤに対し行える操作は、「矩形領域の設定」
						と同じく「解除」の２通りだけである。
						「設定」できる矩形領域は、このレイヤ内に１つだけであ
						る。
						
						このレイヤにアクセスするのはＭＭだけである。ＭＭは、
						画面構成が上のような条件のときにメニューを表示する
						必要が生じた場合、menu2 レイヤから矩形領域を「借り
						て」そこに、32Kを用いたメニュー表示を行う(もちろん、
						メニューが32K色表示を必要としない場合はＭＭは
						menu2 レイヤを使用しない)。
						
						ＤＭは、image レイヤに対してスクロールや描画などの
						操作が行われた場合にも、このmenu2レイヤの表示内容を
						画面上で保存する働きをする。

		image レイヤ	編集画像と格子を表示するためのレイヤ

						編集画像を表示する。16色＋32K色の２ページ構成の場合
						には、32K色ページをハード的に拡大してARTemisの拡大
						表示機能を実現する。32K色高解像度の１ページ構成の
						場合には、編集画像をソフト的に拡大してARTemisの拡大
						表示機能を実現する。

						ハード的に見ると、32K色高解像モード×１ページの場合
						はそのページのことである。16色高解像＋32K色低解像の
						場合は、格子を16色高解像のページに表示し、編集画像
						を 32K色低解像のページに表示する。
*/
