/*
BUFFER *make_bmp(BUFFER *s, int b_width, int s_width, int s_height, 
	int top_skip, int xbit, int ybit);

  Get BMP format(in MS Windows) from Bit Map Buffer

  x_bit  x  y_bit   ->  one point
	Using gray scale in 16 color modes if  x_bit*y_bit > 1.
 
	s       : top        of bit map buffer
	b_width : bit  width of bit map buffer for a line
	s_width : byte width of bit map buffer for a line
	s_height: bit height of bit map buffer

	top_skip: bits to be skipped in the left side after shrinked
	x_bit   : width  by bits to be shrinked to a point (gray scaled)
	y_bit   : height by bits to be shrinked to a point (gray scaled)

	rerurn  : top        of BMP buffer


void free_bmp(BUFFER *bmp);
  release the BMP buffer obtained by make_bmp();

*/

#define	marea_bmp(x)	marea(x)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "dd.h"
#include "err.h"
#include "dviread.h"
#include "inter.h"

#define	ROOM	8

/* epsbox.c */
extern int f_bcolor;

/*	buffer.c	*/
void *marea(unsigned int);

BUFFER *make_bmp(BUFFER *s, int b_width, int s_width, int s_height, 
	int top_skip, int xbit, int ybit);

void free_bmp(BUFFER *bmp);

static void make_gray(BUFFER *s, BUFFER *d, int s_width, int s_height, 
	int top_skip, int b_width, int d_width, int xbit, int ybit);

/* 
	static void move4(int *s, BUFFER *d, int b_width);
*/

static void move16(int *s, BUFFER *d, int b_width);
static void gray1(BUFFER *p, int b_width, int count, int s_width);
static void gray2(BUFFER *p, int b_width, int count, int s_width);
static void gray3(BUFFER *p, int b_width, int count, int s_width);
static void gray4(BUFFER *p, int b_width, int count, int s_width);
static void gray8(BUFFER *p, int b_width, int count, int s_width);
static void grayn(BUFFER *p, int b_width, int x_count, int y_count,
 int s_width);
static void graydown(int b_width, int sgrade, int dgrade);

static const uchar GrayCount[256] =		/* number of bits ON */
{
	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};

static int Shift[9] = 
{
	0, 0, 8, 15, 21, 26, 30, 33, 35
};

static int BitMask[36] =
{
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
	0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03,
	0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07,
	0xf0, 0x78, 0x3c, 0x1e, 0x0f,
	0xf8, 0x7c, 0x3e, 0x1f,
	0xfc, 0x7e, 0x3f,
	0xfe, 0x7f,
	0xff
};

static uchar Gray[17] =
{
	255, 226, 204, 185, 167, 149, 133, 116, 
	101,  85,  70,  56,  41,  27,  13,   0, 
	0
};

static uchar CGray[17] =
{
	255, 226, 204, 185, 167, 149, 133, 116, 
	101,  85,  70,  56,  41,  27,  13,   0, 
	0
};

static int *gray_line;
static int color_bit = 4;

typedef	struct	tagBITMAPFILEHEADER {
	uchar	bfType[2];
	uchar	bfSize[4];
	uchar	bfReserved1[2];
	uchar	bfReserved2[2];
	uchar	bfOffBits[4];
} BITMAPFILEHEADER;

typedef	struct	tagBITMAPINFOHEADER {
	uchar	biSize[4];
	uchar	biWidth[4];
	uchar	biHeight[4];
	uchar	biPlanes[2];
	uchar	biBitCount[2];
	uchar	biCompression[4];
	uchar	biSizeImage[4];
	uchar	biXPelsPerMeter[4];
	uchar	biYPelsPerMeter[4];
	uchar	biCirUsed[4];
	uchar	biCirImportant[4];
} BITMAPINFOHEADER;

static BITMAPFILEHEADER bf = {
	{'B','M'},			/* ID ('B', 'M') */
	{0,0,0,0},			/* file size */
	{0,0},				/* reserved */
	{0,0},				/* reserved */
	{0,0,0,0}			/* sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
							 + COL_CNT * 4 */
};

static BITMAPINFOHEADER bi = {
	{sizeof(BITMAPINFOHEADER) & 0xff,
	 sizeof(BITMAPINFOHEADER) >> 8,
	 0,0},						/* header size */
	{0,0,0,0},					/* width */
	{0,0,0,0},					/* height */
	{1,0},						/* planes */
	{1,0},						/* bit count 1 or 4 or 8 or 24 */
	{0,0,0,0},					/* compression */
	{0,0,0,0},					/* size of image */
	{0,0,0,0},					/* x-resolution */
	{0,0,0,0},					/* y-resolution */
	{0,0,0,0},					/* COL_CNT: color index count */
	{0,0,0,0}					/* important index count */
};

uint to_jxl(BUFFER *, BUFFER *, int, int);

void FreeGray(void)
{
	Free0(gray_line);
	gray_line = NULL;
}

BOOL ChangeColor(BUFFER *s, int type)
{
	static BUFFER	keep[8];
	int i;

/*	if(type >= 0 && ((BITMAPINFOHEADER *)s)->biBitCount[0] !=1) */
/*		return FALSE; */
	s += sizeof(BITMAPINFOHEADER);
	switch(type){
		case 0:		/* push and black */
			memcpy(keep, s, 8);
			if(s[4] == 0xff && s[5] == 0xff && s[6] == 0xff)
				return FALSE;
			for(i=0; i < 3; i++)
				*s++ = 0xff;
			*s = 0;
			for(i=0; i < 3; i++)
				*s++ = 0;
			*s = 0;
			break;
		case 1:		/* push and white */
			memcpy(keep, s, 8);
			if(s[4] == 0 && s[5] == 0 && s[6] == 0)
				return FALSE;
			for(i=0; i < 3; i++)
				*s++ = 0;
			*s++ = 0;
			for(i=0; i < 3; i++)
				*s++ = 0xff;
			*s = 0;
			break;
		case -1:	/* pop */
			memcpy(s, keep, 8);
	}
	return TRUE;
}

BUFFER *KeepBMPBuf(BUFFER *src, int xbyte, int xsize, int ysize, 
	int fc, int bc, BUFFER **at)
{
	int i, j, xx, yy, grade, col, xbit, ybit, scan, xxx, image_size, *pi;
	int c0[3], c1[3];
	BUFFER *buf, *pt, *des, *pts;
	int atbuf[0x400];

	if(!xbyte){
		if(ysize > 0)
			ysize = 1;
		scan = *src;
		for(j=(xsize+7)/8; --j >= 1 && src[j] == scan; );
		if(j == 0)
			xsize = 1;
	}
	scan = ((xsize + 31) / 8) & 0xfffc;		/* WORD boundary */
	bi.biBitCount[0] = 1;
	save_long(&bi.biWidth[0],        xsize);
	save_long(&bi.biHeight[0],       ysize);
	save_long(&bi.biSizeImage[0],    image_size = scan*ysize);
	save_long(&bi.biCirUsed[0],      2);
	save_long(&bi.biCirImportant[0], 2);
	buf = (char*)marea(sizeof(BITMAPINFOHEADER) + 8 + image_size);
	if(buf != NULL){
		memcpy(buf, &bi, sizeof(BITMAPINFOHEADER));
		pt = buf + sizeof(BITMAPINFOHEADER);

		if(bc != 0xffffff && bc != 0)		/* transparent background */
			col = (SetGamma(0) < 0)?0:0xff;
		else
			col = bc & 0xff;
		*pt++ = col;
		*pt++ = col;
		*pt++ = col;
		*pt++ = 0;
								/* set  c0: background  c1: forground */
		col=bc;
		pi=c0;
		for(i=0; i<3; i++){
			pi[i] = col & 0xff;
			col >>= 8;
		}
		col=fc;
		pi=c1;
		for(i=0; i<3; i++){
			*pt++ = pi[i] = col & 0xff;
			col >>= 8;
		}
		*pt++ = 0;
							/* copy the bit image of a character */
		xx = (xsize + 7)/8;
		for(j=0; j< ysize; j++){
			pts = src + xbyte*j;
			des = pt + (ysize-j-1)*scan; 
			for(i=0; i < xx; i++)
				*des++ = *pts++;
		}

		*at = NULL;				/* antialieasing */
		if(   xsize !=1 && ysize !=1 && xsize < 0x400 - ROOM
		  && (xbit = GetXGray()) > 1 && (ybit = GetYGray()) > 1){
			xx = (xsize + xbit - 1)/xbit;
			yy = (ysize + ybit - 1)/ybit;
			xxx = ((xx + 7) / 2) & 0xfffc;		/* WORD boundary */
			image_size = xxx*yy;
			bi.biBitCount[0] = 4;
			save_long(&bi.biWidth[0],        xx);
			save_long(&bi.biHeight[0],       yy);
			save_long(&bi.biSizeImage[0],    image_size);
			save_long(&bi.biCirUsed[0],      16);
			save_long(&bi.biCirImportant[0], 16);
			*at = des =
				(BUFFER *)marea(sizeof(BITMAPINFOHEADER)+64+image_size);
			if(des != NULL){
				memcpy(des, &bi, sizeof(BITMAPINFOHEADER));
				des += sizeof(BITMAPINFOHEADER);
				grade = xbit*ybit;
				if(grade > 15)
					grade = 15;
										/* transparent background */
				if(bc != 0xffffff && bc != 0)
					col = (SetGamma(0) < 0)?0:0xff;
				else
					col = bc & 0xff;
				*des++ = col;
				*des++ = col;
				*des++ = col;
				*des++ = 0;
										/* pallet for antialiasing */
				for(j=1; j < 16; j++){
					col = (j*16 + grade - 1)/grade;
					col = (col > 15)?CGray[15]:CGray[col];
					for(i=0; i<3; i++)
						*des++ = (c0[i]*col + c1[i]*(255-col))/255;
					*des++ = 0;
				}
								/* keep bit image under antialiasing */
				gray_line = atbuf;
				grade = color_bit;
				color_bit = 4;
				make_gray(src, des + (yy-1)*xxx, xbyte, ysize, 0, xx, -xxx, 
					xbit, ybit);
				color_bit = grade;
				gray_line = NULL;
			}
		}
	}
	return buf;
}

int sizeofBMP(BUFFER *s)
{
	int size, i;
	BITMAPINFOHEADER *bi_info = (BITMAPINFOHEADER *)s;

	size = 0;

	if(bi_info != NULL){
		for(i = 3; i >= 0; )
			size = (size << 8)|bi_info->biSizeImage[i--];
		size += (bi_info->biCirUsed[0] << 2) + (bi_info->biCirUsed[1] << 10);
	}
	return size;
}

#if 1
typedef int LONG;

typedef struct  tagRECT
{
	LONG left;
	LONG top;
	LONG right;
	LONG bottom;
}	RECT;

void GetBoundingBox(BUFFER *s, int b_width, int s_width, int s_height, 
	RECT *rc)
{
	int wbyte, x0, y0, xb0, xb1, x1, y1, i, j, k, b;
	BUFFER *pt;

	wbyte = (b_width+7)/8;
	for(y0 = 0; y0 < s_height; y0++){
		pt = s + s_width * y0;
		for(x0 = 0; x0 < wbyte; x0++){
			if(pt[x0])
				goto hit1;
		}
	}
hit1:
	if(x0 >= wbyte){
		rc->left = rc->right = rc->top = rc->bottom = -1;
		return;
	}
	for(y1 = s_height - 1; ; y1--){
		pt = s + s_width * y1;
		for(x1 = 0; x1 < wbyte; x1++){
			if(pt[x1])
				goto hit2;
		}
	}
hit2:
	xb0 = 0;
	xb1 = 7;
	if(x1 < x0)
		x0 = x1;
	for(i = y0; i <= y1; i++){
		pt = s + s_width * i;
		for(j = 0; j <= x0; j++){
			if(pt[j]){
				for(k=0, b=0x80; !(pt[j] & b); k++, b /= 2);
				if(j < x0){
					x0 = j;
					xb0 = k;
				}else if(k < xb0)
					xb0 = k;
				continue;
			}
		}
	}
	x1 = x0;
	for(i = y0; i <= y1; i++){
		pt = s + s_width * i;
		for(j = wbyte-1; j >= x1; j--){
			if(pt[j]){
				for(k=7, b=0x01; !(pt[j] & b); k--, b *= 2);
				if(j > x1){
					x1 = j;
					xb1 = k;
				}else if(k > xb1)
					xb1 = k;
				continue;
			}
		}
	}
	rc->left = x0*8 + xb0;
	rc->right = x1*8 + xb1;
	if(rc->right > b_width -1)
		rc->right = b_width;
	rc->top = y0;
	rc->bottom = y1;
}
#endif


static int WeightedMean(int x, int x1, int x2, int y1, int y2)
{
	if(x1 == x2)
		return y1;
	return (((x2 - x)*y1 + (x - x1)*y2)/(x2 - x1)) & 0xff;
}

BUFFER *make_bmp(BUFFER *s, int b_width, int s_width, int s_height, 
	int top_skip, int xbit, int ybit)
{
	int bcount, xsize, ysize, scan, col_cnt, i, j, grade, xset;
	int col, col1, col2, colb1, colb2, colb3;
	int head_size, image_size;
	BUFFER *bmp_buf;
	BUFFER *d;
#if	0
	d = (char*)marea(s_width*s_height);
	error(14, "%d->%d", s_width*s_height, to_jxl(s, d, s_width*8, s_height));
	exit(1);
#endif
	xset = (xbit > 0)?xbit:-xbit;
	grade = xset*ybit;
	if(xset == 0 || ybit <= 0)
		error(PROGRAM_STOP, "Internal error(make_bmp)");
	if(grade == 1 && xbit > 0)
		bcount = 1;				/* Black & White: 2 colors = 1 bit */
	else
		bcount = color_bit;		/* Gray Scale:   16 grades = 4 bits
												 16 or 256 color mode */

	xsize = (b_width  + xset - 1)/xset;		/* xsize of bmp buffer */
	ysize = (s_height + ybit - 1)/ybit;		/* ysize of bmp buffer */

	switch(bcount){
		case 1:
			scan = ((xsize + 31) / 8) & 0xfffc;		/* WORD boundary */
			col_cnt = 2;
			break;

		case 4:
			scan = ((xsize + 7) / 2) & 0xfffc;		/* WORD boundary */
			col_cnt = 16;
			break;

		case 8:
			scan = (xsize + 3) & 0xfffc;
			col_cnt = 256;
			break;
												/* Currently not used */
		case 24:
			scan = (xsize * 3 + 3) & 0xfffc;
			col_cnt = 0;
			break;
	}
									/* Set header data */
	head_size = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+col_cnt*4;
	image_size = scan*ysize;

	save_long(&bf.bfOffBits[0],      head_size);
	save_long(&bf.bfSize[0],         head_size + image_size);

	bi.biBitCount[0] = bcount;
	save_long(&bi.biWidth[0],        xsize);
	save_long(&bi.biHeight[0],       ysize);
	save_long(&bi.biSizeImage[0],    image_size);

	save_long(&bi.biCirUsed[0],      col_cnt);
	save_long(&bi.biCirImportant[0], col_cnt);

#if	0
	error(WARNING, "xsize %d, ysize %d, scan %d, head %d, image %d[%d:%d]\n",
		xsize, ysize, scan, head_size, image_size, xbit, ybit);
#endif

												/* get BMP buffer */
	bmp_buf = (char *)marea_bmp(head_size + image_size);
	memset(bmp_buf, 0, head_size + image_size);
	gray_line = (int *)marea((b_width + ROOM)* sizeof(int));

												/* copy header */
	memcpy(bmp_buf, &bf, sizeof(BITMAPFILEHEADER));
	d = bmp_buf + sizeof(BITMAPFILEHEADER);
	memcpy(d, &bi, sizeof(BITMAPINFOHEADER));
	d += sizeof(BITMAPINFOHEADER);

									/* set color pallet */
	colb1 = f_bcolor & 0xff;
	colb2 = (f_bcolor >> 8) & 0xff;
	colb3 = (f_bcolor >> 16) & 0xff;
	col1 = Gray[0];
	col = col2 = Gray[15];
	if(grade > 15) grade = 15;
	for(i = 0; i < col_cnt; i++){
		j = ((i&0xf)*15 + grade - 1)/grade;
		if(i == 0x80)
			col = col1;
		if(j > 15)
			j = 15;
		j = Gray[j];
#ifdef WIN32
		if(f_bcolor >= 0){
			if(i >= 0x10 && i < 0x80){	/* colored background */
				*d++ = (i & 0x10)?col2:j;
				*d++ = (i & 0x20)?col2:j;
				*d++ = (i & 0x40)?col2:j;
			}else{						/* normal & colored character */
				*d++ = WeightedMean(j, col1, col2, colb1, (i&0x10)?col1:col2);
				*d++ = WeightedMean(j, col1, col2, colb2, (i&0x20)?col1:col2);
				*d++ = WeightedMean(j, col1, col2, colb3, (i&0x40)?col1:col2);
			}
		}else
#endif
		{
			*d++ = (i & 0x10)?col:j;
			*d++ = (i & 0x20)?col:j;
			*d++ = (i & 0x40)?col:j;
		}
		*d++ = 0;
	}
										/* Make gray scaled BMP data */
	if(s)
		make_gray(s, d + (ysize - 1)*scan, s_width, s_height, top_skip, 
		xsize, -scan, xbit, ybit);
	FreeGray();
	return bmp_buf;
}

int SetColor(int color)
{
	if(color == 4 || color == 8)
		color_bit = color;
	return color_bit;
}

void gray_gamma(int scale, int top, int end, int grade)
{
	int i, diff;

	diff = end - top;
	if(--grade <= 0)
		return;

	Gray[0] = top;
	for(i = 1; i <= grade; i++)
		Gray[i] = 
			(int)(top + floor(exp(log((double)i/grade)*scale/1000)*diff));
	CGray[0] = 255;
	for(i = 1; i <= 16; i++)
		CGray[i] = 
			(int)(255-floor(exp(log((double)i/16)*scale/1000)*255));
}

/*
 *			Make gray scaled shrinked image from Bit Map Buffer 
 *							no trim under bits

	s       : top of bit map buffer
	s_width : byte width of bit map buffer for a line   (may be negative)
	s_height: bit height of bit map buffer
	d       : top of gray scale buffer
	top_skip: number of bits to be skipped after gray_scaled
	b_width : bit width of gray scale buffer
	d_width : byte width of gray scale buffer for a line (may be negative)
	xbit    : number of bit width to be a point
	ybit    : number of bit height to be a point
 */

static void make_gray(BUFFER *s, BUFFER *d, int s_width, int s_height, 
	int top_skip, int b_width, int d_width, int xbit, int ybit)
{
	int	i, j, count, num, grade;
	BUFFER *p;
	BUFFER *q;
	int *r;

	r = gray_line + top_skip;
	grade = xbit*ybit;
	if(grade < 0)
		grade = xbit = ybit = 1;
	else if(grade <= 1){
		s += top_skip/8;
		top_skip &= 7;
		count = (b_width + 7)/8;
		if(top_skip){
			num = 8 - top_skip;
			while(s_height-- > 0){
				p = s;
				q = d;
				for(j=count; j-- > 0;){
					i = *p++ << top_skip;
					*q++ = i | (*p >> num);
				}
				s += s_width;
				d += d_width;
			}
		}else while(s_height-- > 0){
			memcpy(d, s, count);
			s += s_width;
			d += d_width;
		}
		return;
	}
	for(num = xbit; !(num & 1); num >>= 1);
	while(top_skip >= num * 8){
		top_skip -= num * 8;
		s += num;
	}
	for(num = s_height; num > 0; num -= ybit){
			count = (num <= ybit)?num:ybit;
		for(i = top_skip + b_width + ROOM; --i >= 0; )
			gray_line[i] = 0;
		switch(xbit){
		  case 1:
			gray1(s, b_width, count, s_width);
			break;

		  case 2:
			gray2(s, b_width, count, s_width);
			break;

		  case 3:
			gray3(s, b_width, count, s_width);
			break;

		  case 4:
			gray4(s, b_width, count, s_width);
			break;

		  case 8:
			gray8(s, b_width, count, s_width);
			break;
			
		  default:
			grayn(s, b_width, xbit, count, s_width);
			break;
		}
		if(grade > 15)
			graydown(b_width, grade, 15);		/* within 16 colors */
		move16(r, d, b_width);
		s += s_width*ybit;
		d += d_width;
	}
}

static void graydown(int b_width, int sgrade, int dgrade)
{
	int	i;

	for(i=0; i < b_width; i++)
		gray_line[i] = gray_line[i]*dgrade/sgrade;
}

static void move16(int *s, BUFFER *d, int b_width)
{
	if(color_bit == 8){
		while(b_width-- > 0)
			*d++ = *s++;
			return;
	}
	while(b_width > 1){			/* 16 colors */
		*d     = *s++ << 4;
		*d++  |= *s++;
		b_width -= 2;
	}
	if(b_width)
		*d     = *s << 4;
}

static void gray1(BUFFER *p, int b_width, int count, int s_width)
{
	int i, j;
	int *buf;
	BUFFER *q;

	while(count-- > 0){
		q = p;
		p += s_width;
		buf = gray_line;
		for(i = b_width; i > 0; i -= 8){
			for(j = 0x100; (j >>= 1) != 0; ){
				if(*q & j) *buf += 1;
				buf++;
			}
			q++;
		}
	}
}

static void gray2(BUFFER *p, int b_width, int count, int s_width)
{
	int i;
	int *buf, *b;
	BUFFER *q;

	b = gray_line;
	if(count > 1){
		while(b_width > 0){
			if(*p | *(p+s_width))
				break;
			p++;
			b += 4;
			b_width -= 4;
		}
	}
	while(count-- > 0){
		q = p;
		p += s_width;
		buf = b;
		for(i = b_width; i > 0; i -= 4 ){
			*buf++ += GrayCount[*q   & 0xc0];
			*buf++ += GrayCount[*q   & 0x30];
			*buf++ += GrayCount[*q   & 0x0c];
			*buf++ += GrayCount[*q++ & 0x03];
		}
	}
}

static void gray4(BUFFER *p, int b_width, int count, int s_width)
{
	int i;
	int *buf, *b;
	BUFFER *q;

	b = gray_line;
	if(count > 3){
		while(b_width > 0){
			if(  *(q=p) || *(q += s_width) 
			  || *(q += s_width) || *(q + s_width) )
				break;
			p++;
			b += 2;
			b_width -= 2;
		}
	}
	while(count-- > 0){
		q  = p;
		p += s_width;
		buf = b;
		for(i = b_width; i > 0; i -= 2){
			*buf++ += GrayCount[*q   & 0xf0];
			*buf++ += GrayCount[*q++ & 0x0f];
		}
	}
}

static void gray8(BUFFER *p, int b_width, int count, int s_width)
{
	int i;
	int *buf;
	BUFFER *q;

	while(count-- > 0){
		q  = p;
		p += s_width;
		buf = gray_line;
		for(i = b_width; i-- > 0; )
			*buf++ += GrayCount[*q++];
	}
}

static void gray3(BUFFER *p, int b_width, int count, int s_width)
{
	int i, s_left;
	int *buf;
	BUFFER *q;

	while(count-- > 0){
		q  = p;
		p += (s_left = s_width);
		buf = gray_line;
		i = b_width;
		while(1){
			*buf++ += GrayCount[*q   & 0xe0];
			if(--i <= 0) break;

			*buf++ += GrayCount[*q   & 0x1c];
			if(--i <= 0) break;

			*buf   += GrayCount[*q++ & 0x03];
			if(--s_left <= 0)	break;
			*buf++ += GrayCount[*q   & 0x80];
			if(--i <= 0) break;

			*buf++ += GrayCount[*q   & 0x70];
			if(--i <= 0) break;

			*buf++ += GrayCount[*q   & 0x0e];
			if(--i <= 0) break;

			*buf   += GrayCount[*q++ & 0x01];
			if(--s_left <= 0)	break;
			*buf++ += GrayCount[*q   & 0xc0];
			if(--i <= 0) break;

			*buf++ += GrayCount[*q   & 0x38];
			if(--i <= 0) break;

			*buf++ += GrayCount[*q++ & 0x07];
			if(--s_left <= 0)	break;
			if(--i <= 0) break;
		}
	}
}

static void grayn(BUFFER *p, int b_width, int x_count, int y_count,
 int s_width)
{
	int i, bm, s, r, n, s_left;
	int *buf;
	BUFFER *q;

	buf = gray_line;
	s = 0;
	r = x_count;
	s_left = s_width;
	while(1){
		if(r > (n = 8 - s)){
			bm = BitMask[Shift[n] + s];
			q = p++;
			for(i = y_count; i-- > 0; q += s_width)
				*buf += GrayCount[*q & bm];
			if(--s_left<= 0)
				break;
			s = 0;
			r -= n;
		}else{
			bm = BitMask[Shift[r] + s];
			q = p;
			for(i = y_count; i-- > 0; q += s_width)
				*buf += GrayCount[*q & bm];
			buf++;
			if(n != r)
				s += r;
			else{
				p++;
				s = 0;
				if(s_left-- <= 0)
					break;
			}
			r = x_count;
			if(--b_width <= 0) break;
		}
	}
}

#if	0
static void move4(int *s, BUFFER *d, int b_width)
{
	int data;

	while(b_width > 3){			/* 4 colors */
		data  = *s++ << 6;
		data |= *s++ << 4;
		data |= *s++ << 2;
		data |= *s++;
		*d++  = data;
		b_width -= 4;
	}
	while(1){
		if(b_width-- <= 0) break;
		data  = *s++ << 6;
		if(b_width-- <= 0) break;
		data |= *s++ << 4;
		if(b_width-- <= 0) break;
		data |= *s++ << 2;
	}
}
#endif

#if 0
int comp_line(uchar *s, uchar *d, int len)
{
	uchar *p, *s0, *s1, *s2;
	int	count;

	s2 = s + len;
	p = d;
	*p++ = 0;
	*p++ = 0;

	for(s0 = s1 = s; ++s < s1; ){
		if(*s1 == *s)
			continue;
		if(s - s1 < 4){
			s1 = s;
			continue;
		}
		while(s1 - s0 > 254){
			*p++ =   0;
			*p++ = 254;
			for(count = 254; count-- > 0; )
				*p++ = *s0++;
		}
		if(s1 - s0 > 2){
			*p++ = 0;
			*p++ = s1 - s0;
			while(s1 - s0 > 1){
				*p++ = *s0++;
				*p++ = *s0++;
			}
			if(s1 > s0){
				*p++ = *s0++;
				*p++ = 0;
			}
		}else{
			if(s1 - s0 == 2 && *s0 = *(s0 + 1)){
				*p++ = 2;
				*p++ = *s0;
				s0 = s1;
			}
			while(s1 > s0){
				*p++ = 1;
				*p++ = *s0++
			}
		}while(s - s1 > 255){
			*p++ = 255;
			*p++ = *s1;
			s1 += 255;
		}
		*p++ = s - s1;
		*p++ = *s1;
		s0 = s1 = s;
	}
	while(s1 - s0 > 254){
		*p++ =   0;
		*p++ = 254;
		for(count = 254; count-- > 0; )
			*p++ = *s0++;
	}
	if(s1 - s0 > 2){
		*p++ = 0;
		*p++ = s1 - s0;
		while(s1 - s0 > 1){
			*p++ = *s0++;
			*p++ = *s0++;
		}
		if(s1 > s0){
			*p++ = *s0++;
			*p++ = 0;
		}
	}else{
		if(s1 - s0 == 2 && *s0 = *(s0 + 1)){
			*p++ = 2;
			*p++ = *s0;
			s0 = s1;
		}
		while(s1 > s0){
			*p++ = 1;
			*p++ = s0++
		}
	}
}
#endif

#if	0
#define	REPT			14
#define	DYN_F			12
#define	DYN_2			((12 - DYN_F)*16 + 15 + DYN_F + 1)	/* 28 */
#define	DYN_L			((13 - DYN_F)*16 + DYN_F - 15)	/* 13 */


void set_num(uchar **ptr, long num)
{
	int i, count, temp[0x10];

	if (num <= DYN_F) {
		*((*ptr)++) = num;
	}
	else {
		if (num <= DYN_2) {
			num -= (DYN_F + 1);
			*((*ptr)++) = num / 16 + (DYN_F + 1);
			*((*ptr)++) = (num & 15);
		}
		else {
			for (num -= DYN_L, count = 0; num != 0; num /= 16) {
				temp[count++] = (num & 15);
			}
			for (i = count; i > 1; i--)
				*((*ptr)++) = 0;
			while (count-- > 0)
				*((*ptr)++) = temp[count];
		}
	}
}

static void to_xor(uchar *f_buf, int b_wid, int len)
{
	unsigned char *w_ptr;

	for (w_ptr = f_buf + b_wid*(len-1); w_ptr > f_buf; ){
		w_ptr--;
		w_ptr[b_wid] ^= *w_ptr;
	}
}

static unsigned int to_jxl(uchar *f_buf, uchar *w2_buf, int width, int len)
{
	unsigned char *ptr;
	unsigned char *w_ptr;
	int data, shift, b_wid, c_len, c_wid, f_b;
	long b_cnt, b_bcnt, b_brep;
	int  total;
	static uint bit[8] = {128, 64, 32, 16, 8, 4, 2, 1};

	ptr = f_buf;
	b_cnt = 1;
	b_brep = f_b = 0;
	b_wid = (width + 7) / 8;
	total = b_wid * len;

	to_xor(f_buf, b_wid, len);

	for (w_ptr = w2_buf, c_len = 0; c_len <= len; c_len++) {
		if (c_len == len){
			data = 0;
			goto last;
		}
		for (c_wid = 0; c_wid < width; c_wid++) {
			if ((shift = (c_wid & 7)) == 0) {
				data = *ptr++;
			}
last:		if ((data & bit[shift]) != 0) {
				if (f_b != 0)			/* on -> on */
					b_bcnt++;
				else {
					f_b = 1;			/* off -> on */
					b_bcnt = 0;
				}
			}
			else{
				if (f_b != 0){			/* on -> off */
					if (b_bcnt){
						if (b_bcnt == b_brep)
							*w_ptr++ = 15;
						else{
							*w_ptr++ = REPT;
							set_num(&w_ptr, b_brep = b_bcnt);
						}
					}
					set_num(&w_ptr, b_cnt);
					b_cnt = 1;
					f_b = 0;
				}
				else					/* off -> off */
					b_cnt++;
			}
		}
		if (w_ptr - w2_buf >= total) break;
	}
	w_ptr[0] = 0;
	return (w_ptr - w2_buf);
}

#endif
