/*
*               TeX Device Driver DVIOUT, DVIPRT で
*               書体倶楽部のアウトラインフォントを使う
*
*                            vraster.c
*
*     "本プログラムは、向内さんの書かれた dvii という dvi ドライバの
*      アウトラインフォント展開アルゴリズムと、ソースコードの大部分を
*      使用しています。このファイルでは、dvii のルーチンをオリジナル
*      と表現しています。"
*
*
*                         1992  by T.Minagawa
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <dir.h>
/* #include <ctype.h> */
#include <string.h>

#include "dd.h"
#include "err.h"
#include "vfont.h"
#include "vraster.h"

/* グローバル変数 */
int *vtran = NULL;
int *htran = NULL;
static int tlinecount = 0;

/* vdata.c */
extern Line *linebuf;
extern int linecount;
extern int itemcount;

/* init.c prtinit.c */
extern int tategaki;
extern char ptex_mode;

/* vfont.c */
extern int s_ratio[];
extern V_FONT *vfont[];

/* プロトタイプ宣言 */
int readdata(VPARA_TBL *, DRAW_MODE);
void setflag(VPARA_TBL *, int);
void getthinwidth(int *, int *);
void makescale(VPARA_TBL *, VWORK_TBL *);
static void thin_adj(VPARA_TBL *, VWORK_TBL *);
static void maketran(VPARA_TBL *, VWORK_TBL *, int);

/*static int ysljudge( _Setflag*, _Setflag* );*/
static void xsubnorm(int, VWORK_TBL *);
static void ysubnorm(int, VWORK_TBL *);
static void xsubscale(int, VWORK_TBL *, int, int *);
static void ysubscale(int, VWORK_TBL *, int, int *);
static int searchtran(int *, int, int, int, int);
static int rasterexpand(VPARA_TBL *, VWORK_TBL *);

void inssort(int, int *);

/* buffer.c */
char *dup_string(char *);
char *marea(unsigned int);

/********************************************************
	inssort
	奥村晴彦氏の "C言語によるアルゴリズム事典" から引用
	させて頂きました
*********************************************************/
void inssort(int n, int *a)
{
	int i, j, x;

	for (i = 1; i < n; i++) {
		x = a[i];
		for (j = i - 1; j >= 0 && a[j] > x; j--)
			a[j + 1] = a[j];
		a[j + 1] = x;
	}
}

/********************************************************
	zs_qdraw
	水平方向のドットを埋める
*********************************************************/
void zs_qdraw(unsigned int xl, unsigned int xr, BUFFER *rp)
{
	unsigned short i, bl, br, ml, mr;
	static unsigned char ls[] =
	{0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
	static unsigned char rs[] =
	{0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};

	if (xl > xr) {
		i = xl;
		xl = xr;
		xr = i;
	}
	bl = xl / 8;
	ml = xl % 8;
	br = xr / 8;
	mr = xr % 8;

	rp += bl;

	if (bl == br) {
		*rp |= ls[ml] & rs[mr];
	}
	else {
		*rp++ |= ls[ml];
		for (i = br - bl - 1; i > 0; i--) {
			*rp++ |= 0xff;
		}
		*rp |= rs[mr];
	}
}

/********************************************************
	zs_vqdraw
	垂直方向のドットを埋める
*********************************************************/
void zs_vqdraw(int yu, int yl, BUFFER *rp, int x, int b_width)
{
	int y;

	if (yu > yl) {
		y = yu;
		yu = yl;
		yl = y;
	}

	rp += yu * b_width;

	for (y = yl - yu; y >= 0; y--) {
		zs_qdraw(x, x, rp);
		rp += b_width;
	}
}

/****************************************************
*	xsubnorm
*	水平方向をリニアにスケーリングする
*	(スケーリングなしと同じ)
*****************************************************/
static void xsubnorm(int vf_size, VWORK_TBL *tbl2)
{
	int i, dotsize, offset;

	offset = (tbl2->xdotwidth) / 2;
	dotsize = tbl2->_xdot;

	for (i = 0; i < dotsize; i++) {
		htran[i] = (int)((long)i * vf_size / dotsize) + offset;
	}
}

/****************************************************
*	ysubnorm
*	垂直方向をリニアにスケーリングする
*	(スケーリングなしと同じ)
*****************************************************/
static void ysubnorm(int vf_size, VWORK_TBL *tbl2)
{
	int i, dotsize, offset;

	offset = (tbl2->ydotwidth) / 2;
	dotsize = tbl2->_ydot;

	for (i = 0; i < dotsize; i++) {
		vtran[i] = (int)((long)i * vf_size / dotsize) + offset;
	}
}

/****************************************************
*	xsubsclae
*	水平方向をスケーリングする
*****************************************************/
static void xsubscale
    (int vf_size, VWORK_TBL *tbl2, int mdot, int *tlinetbl) {
	int i, j, k, dotsize, offset;

	offset = tbl2->xdotwidth / 2 - tbl2->xthin;
	dotsize = tbl2->_xdot;

	for (i = 0; i < dotsize; i++) {
		htran[i] = (int)((long)i * vf_size / mdot) + offset;
	}

	for (i = 0; i < tlinecount; i++) {
		for (j = 0; j < dotsize; j++) {
			if (tlinetbl[i] <= htran[j]) {
				if (j > 0) {
					if (htran[j] - tlinetbl[i]
						>= tlinetbl[i] - htran[j - 1]) {
						offset = tlinetbl[i] - htran[j - 1];
						for (k = j - 1; k < dotsize; k++) {
							htran[k] += offset;
						}
					}
					else {
						offset = htran[j] - tlinetbl[i];
						for (k = j; k < dotsize; k++) {
							htran[k] -= offset;
						}
					}
				}
				break;
			}
		}
	}

	for (i = 0; i < dotsize; i++) {
		htran[i] += tbl2->xthin;
	}
}

/****************************************************
*	ysubsclae
*	垂直方向をスケーリングする
*****************************************************/
static void ysubscale
    (int vf_size, VWORK_TBL *tbl2, int mdot, int *tlinetbl) {
	int i, j, k, dotsize, offset;

	offset = tbl2->ydotwidth / 2 - tbl2->ythin;
	dotsize = tbl2->_ydot;
	for (i = 0; i < dotsize; i++) {
		vtran[i] = (int)((long)i * vf_size / mdot) + offset;
	}

	for (i = 0; i < tlinecount; i++) {
		for (j = 0; j < dotsize; j++) {
			if (tlinetbl[i] <= vtran[j]) {
				if (j > 0) {
					if (vtran[j] - tlinetbl[i]
						>= tlinetbl[i] - vtran[j - 1]) {
						offset = tlinetbl[i] - vtran[j - 1];
						for (k = j - 1; k < dotsize; k++) {
							vtran[k] += offset;
						}
					}
					else {
						offset = vtran[j] - tlinetbl[i];
						for (k = j; k < dotsize; k++) {
							vtran[k] -= offset;
						}
					}
				}
				break;
			}
		}
	}

	for (i = 0; i < dotsize; i++) {
		vtran[i] += tbl2->ythin;
	}
}

/*********************************************************
*	makescale
*	scaling のための tran[](translate table) を作成する
*********************************************************/
void makescale(VPARA_TBL *tbl1, VWORK_TBL *tbl2)
{
	int i, width, _xdot, _ydot;
	int left, right, _xoffset = 0;
	static int xsize_save = 0, ysize_save = 0;

	width = _xdot = tbl1->width;
	_ydot = tbl1->height;
	left = tbl1->max_width + 1;
	right = 0x00;

	for (i = 0; i < linecount; i++) {
		if (left > linebuf[i].xl) {
			left = linebuf[i].xl;
		}
		if (right < linebuf[i].xr) {
			right = linebuf[i].xr;
		}
	}

	if (left < right) {
		while (mxrscaling(right) - mxlscaling(left) > width) {
			_xdot--;
		}
#ifdef XOFFSET
		_xoffset = (width - (mxrscaling(right) - mxlscaling(left))) / 2
			- mxlscaling(left);
#endif
	}

	if (_ydot <= 0 || _xdot <= 0) {
		return;
	}

	if (_ydot > ysize_save) {
		if (ysize_save)
			free(vtran);
		vtran = (int *)marea(sizeof(int) * _ydot);

		ysize_save = _ydot;
	}
	if (_xdot > xsize_save) {
		if (xsize_save)
			free(htran);
		htran = (int *)marea(sizeof(int) * width);

		xsize_save = _xdot;
	}

	for (i = _xdot; i < width; i++) {
		htran[i] = tbl1->max_width + 1;
	}

	tbl2->_xdot = _xdot;
	tbl2->_ydot = _ydot;
	tbl2->_xoffset = _xoffset;
}

/*********************************************************
*	thin_adj
*	xthin, ythin を調整する
*********************************************************/
static void thin_adj(VPARA_TBL *tbl1, VWORK_TBL *tbl2)
{
	int xt, yt, xthin, ythin, xdotwidth, ydotwidth;

	xdotwidth = tbl1->max_width / tbl2->_xdot;
	ydotwidth = tbl1->max_height / tbl2->_ydot;
	xt = tbl2->xthin;
	yt = tbl2->ythin;

	if (xt < xdotwidth) {
		xthin = xt / 2;
	}
	else if (xt % xdotwidth > xdotwidth / 2) {	/* 切り上げ	*/
		xthin = (xt % xdotwidth + 1) / 2;
	}
	else {
		xthin = (xt - (xt / xdotwidth - 1) * xdotwidth + 1) / 2;
		if (xthin >= xdotwidth / 2) {
			xthin = xdotwidth / 2 - 1;
		}
	}

	if (yt < ydotwidth) {
		ythin = yt / 2;
	}
	else if (yt % ydotwidth > ydotwidth / 2) {	/* 切り上げ	*/
		ythin = (yt % ydotwidth + 1) / 2;
	}
	else {
		ythin = (yt - (yt / ydotwidth - 1) * ydotwidth + 1) / 2;
		if (ythin >= ydotwidth / 2) {
			ythin = ydotwidth / 2 - 1;
		}
	}
	tbl2->xthin = xthin;
	tbl2->ythin = ythin;
	tbl2->xdotwidth = xdotwidth;
	tbl2->ydotwidth = ydotwidth;
}

/*********************************************************
*	maketran
*	translate table を作成する
*********************************************************/
static void maketran(VPARA_TBL *tbl1, VWORK_TBL *tbl2, int itemno)
{
	int i, j, flag, vf_size, dotwidth;
	int lv, mv, bv, sv, ms, bs, ss, half;
	static int tbl_size = 0;
	static int *tlinetbl;

	if (linecount > tbl_size) {
		if (tlinetbl != NULL) {
			free(tlinetbl);
		}
		tlinetbl = (int *)marea(sizeof(Line) * linecount);

		tbl_size = linecount;
	}

	/*	水平方向のスケーリング	*/

	tlinecount = 0;
	vf_size = tbl1->max_width + 1;
	dotwidth = tbl2->xdotwidth;
	for (i = 0; i < linecount; i++) {
		if (linebuf[i].itemno != itemno) {
			continue;
		}

		flag = linebuf[i].flag;
		if ((flag & VERTLINE) && (flag & HOPEN)
			&& (linebuf[i].dy > YTHICKLINEWIDTHDOUBLE)) {
			tlinetbl[tlinecount++] = linebuf[i].xl;
		}
	}
	if (tlinecount == 0) {
		xsubnorm(vf_size, tbl2);
		goto skip1;
	}

	inssort(tlinecount, tlinetbl);
	half = dotwidth / 2;
	mv = tbl2->_xdot;
	bv = sv = -1;
	while (bv < 0 || sv < 0) {
		lv = mv;
		if (mv <= 0) {
			xsubnorm(vf_size, tbl2);
			goto skip1;
		}

		xsubscale(vf_size, tbl2, mv, tlinetbl);
		ms = htran[tbl2->_xdot - 1];

		if (ms == vf_size - half) {
			bv = sv = mv;
			bs = ss = ms;
		}
		else if (ms > vf_size - half) {
			bv = mv;
			bs = ms;
			mv++;
		}
		else {
			sv = mv;
			ss = ms;
			mv--;
		}
	}
#if	TAKENEAR
	if (3 * (bs - (vf_size - half)) >= 2 * ((vf_size - half) - ss)) {
		/*	if(bs - (vf_size - half) >= (vf_size - half) - ss){	*/
		if ((vf_size - half) - ss > (tbl1->max_width + 1) / tbl2->_xdot / 2) {
			xsubnorm(vf_size, tbl2);
			goto skip1;
		}
		mv = sv;

		if (mv != lv) {
			xsubscale(vf_size, tbl2, mv, tlinetbl);
		}

		j = ((vf_size - half) - ss) / dotwidth / 2;
		if (j > 0) {
			for (i = 0; i < tbl2->_xdot - j; i++) {
				htran[i] = htran[i + j];
			}
			for (; i < tbl2->_xdot; i++) {
				htran[i] = htran[i - 1] + dotwidth;
			}
		}
	}
	else {
		if (bs - (vf_size - half) > (tbl1->max_width + 1) / tbl2->_xdot / 2) {
			xsubnorm(vf_size, tbl2);
			goto skip1;
		}
		mv = bv;

		if (mv != lv) {
			xsubscale(vf_size, tbl2, mv, tlinetbl);
		}
	}

#else
	if ((vf_size - half) - ss > (tbl1->max_width + 1) / tbl2->_xdot / 2) {
		xsubnorm(vf_size, tbl2);
		goto skip1;
	}

	if (sv != lv) {
		xsubscale(vf_size, tbl2, sv, tlinetbl);
	}

	j = ((vf_size - half) - ss) / dotwidth / 2;
	if (j > 0) {
		for (i = 0; i < tbl2->_xdot - j; i++) {
			htran[i] = htran[i + j];
		}
		for (; i < tbl2->_xdot; i++) {
			htran[i] = htran[i - 1] + dotwidth;
		}
	}
#endif
  skip1:

	/*	垂直方向のスケーリング	*/

	tlinecount = 0;
	vf_size = tbl1->max_height + 1;
	dotwidth = tbl2->ydotwidth;
	for (i = 0; i < linecount; i++) {
		if (linebuf[i].itemno != itemno) {
			continue;
		}

		flag = linebuf[i].flag;
		if ((flag & HORILINE) && (flag & VOPEN)
			&& (linebuf[i].dx > XTHICKLINEWIDTHDOUBLE)) {
			tlinetbl[tlinecount++] = linebuf[i].yu;
		}
	}
	if (tlinecount == 0) {
		ysubnorm(vf_size, tbl2);
		goto skip2;
	}

	inssort(tlinecount, tlinetbl);
	half = dotwidth / 2;
	mv = tbl2->_ydot;
	bv = sv = -1;
	while (bv < 0 || sv < 0) {
		lv = mv;
		if (mv <= 0) {
			ysubnorm(vf_size, tbl2);
			goto skip2;
		}

		ysubscale(vf_size, tbl2, mv, tlinetbl);
		ms = vtran[tbl2->_ydot - 1];

		if (ms == vf_size - half) {
			bv = sv = mv;
			bs = ss = ms;
		}
		else if (ms > vf_size - half) {
			bv = mv;
			bs = ms;
			mv++;
		}
		else {
			sv = mv;
			ss = ms;
			mv--;
		}
	}
#if	TAKENEAR
	if (3 * (bs - (vf_size - half)) >= 2 * ((vf_size - half) - ss)) {
		/*	if(bs - (vf_size - half) >= (vf_size - half) - ss){	*/
		if ((vf_size - half) - ss > (tbl1->max_height + 1) / tbl2->_ydot / 2) {
			ysubnorm(vf_size, tbl2);
			goto skip2;
		}
		mv = sv;

		if (mv != lv) {
			ysubscale(vf_size, tbl2, mv, tlinetbl);
		}

		j = ((vf_size - half) - ss) / dotwidth / 2;
		if (j > 0) {
			for (i = 0; i < tbl2->_ydot - j; i++) {
				vtran[i] = vtran[i + j];
			}
			for (; i < tbl2->_ydot; i++) {
				vtran[i] = vtran[i - 1] + dotwidth;
			}
		}
	}
	else {
		if (bs - (vf_size - half) > (tbl1->max_height + 1) / tbl2->_ydot / 2) {
			ysubnorm(vf_size, tbl2);
			goto skip2;
		}
		mv = bv;

		if (mv != lv) {
			ysubscale(vf_size, tbl2, mv, tlinetbl);
		}
	}

#else
	if ((vf_size - half) - ss > (tbl1->max_height + 1) / tbl2->_ydot / 2) {
		ysubnorm(vf_size, tbl2);
		goto skip2;
	}

	if (sv != lv) {
		ysubscale(vf_size, tbl2, sv, tlinetbl);
	}

	j = ((vf_size - half) - ss) / dotwidth / 2;
	if (j > 0) {
		for (i = 0; i < tbl2->_ydot - j; i++) {
			vtran[i] = vtran[i + j];
		}
		for (; i < tbl2->_ydot; i++) {
			vtran[i] = vtran[i - 1] + dotwidth;
		}
	}
#endif
  skip2:;
}

/********************************************************
*	searchtran
*	tran[](translate table) をもとに、ベクトルフォント
*	座標系からビットマップ座標系に変換する
*********************************************************/
static int searchtran(int *tran, int nelem, int val, int flag, int vf_size)
{
	int lp, rp, mp;

	lp = 0;
	rp = nelem - 1;

	if (val >= tran[rp]) {
		return (rp);
	}
	else if (val <= tran[lp]) {
		return (lp);
	}

	mp = (int)((long)nelem * val / vf_size);

	if (mp >= nelem)
		mp = nelem - 1;
	if (tran[mp] > val) {
		while (tran[--mp] > val);
		if (tran[mp] == val)
			rp = lp = mp;
		else {
			lp = mp;
			rp = mp + 1;
		}
	}
	else if (tran[mp] < val) {
		while (tran[++mp] < val);
		if (tran[mp] == val)
			rp = lp = mp;
		else {
			rp = mp;
			lp = mp - 1;
		}
	}
	else {
		rp = lp = mp;
	}

	switch (flag) {
	  case LEFT:
		  return (lp);
	  case RIGHT:
		  return (rp);
	  default:
		  return ((tran[rp] - val >= val - tran[lp]) ? lp : rp);
	}
}

/********************************************************
*	rasterexpand
*	実際にビットマップに展開する
*********************************************************/
static int rasterexpand(VPARA_TBL *tbl1, VWORK_TBL *tbl2)
{
	int i, j, x, y, vx, vy, b_width;
	int xl, xr, yu, yl, it, tm;
	int width, height, _xoffset, _xdot, _ydot;
	static int lcount_save = 0;
	BUFFER *rp;

	static int *ohlineindex = NULL, *chlineindex = NULL;
	static int *iolineindex = NULL, *iclineindex = NULL;
	int ohlinecount = 0, chlinecount = 0;
	int iolinecount, iclinecount, iomin, iomax, icmin, icmax;

	width = tbl1->width;
	height = tbl1->height;
	_xoffset = tbl2->_xoffset;
	_xdot = tbl2->_xdot;
	_ydot = tbl2->_ydot;

	if (linecount > lcount_save) {
		if (lcount_save) {
			free(ohlineindex);
			free(chlineindex);
			free(iolineindex);
			free(iclineindex);
		}
		ohlineindex = (int *)marea(sizeof(int) * linecount);
		chlineindex = (int *)marea(sizeof(int) * linecount);
		iolineindex = (int *)marea(sizeof(int) * linecount);
		iclineindex = (int *)marea(sizeof(int) * linecount);

		lcount_save = linecount;
	}
	b_width = (width + 7) / 8;

	for (it = 0; it < itemcount; it++) {
		maketran(tbl1, tbl2, it);

#if SQFLAG & 0x03
		rp = tbl1->buffer;
		iolinecount = iclinecount = 0;

		iomax = icmax = -1;
		iomin = icmin = tbl1->max_width + 2;

		for (i = 0; i < linecount; i++) {
			if (linebuf[i].itemno == it) {
				if (linebuf[i].flag & HOPEN) {
					iolineindex[iolinecount++] = i;
					if (linebuf[i].yl > iomax)
						iomax = linebuf[i].yl;
					if (linebuf[i].yu < iomin)
						iomin = linebuf[i].yu;
				}
				if (linebuf[i].flag & HCLOSE) {
					iclineindex[iclinecount++] = i;
					if (linebuf[i].yl > icmax)
						icmax = linebuf[i].yl;
					if (linebuf[i].yu < icmin)
						icmin = linebuf[i].yu;
				}
			}
		}

		for (y = 0; y < height; y++) {
			ohlinecount = chlinecount = 0;
			vy = vtran[y];

			if (iomin <= vy && vy <= iomax) {
				for (j = 0; j < iolinecount; j++) {
					i = iolineindex[j];
					/* もし vy の点が接点になっていると、vy の */
					/* 点を 2重に評価してしまう。従って、      */
					/* HOPEN の Line については、終点側の y値  */
					/* (y2)を優先させる。そのために、YREV の値 */
					/* を見て、yu, yl どちらが y2 に相当するの */
					/* か判断してから、重ならないように 1ずら  */
					/* す。                                    */
					yu = linebuf[i].yu;
					yl = linebuf[i].yl;
					if (linebuf[i].flag & YREV) {
						yl--;
					}
					else {
						yu++;
					}
					if (yu <= vy && vy <= yl) {
						/* ドットマトリックスに展開する時にどうせ */
						/* 精度が落ちるので、ここでは double で管 */
						/* 理する必要ない                         */
						if (linebuf[i].flag & XYREV)
							ohlineindex[ohlinecount++]
								= (int)(linebuf[i].xl
										+ (long)(linebuf[i].yl - vy)
										* linebuf[i].dx / linebuf[i].dy);
						else
							ohlineindex[ohlinecount++]
								= (int)(linebuf[i].xl
										+ (long)(vy - linebuf[i].yu)
										* linebuf[i].dx / linebuf[i].dy);
					}
				}
			}

			if (icmin <= vy && vy <= icmax) {
				for (j = 0; j < iclinecount; j++) {
					i = iclineindex[j];
					/* HCLOSE の Line については、始点側の y値 */
					/* (y1)を優先させる。そのために、YREV の値 */
					/* を見て、yu, yl どちらが y1 に相当するの */
					/* か判断してから、重ならないように 1ずら  */
					/* す。                                    */
					yu = linebuf[i].yu;
					yl = linebuf[i].yl;
					if (linebuf[i].flag & YREV) {
						yu++;
					}
					else {
						yl--;
					}
					if (yu <= vy && vy <= yl) {
						/* 同様にここでも double で管理しない */
						if (linebuf[i].flag & XYREV)
							chlineindex[chlinecount++]
								= (int)(linebuf[i].xl
										+ (long)(linebuf[i].yl - vy)
										* linebuf[i].dx / linebuf[i].dy);
						else
							chlineindex[chlinecount++]
								= (int)(linebuf[i].xl
										+ (long)(vy - linebuf[i].yu)
										* linebuf[i].dx / linebuf[i].dy);
					}
				}
			}

			if (ohlinecount != chlinecount) {
				error(WARNING,
						"[vfont] h-data error.(%#x) %s",
						tbl1->char_code, vfont[i]->v_font_name);
				return (FAILURE);
			}

			if (ohlinecount != 0 && chlinecount != 0) {
				inssort(ohlinecount, ohlineindex);
				inssort(chlinecount, chlineindex);
			}

			for (i = 0; i < ohlinecount; i++) {
				xl = searchtran(htran, _xdot, ohlineindex[i], RIGHT,
								tbl1->width);
				xr = searchtran(htran, _xdot, chlineindex[i], LEFT,
								tbl1->width);

#if SQFLAG & 0x01
				if (xl <= xr) {
					tm = _xoffset + xr + tbl1->xfat;
					if (tm >= _xdot) {
						tm = _xdot - 1;
					}
					xl += _xoffset;
					if (xl >= _xdot) {
						xl = _xdot - 1;
					}
					zs_qdraw(xl, tm, rp);
					/*					zs_qdraw( _xoffset + xl, tm, rp );*/
				}
#endif
#if SQFLAG & 0x02
				if (xl > xr) {
					xl = searchtran(htran, _xdot, (chlineindex[i]
							  + ohlineindex[i] + 1) / 2, NEAR, tbl1->width);
					tm = _xoffset + xl + tbl1->xfat;
					if (tm >= _xdot) {
						tm = _xdot - 1;
					}
					xl += _xoffset;
					if (xl >= _xdot) {
						xl = _xdot - 1;
					}
					zs_qdraw(xl, tm, rp);
					/*					zs_qdraw( _xoffset + xl, tm, rp );*/
				}
#endif
			}
			rp += b_width;
		}
#endif

#if SQFLAG & 0x0c
		rp = tbl1->buffer;

		iolinecount = iclinecount = 0;

		iomax = icmax = -1;
		iomin = icmin = tbl1->max_width + 2;

		for (i = 0; i < linecount; i++) {
			if (linebuf[i].itemno == it) {
				if (linebuf[i].flag & VOPEN) {
					iolineindex[iolinecount++] = i;

					if (linebuf[i].xr > iomax) {
						iomax = linebuf[i].xr;
					}
					if (linebuf[i].xl < iomin) {
						iomin = linebuf[i].xl;
					}
				}
				if (linebuf[i].flag & VCLOSE) {
					iclineindex[iclinecount++] = i;

					if (linebuf[i].xr > icmax) {
						icmax = linebuf[i].xr;
					}
					if (linebuf[i].xl < icmin) {
						icmin = linebuf[i].xl;
					}
				}
			}
		}

		for (x = 0; x < width; x++) {
			ohlinecount = chlinecount = 0;
			vx = htran[x];

			if (iomin <= vx && vx <= iomax) {
				for (j = 0; j < iolinecount; j++) {
					i = iolineindex[j];
					/* VOPEN の Line については、始点側の x値  */
					/* (x1)を優先させる。そのために、XREV の値 */
					/* を見て、xl, xr どちらが x1 に相当するの */
					/* か判断してから、重ならないように 1ずら  */
					/* す。                                    */
					xl = linebuf[i].xl;
					xr = linebuf[i].xr;
					if (linebuf[i].flag & XREV) {
						xl++;
					}
					else {
						xr--;
					}
					if (xl <= vx && vx <= xr) {
						if (linebuf[i].flag & XYREV)
							ohlineindex[ohlinecount++]
								= (int)(linebuf[i].yu
										+ (long)(linebuf[i].xr - vx)
										* linebuf[i].dy / linebuf[i].dx);
						else
							ohlineindex[ohlinecount++]
								= (int)(linebuf[i].yu
										+ (long)(vx - linebuf[i].xl)
										* linebuf[i].dy / linebuf[i].dx);
					}
				}
			}

			if (icmin <= vx && vx <= icmax) {
				for (j = 0; j < iclinecount; j++) {
					i = iclineindex[j];
					/* VCLOSE の Line については、終点側の x値 */
					/* (x2)を優先させる。そのために、XREV の値 */
					/* を見て、xl, xr どちらが x2 に相当するの */
					/* か判断してから、重ならないように 1ずら  */
					/* す。                                    */
					xl = linebuf[i].xl;
					xr = linebuf[i].xr;
					if (linebuf[i].flag & XREV) {
						xr--;
					}
					else {
						xl++;
					}
					if (xl <= vx && vx <= xr) {
						if (linebuf[i].flag & XYREV)
							chlineindex[chlinecount++]
								= (int)(linebuf[i].yu
										+ (long)(linebuf[i].xr - vx)
										* linebuf[i].dy / linebuf[i].dx);
						else
							chlineindex[chlinecount++]
								= (int)(linebuf[i].yu
										+ (long)(vx - linebuf[i].xl)
										* linebuf[i].dy / linebuf[i].dx);
					}
				}
			}

			if (ohlinecount != chlinecount) {
				error(WARNING, 
						"[vfont] v-data error.(%#x) %s",
						tbl1->char_code, vfont[i]->v_font_name);
				return (FAILURE);
			}

			if (ohlinecount != 0 && chlinecount != 0) {
				inssort(ohlinecount, ohlineindex);
				inssort(chlinecount, chlineindex);
			}

			for (i = 0; i < ohlinecount; i++) {
				yu = searchtran(vtran, _ydot, ohlineindex[i], RIGHT,
								tbl1->height);
				yl = searchtran(vtran, _ydot, chlineindex[i], LEFT,
								tbl1->height);

#if SQFLAG & 0x04
				if (yu <= yl) {
					tm = yl + tbl1->yfat;
					if (tm >= _ydot) {
						tm = _ydot - 1;
					}
					zs_vqdraw(yu, tm, rp, _xoffset + x, b_width);
				}
#endif
#if SQFLAG & 0x08
				if (yu > yl) {
					yu = searchtran(vtran, _ydot, (ohlineindex[i]
							 + chlineindex[i] + 1) / 2, NEAR, tbl1->height);
					tm = yu + tbl1->yfat;
					if (tm >= _ydot) {
						tm = _ydot - 1;
					}
					zs_vqdraw(yu, tm, rp, _xoffset + x, b_width);
				}
#endif
			}
		}
#endif
	}
	return (TRUE);
}

/********************************************************
*	MakeOutLine
*	出来上がったラスターを白抜き文字にする	92.11.10 冨家
*********************************************************/
static void farmovmem(BUFFER *d, BUFFER *s, int num)
{
	asm push ds
	    asm lds si, s
	    asm les di, d
	    asm mov cx, word ptr num
	    asm cld
	    asm rep movsb
	    asm pop ds
};

static void MakeOutLine(VPARA_TBL *tbl_ptr, uchar *Pattern, int PatHeight, int PatByteWidth)
{
	BUFFER *Tmp1;
	BUFFER *Tmp2;
	BUFFER *Tmp3;
	BUFFER *Tmp;
	BUFFER *OutBuf;
	BUFFER *PatPtr;
	unsigned char Mask1, Mask2, Mask3;
	int ByteWidth, v, h, BytePos1, BytePos2, BytePos3, PatBytePos, PatH;

	ByteWidth = (tbl_ptr->width + 7) / 8;

	if (ByteWidth + ByteWidth + ByteWidth > COMMON_SIZE)
		return;

	Tmp1 = (BUFFER *)((HUGE_BUF *)common_work);	/*このキャストは無意味？*/
	Tmp2 = Tmp1 + ByteWidth;
	Tmp3 = Tmp2 + ByteWidth;
	OutBuf = tbl_ptr->buffer;
	farmovmem(Tmp1, OutBuf, ByteWidth);
	OutBuf += ByteWidth;
	farmovmem(Tmp2, OutBuf, ByteWidth);

	PatH = 0;
	PatPtr = Pattern;

	for (v = tbl_ptr->height - 2; v > 0; v--) {
		farmovmem(Tmp3, OutBuf + ByteWidth, ByteWidth);
		Mask1 = 128;
		Mask2 = 64;
		Mask3 = 32;
		BytePos1 = BytePos2 = BytePos3 = PatBytePos = 0;
		for (h = tbl_ptr->width - 2; h > 0; h--) {
			if (Tmp2[BytePos2] & Mask2) {
				if (((Tmp1[BytePos2] & Mask2)
					 && (Tmp2[BytePos1] & Mask1)
					 && (Tmp2[BytePos3] & Mask3)
					 && (Tmp3[BytePos2] & Mask2))) {
					OutBuf[BytePos2] ^= (Mask2 & (PatPtr[PatBytePos] ^ 0xff));
				}
			}
			if ((Mask1 >>= 1) == 0) {
				Mask1 = 128;
				BytePos1++;
			}
			if ((Mask2 >>= 1) == 0) {
				Mask2 = 128;
				BytePos2++;
				if (++PatBytePos >= PatByteWidth)
					PatBytePos = 0;
			}
			if ((Mask3 >>= 1) == 0) {
				Mask3 = 128;
				BytePos3++;
			}
		}
		Tmp = Tmp1;
		Tmp1 = Tmp2;
		Tmp2 = Tmp3;
		Tmp3 = Tmp;
		OutBuf += ByteWidth;
		if (++PatH < PatHeight)
			PatPtr += PatByteWidth;
		else {
			PatH = 0;
			PatPtr = Pattern;
		}
	}
	return;
}

static void MakeFill(VPARA_TBL *tbl_ptr, uchar *Pattern, int PatHeight, int PatByteWidth)
{
	BUFFER *OutBuf;
	BUFFER *PatPtr;
	unsigned char Mask;
	int ByteWidth, v, h, BytePos, PatBytePos, PatH;

	ByteWidth = (tbl_ptr->width + 7) / 8;

	OutBuf = tbl_ptr->buffer;

	PatH = 0;
	PatPtr = Pattern;

	for (v = tbl_ptr->height; v > 0; v--) {
		Mask = 128;
		BytePos = PatBytePos = 0;
		for (h = tbl_ptr->width; h > 0; h--) {
			if (OutBuf[BytePos] & Mask) {
				OutBuf[BytePos] ^= (Mask & (PatPtr[PatBytePos] ^ 0xff));
			}
			if ((Mask >>= 1) == 0) {
				Mask = 128;
				BytePos++;
			}
		}
		OutBuf += ByteWidth;
		if (++PatH < PatHeight)
			PatPtr += PatByteWidth;
		else {
			PatH = 0;
			PatPtr = Pattern;
		}
	}
	return;
}

static uchar *GrayPattern[10] =
{
	"\x00\x00\x00\x00",
	"\x00\x22\x00\x00",
	"\x00\x22\x00\x88",
	"\x00\x22\x44\x00",
	"\x00\x66\x44\x00",
	"\x00\xaa\x00\x88",
	"\x00\x66\x66\x00",
	"\x00\xaa\x00\xaa",
	"\x11\xaa\x44\xaa",
	"\xee\x55\xbb\x55"
};

/********************************************************
*	draw_fine
*	精細モードで展開する( dvii のルーチン )
*********************************************************/
int draw_fine(VPARA_TBL *tbl_ptr)
{
	int i, h, itemflag, vcorrection;
	int xthin, ythin, stat;
	VWORK_TBL w_table;

	if (tbl_ptr->v_code > MAX_CODE)
		return (FAILURE);

	linecount = itemcount = 0;
	if (readdata(tbl_ptr, FINE_MODE) == FAILURE)
		return (FAILURE);

	if (tbl_ptr->rotation == 0 && !tategaki && !ptex_mode) {
		/* 回転したときは処理が面倒なので y offset の調整は省略 */
		vcorrection = tbl_ptr->yoffset;
		for (i = h = 0; i < linecount; i++) {
			linebuf[i].yu += vcorrection;
			linebuf[i].yl += vcorrection;
			h = max(h, linebuf[i].yl - (tbl_ptr->max_height + 1));
		}
		if (h > 0) {
			for (i = 0; i < linecount; i++) {
				linebuf[i].yu -= h;
				linebuf[i].yl -= h;
			}
		}
	}

	/* アイテムに分けて処理をするかどうか */
	if (20 < tbl_ptr->width && tbl_ptr->width < 128 &&
		20 < tbl_ptr->height && tbl_ptr->height < 128)
		itemflag = 1;
	else
		itemflag = 0;

	setflag(tbl_ptr, itemflag);
	getthinwidth(&xthin, &ythin);
	w_table.xthin = xthin;
	w_table.ythin = ythin;
	makescale(tbl_ptr, &w_table);
	thin_adj(tbl_ptr, &w_table);

	stat = rasterexpand(tbl_ptr, &w_table);
	if (tbl_ptr->dtype == VTRACE)
		MakeOutLine(tbl_ptr, "\0", 1, 1);
	else if (tbl_ptr->thin < 100 && tbl_ptr->thin >= 0) {
		if (tbl_ptr->dtype == BOTH)
			MakeOutLine(tbl_ptr, GrayPattern[tbl_ptr->thin / 10], 4, 1);
		else
			MakeFill(tbl_ptr, GrayPattern[tbl_ptr->thin / 10], 4, 1);
	}
	return stat;
}

/* end of all */
