////////////////////////////////////////////////////////////////////////////////
//  CQ出版社 インターフェース 2008年7月号
//
//  初音ミク（はちゅねみく）・アニメーション
//  画像変換ツール	Copyright(c)2008 Sumio Morioka
//  morioka@fb3.so-net.ne.jp,  http://www002.upp.so-net.ne.jp/morioka/
//
//  本プログラムコードの商業利用，および改変ファイルの二次配布を禁じます．それ以外の
//  利用はとくに制限しませんが，利用によって生じた被害・不利益などに対しては，一切
//  責任を負いませんのでご了承ください．
//
//  また，本プログラムのサンプルとして同梱される初音ミク，はちゅねミク，鏡音リン・レンの
//  画像（フォント・データも含む）は，クリプトン・フューチャー・メディア株式会社
// （http://www.crypton.co.jp）の著作物です．商業利用等については同社へ問い合わせてください．
//
//
// History:
// 1. Original coding by Sumio Morioka, 2008/04/18
//
////////////////////////////////////////////////////////////////////////////////

#ifndef	BMP2FRFONT_MAIN_C
#define	BMP2FRFONT_MAIN_C

#include <math.h>
#include "bmp2FRfontMain.h"

///////////////////////////////////////////////////////////
// global
///////////////////////////////////////////////////////////

uint8 bmp2FRfont_GLBL_plt_org_data[PALETTE_ENTRY][PALETTE_COLOR + 1] = {	// default palette
	// R8  G8   B8  ena
	{  0,   0,   0, 1},	// 0
	{  0,   0, 144, 1},	// 1
	{144,   0,   0, 1},	// 2
	{144,   0, 144, 1},	// 3
	{  0, 144,   0, 1},	// 4
	{  0, 144, 144, 1},	// 5
	{144, 144,   0, 1},	// 6
	{180, 180, 180, 1},	// 7
	{144, 144, 144, 1},	// 8
	{  0,   0, 255, 1},	// 9
	{255,   0,   0, 1},	// 10
	{255,   0, 255, 1},	// 11
	{  0, 255,   0, 1},	// 12
	{  0, 255, 255, 1},	// 13
	{255, 255,   0, 1},	// 14
	{255, 255, 255, 1}	// 15
};


///////////////////////////////////////////////////////////
// functions
///////////////////////////////////////////////////////////

void bmp2FRfont_initdata(BMP2FRFONT_DATA *data)
{
	data->width		= 0;
	data->height	= 0;
	data->bmp_data	= (uint8 *)NULL;
	data->rdata		= (uint8 *)NULL;
	data->gdata		= (uint8 *)NULL;
	data->bdata		= (uint8 *)NULL;
	data->plt_map	= (uint8 *)NULL;
}


void bmp2FRfont_cleardata(BMP2FRFONT_DATA *data)
{
	data->width		= 0;
	data->height	= 0;

	if (data->bmp_data != (uint8 *)NULL)
		free(data->bmp_data);
	if (data->rdata != (uint8 *)NULL)
		free(data->rdata);
	if (data->gdata != (uint8 *)NULL)
		free(data->gdata);
	if (data->bdata != (uint8 *)NULL)
		free(data->bdata);
	if (data->plt_map != (uint8 *)NULL)
		free(data->plt_map);

	data->bmp_data	= (uint8 *)NULL;
	data->rdata		= (uint8 *)NULL;
	data->gdata		= (uint8 *)NULL;
	data->bdata		= (uint8 *)NULL;
	data->plt_map	= (uint8 *)NULL;
}


int bmp2FRfont_v8to3(int x)
{
	int	ret;
	if (x < 20)			ret = 0;
	else if (x < 56)	ret = 1;
	else if (x < 92)	ret = 2;
	else if (x < 128)	ret = 3;
	else if (x < 164)	ret = 4;
	else if (x < 200)	ret = 5;
	else if (x < 236)	ret = 6;
	else 				ret = 7;
	return (ret);
}


int bmp2FRfont_v3to8(int x)
{
	int	ret	= 0;
	if (x == 0)			ret = 0;
	else if (x == 1)	ret = 36;
	else if (x == 2)	ret = 72;
	else if (x == 3)	ret = 108;
	else if (x == 4)	ret = 144;
	else if (x == 5)	ret = 180;
	else if (x == 6)	ret = 216;
	else if (x == 7)	ret = 255;
	return (ret);
}


void bmp2FRfont_get_histogram(BMP2FRFONT_DATA *data)
{
	const int		x_size		= data->width;
	const int		y_size		= data->height;
	int				x, y, i, j;
	unsigned int	hist[2][512];	// y0: num, y1: mark

	if (data == (BMP2FRFONT_DATA *)NULL)
		return;

	if (data->bmp_data == (uint8 *)NULL) {
		for (i = 0; i < 15; i++) {
			(data->hist)[i][0]	= 0;
			(data->hist)[i][1]	= 0;
			(data->hist)[i][2]	= 0;
			(data->hist)[i][3]	= 1;
		}
		return;
	}

	// clear histogram buffer
	for (i = 0; i < 512; i++) {
		hist[0][i]	= 0;		// #
		hist[1][i]	= 0;		// mark
	}

	// scan picture
	for (y = 0; y < y_size; y++) {
		for (x = 0; x < x_size; x++) {
			int		r, g, b;
			int		col;

			r	= bmp2FRfont_v8to3(bmp_rval(data, 1, x, y));
			g	= bmp2FRfont_v8to3(bmp_gval(data, 1, x, y));
			b	= bmp2FRfont_v8to3(bmp_bval(data, 1, x, y));

			col		= color_num(r, g, b);
			(hist[0][col])++;
		}
	}

	// make output
	for (i = 0; i < 16; i++) {
		int		maxpos, maxval;
		int		r, g, b;

		maxpos	= -1;
		maxval	= 0;

		for (j = 0; j < 512; j++) {
			if (hist[1][j] != 0)
				continue;
			if (maxpos == -1 || (unsigned int)maxval < hist[0][j]) {
				maxpos	= j;
				maxval	= hist[0][j];
			}
		}

		r	= bmp2FRfont_v3to8((maxpos >> 6) & 7);
		g	= bmp2FRfont_v3to8((maxpos >> 3) & 7);
		b	= bmp2FRfont_v3to8(maxpos & 7);

		hist[1][maxpos]	= 1;

		(data->hist)[i][0]	= r;		// R
		(data->hist)[i][1]	= g;		// G
		(data->hist)[i][2]	= b;		// B
		(data->hist)[i][3]	= 1;		// ena
	}
}


void bmp2FRfont_hsv_location(uint8 h1, uint8 s1, uint8 v1, double *d1_x, double *d1_y, double *d1_z)
{
	double	s1_loc, v1_loc, h1_deg;

	// calc dot1 location
	v1_loc	= ((double)v1 / 255.0f);
	s1_loc	= ( ((double)s1 / 255.0f) * ((double)v1 / 255.0f) );
	h1_deg	= 2.0f * PI * ((double)h1 / 255.0f);

	*d1_x	= cos(h1_deg) * s1_loc;
	*d1_y	= sin(h1_deg) * s1_loc;
	*d1_z	= v1_loc;
}


double bmp2FRfont_hsv_distance_from_loc(double d1_x, double d1_y, double d1_z, double d2_x, double d2_y, double d2_z)
{
	return (
			  ((d1_x - d2_x) * (d1_x - d2_x) * DIST_H_SCALE)
			+ ((d1_y - d2_y) * (d1_y - d2_y) * DIST_S_SCALE)
			+ ((d1_z - d2_z) * (d1_z - d2_z) * DIST_V_SCALE)
		);
}


double bmp2FRfont_hsv_distance(uint8 h1, uint8 s1, uint8 v1, uint8 h2, uint8 s2, uint8 v2)
{
	double	d1_x, d1_y, d1_z;
	double	d2_x, d2_y, d2_z;

	bmp2FRfont_hsv_location(h1, s1, v1, &d1_x, &d1_y, &d1_z);
	bmp2FRfont_hsv_location(h2, s2, v2, &d2_x, &d2_y, &d2_z);
	return (bmp2FRfont_hsv_distance_from_loc(d1_x, d1_y, d1_z, d2_x, d2_y, d2_z));
}


void bmp2FRfont_rgb2hsv(
	const uint8	indatR, 	// matrix data (uint8, INTERNAL FMT)
	const uint8	indatG, 	// matrix data (uint8, INTERNAL FMT)
	const uint8	indatB, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatH, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatS, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatV 	// matrix data (uint8, INTERNAL FMT)
)
{
	uint8	rval, gval, bval;
	uint8	max, min;
	int		ret;

	rval	= indatR;
	gval	= indatG;
	bval	= indatB;

	max	= (rval >= gval) ? rval : gval;
	max	= (bval >= max)  ? bval : max;

	min	= (rval <= gval) ? rval : gval;
	min	= (bval <= min)  ? bval : min;

	///////////////////////////////////////////
	// compute H
	///////////////////////////////////////////
	if (max == min)
		ret	= 0;	// (undef)
	else if (rval >= gval && rval >= bval)	// max: R
		ret	= ((gval - bval) * 60) / (max - min);
	else if (gval >= rval && gval >= bval)	// max: G
		ret	= ((bval - rval) * 60) / (max - min) + 120;
	else			// max: B
		ret	= ((rval - gval) * 60) / (max - min) + 240;

	while (ret < 0)
		ret	+= 360;
	ret	%= 360;

	*outdatH	= (ret * 255) / 360;

	///////////////////////////////////////////
	// compute S
	///////////////////////////////////////////
	if (max == 0)
		ret	= 0;	// (undef)
	else
		ret	= ((max - min) * 255) / max;

	*outdatS	= ret;

	///////////////////////////////////////////
	// compute V
	///////////////////////////////////////////
	*outdatV	= max;
}


void bmp2FRfont_hsv2rgb(
	const uint8	indatH, 	// matrix data (uint8, INTERNAL FMT)
	const uint8	indatS, 	// matrix data (uint8, INTERNAL FMT)
	const uint8	indatV, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatR, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatG, 	// matrix data (uint8, INTERNAL FMT)
	uint8		*outdatB 	// matrix data (uint8, INTERNAL FMT)
)
{
	uint8	hval, sval, vval;

	hval	= indatH;
	sval	= indatS;
	vval	= indatV;

	if (sval == 0) {
		*outdatR	= vval;
		*outdatG	= vval;
		*outdatB	= vval;
	}
	else {
		unsigned int	hi, f, p, q, t;
		uint8			pval, qval, tval;

		hi		= (hval / 43);										// 0-5 (43 == 256 / 6)
		f		= (hval - hi * 43) * 6;								// real_f * 255
		p		= (vval * (255 - sval)) / 255;						// (v/255 * (1 - s/255)) * 255
		q		= (vval * (255 - ((sval * f) / 255))) / 255;		// (v/255 * (1 - s/255 * f/255)) * 255
		t		= (vval * (255 - sval + ((sval * f) / 255))) / 255;	// (v/255 * (1 - s/255  + s/255 * f/255)) * 255

		if (p > 255)
			pval	= 255;
		else if (p < 0)
			pval	= 0;
		else
			pval	= (uint8)p;

		if (q > 255)
			qval	= 255;
		else if (q < 0)
			qval	= 0;
		else
			qval	= (uint8)q;

		if (t > 255)
			tval	= 255;
		else if (t < 0)
			tval	= 0;
		else
			tval	= (uint8)t;

		switch (hi) {
		case 0:
			*outdatR	= vval;
			*outdatG	= tval;
			*outdatB	= pval;
			break;
		case 1:
			*outdatR	= qval;
			*outdatG	= vval;
			*outdatB	= pval;
			break;
		case 2:
			*outdatR	= pval;
			*outdatG	= vval;
			*outdatB	= tval;
			break;
		case 3:
			*outdatR	= pval;
			*outdatG	= qval;
			*outdatB	= vval;
			break;
		case 4:
			*outdatR	= tval;
			*outdatG	= pval;
			*outdatB	= vval;
			break;
		default:
			*outdatR	= vval;
			*outdatG	= pval;
			*outdatB	= qval;
		}
	}
}


void bmp2FRfont_calc_gaussian_hsv(
	const uint8*	matR,
	const uint8*	matG,
	const uint8*	matB,
	const int		x,
	const int		y,
	const int		x_size,
	const int		y_size,
	uint8			*outdatH,
	uint8			*outdatS,
	uint8			*outdatV
)
{
	int		xp, yp, xpp, ypp;
	int		r_sum, g_sum, b_sum;

	if (matR == (uint8 *)NULL || matG == (uint8 *)NULL || matB == (uint8 *)NULL)
		return;

	r_sum	= 0;
	g_sum	= 0;
	b_sum	= 0;

	for (yp = y - 1; yp <= y + 1; yp++) {
		for (xp = x - 1; xp <= x + 1; xp++) {
			uint8	r, g, b;

			xpp	= xp;
			if (xp < 0)
				xpp	= -(xp);
			else if (xp >= x_size)
				xpp	= x_size - (xp + 1 - x_size);

			ypp	= yp;
			if (yp < 0)
				ypp	= -(yp);
			else if (yp >= y_size)
				ypp	= y_size - (yp + 1 - y_size);

			r	= mat_val(matR, xpp, ypp, x_size, y_size);
			g	= mat_val(matG, xpp, ypp, x_size, y_size);
			b	= mat_val(matB, xpp, ypp, x_size, y_size);

			// gaussian 0.4
//			if (xp == x && yp == y) {
//				r_sum	+= ((r * 760) / 900);
//				g_sum	+= ((g * 760) / 900);
//				b_sum	+= ((b * 760) / 900);
//			}
//			else if (xp == x || yp == y) {
//				r_sum	+= ((r * 33) / 900);
//				g_sum	+= ((g * 33) / 900);
//				b_sum	+= ((b * 33) / 900);
//			}
//			else {
//				r_sum	+= ((r * 2) / 900);
//				g_sum	+= ((g * 2) / 900);
//				b_sum	+= ((b * 2) / 900);
//			}

			// gaussian 0.5
//			if (xp == x && yp == y) {
//				r_sum	+= ((r * 560) / 900);
//				g_sum	+= ((g * 560) / 900);
//				b_sum	+= ((b * 560) / 900);
//			}
//			else if (xp == x || yp == y) {
//				r_sum	+= ((r * 75) / 900);
//				g_sum	+= ((g * 75) / 900);
//				b_sum	+= ((b * 75) / 900);
//			}
//			else {
//				r_sum	+= ((r * 10) / 900);
//				g_sum	+= ((g * 10) / 900);
//				b_sum	+= ((b * 10) / 900);
//			}

			// gaussian 0.525
//			if (xp == x && yp == y) {
//				r_sum	+= ((r * 512) / 900);
//				g_sum	+= ((g * 512) / 900);
//				b_sum	+= ((b * 512) / 900);
//			}
//			else if (xp == x || yp == y) {
//				r_sum	+= ((r * 83) / 900);
//				g_sum	+= ((g * 83) / 900);
//				b_sum	+= ((b * 83) / 900);
//			}
//			else {
//				r_sum	+= ((r * 14) / 900);
//				g_sum	+= ((g * 14) / 900);
//				b_sum	+= ((b * 14) / 900);
//			}

			// gaussian 0.55
			if (xp == x && yp == y) {
				r_sum	+= ((r * 472) / 900);
				g_sum	+= ((g * 472) / 900);
				b_sum	+= ((b * 472) / 900);
			}
			else if (xp == x || yp == y) {
				r_sum	+= ((r * 90) / 900);
				g_sum	+= ((g * 90) / 900);
				b_sum	+= ((b * 90) / 900);
			}
			else {
				r_sum	+= ((r * 17) / 900);
				g_sum	+= ((g * 17) / 900);
				b_sum	+= ((b * 17) / 900);
			}

			// gaussian 0.6
//			if (xp == x && yp == y) {
//				r_sum	+= ((r * 400) / 900);
//				g_sum	+= ((g * 400) / 900);
//				b_sum	+= ((b * 400) / 900);
//			}
//			else if (xp == x || yp == y) {
//				r_sum	+= ((r * 100) / 900);
//				g_sum	+= ((g * 100) / 900);
//				b_sum	+= ((b * 100) / 900);
//			}
//			else {
//				r_sum	+= ((r * 25) / 900);
//				g_sum	+= ((g * 25) / 900);
//				b_sum	+= ((b * 25) / 900);
//			}

			// gaussian 0.7
//			if (xp == x && yp == y) {
//				r_sum	+= ((r * 300) / 900);
//				g_sum	+= ((g * 300) / 900);
//				b_sum	+= ((b * 300) / 900);
//			}
//			else if (xp == x || yp == y) {
//				r_sum	+= ((r * 110) / 900);
//				g_sum	+= ((g * 110) / 900);
//				b_sum	+= ((b * 110) / 900);
//			}
//			else {
//				r_sum	+= ((r * 40) / 900);
//				g_sum	+= ((g * 40) / 900);
//				b_sum	+= ((b * 40) / 900);
//			}
		}
	}

	bmp2FRfont_rgb2hsv((uint8)r_sum, (uint8)g_sum, (uint8)b_sum, outdatH, outdatS, outdatV);
}


void bmp2FRfont_reduce_color(BMP2FRFONT_DATA *data, int forcePLTflag, uint8 forceR, uint8 forceG, uint8 forceB, int forcePLT)
{
	const int	x_size	= data->width;
	const int	y_size	= data->height;

	uint8		*buf0_0, *buf0_1, *buf0_2;			// original picture in HSV
	double		*buf0b_0, *buf0b_1, *buf0b_2;		// original picture in HSV-space coordinate
	uint8		*buf1_0, *buf1_1, *buf1_2;			// modified picture in RGB
	uint8		*buf1b_0, *buf1b_1, *buf1b_2;		// modified picture in HSV
	int			x, y, i, tblpos;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return;
	if (data->bmp_data == (uint8 *)NULL)
		return;

	//////////////////////////////////////////////////////////////
	// init
	//////////////////////////////////////////////////////////////
	srand(time(NULL));

	buf0_0	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf0_1	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf0_2	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);

	buf0b_0	= (double *)malloc(sizeof (double) * x_size * y_size);
	buf0b_1	= (double *)malloc(sizeof (double) * x_size * y_size);
	buf0b_2	= (double *)malloc(sizeof (double) * x_size * y_size);

	buf1_0	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf1_1	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf1_2	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);

	buf1b_0	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf1b_1	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	buf1b_2	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);

	if (buf0_0 == (uint8 *)NULL || buf0_1 == (uint8 *)NULL || buf0_2 == (uint8 *)NULL
			|| buf0b_0 == (double *)NULL || buf0b_1 == (double *)NULL || buf0b_2 == (double *)NULL
			|| buf1_0 == (uint8 *)NULL || buf1_1 == (uint8 *)NULL || buf1_2 == (uint8 *)NULL
			|| buf1b_0 == (uint8 *)NULL || buf1b_1 == (uint8 *)NULL || buf1b_2 == (uint8 *)NULL) {

		if (buf0_0 != (uint8 *)NULL)
			free(buf0_0);
		if (buf0_1 != (uint8 *)NULL)
			free(buf0_1);
		if (buf0_2 != (uint8 *)NULL)
			free(buf0_2);
		if (buf0b_0 != (double *)NULL)
			free(buf0b_0);
		if (buf0b_1 != (double *)NULL)
			free(buf0b_1);
		if (buf0b_2 != (double *)NULL)
			free(buf0b_2);
		if (buf1_0 != (uint8 *)NULL)
			free(buf1_0);
		if (buf1_1 != (uint8 *)NULL)
			free(buf1_1);
		if (buf1_2 != (uint8 *)NULL)
			free(buf1_2);
		if (buf1b_0 != (uint8 *)NULL)
			free(buf1b_0);
		if (buf1b_1 != (uint8 *)NULL)
			free(buf1b_1);
		if (buf1b_2 != (uint8 *)NULL)
			free(buf1b_2);
		return;
	}

	for (i = 0; i < 16; i++) {
		uint8	r, g, b, h, s, v;
		double	x, y, z;

		r	= (data->plt_data_RGB)[i][0];
		g	= (data->plt_data_RGB)[i][1];
		b	= (data->plt_data_RGB)[i][2];

		bmp2FRfont_rgb2hsv(r, g, b, &h, &s, &v);

		(data->plt_data_HSV)[i][0]	= h;
		(data->plt_data_HSV)[i][1]	= s;
		(data->plt_data_HSV)[i][2]	= v;
		(data->plt_data_HSV)[i][3]	= (data->plt_data_RGB)[i][3];	// ena

		bmp2FRfont_hsv_location(h, s, v, &x, &y, &z);

		(data->plt_data_HSV_coodinate)[i][0]	= x;
		(data->plt_data_HSV_coodinate)[i][1]	= y;
		(data->plt_data_HSV_coodinate)[i][2]	= z;
	}

	//////////////////////////////////////////////////////////////
	// copy indat to BUF0 (HSV)
	//////////////////////////////////////////////////////////////
	for (x = 0; x < x_size; x++) {
		for (y = 0; y < y_size; y++) {
			uint8	r, g, b, h, s, v;
			double	lx, ly, lz;

			r	= bmp_rval(data, 1, x, y);
			g	= bmp_gval(data, 1, x, y);
			b	= bmp_bval(data, 1, x, y);

			bmp2FRfont_rgb2hsv(r, g, b, &h, &s, &v);

			mat_val(buf0_0, x, y, x_size, y_size)	= h;
			mat_val(buf0_1, x, y, x_size, y_size)	= s;
			mat_val(buf0_2, x, y, x_size, y_size)	= v;

			bmp2FRfont_hsv_location(h, s, v, &lx, &ly, &lz);

			mat_val(buf0b_0, x, y, x_size, y_size)	= lx;
			mat_val(buf0b_1, x, y, x_size, y_size)	= ly;
			mat_val(buf0b_2, x, y, x_size, y_size)	= lz;
		}
	}

	//////////////////////////////////////////////////////////////
	// initial assignment to BUF1
	//////////////////////////////////////////////////////////////
	{
		int		first;
		double	mindist, curdist;

		for (x = 0; x < x_size; x++) {
			for (y = 0; y < y_size; y++) {
				double	x1, y1, z1;

				x1	= mat_val(buf0b_0, x, y, x_size, y_size);
				y1	= mat_val(buf0b_1, x, y, x_size, y_size);
				z1	= mat_val(buf0b_2, x, y, x_size, y_size);

				first		= 1;
				tblpos		= 0;

				for (i = 0; i < 16; i++) {
					double	x2, y2, z2;

					if ((data->plt_data_HSV)[i][3] == 0)
						continue;

					bmp2FRfont_hsv_location((data->plt_data_HSV)[i][0], (data->plt_data_HSV)[i][1], (data->plt_data_HSV)[i][2], &x2, &y2, &z2);
					curdist	= bmp2FRfont_hsv_distance_from_loc(x1, y1, z1, x2, y2, z2);

					if (first == 1 || curdist < mindist) {
						first		= 0;
						mindist		= curdist;
						tblpos		= i;
					}
				}

				mat_val(buf1b_0, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][0];
				mat_val(buf1b_1, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][1];
				mat_val(buf1b_2, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][2];

				mat_val(buf1_0, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][0];
				mat_val(buf1_1, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][1];
				mat_val(buf1_2, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][2];
			}
		}
	}

	//////////////////////////////////////////////////////////////
	// improve assignment of BUF1
	//////////////////////////////////////////////////////////////
	{
		int		first;
		double	mindist, curdist;
		int		offset_x, offset_y, pattern, l;

		for (l = 0; l < 3; l++) {
			for (pattern = 0; pattern < 2 * 2; pattern++) {
				offset_x	= pattern / 2;
				offset_y	= pattern % 2;

				for (y = offset_y; y < y_size; y += 2) {
					for (x = offset_x; x < x_size; x += 2) {
						uint8	org_h, org_s, org_v;
						double	org_x, org_y, org_z;

						uint8	bak_h, bak_s, bak_v;
						uint8	bak_r, bak_g, bak_b;

						org_h	= mat_val(buf0_0, x, y, x_size, y_size);
						org_s	= mat_val(buf0_1, x, y, x_size, y_size);
						org_v	= mat_val(buf0_2, x, y, x_size, y_size);

						org_x	= mat_val(buf0b_0, x, y, x_size, y_size);
						org_y	= mat_val(buf0b_1, x, y, x_size, y_size);
						org_z	= mat_val(buf0b_2, x, y, x_size, y_size);

						bak_r	= mat_val(buf1_0, x, y, x_size, y_size);
						bak_g	= mat_val(buf1_1, x, y, x_size, y_size);
						bak_b	= mat_val(buf1_2, x, y, x_size, y_size);

						bak_h	= mat_val(buf1b_0, x, y, x_size, y_size);
						bak_s	= mat_val(buf1b_1, x, y, x_size, y_size);
						bak_v	= mat_val(buf1b_2, x, y, x_size, y_size);

						if (org_h != bak_h || org_s != bak_s || org_v != bak_v) {
							first		= 1;
							tblpos		= 0;

							for (i = 0; i < 16; i++) {
								uint8	ave_h, ave_s, ave_v;
								double	ave_x, ave_y, ave_z;

								if ((data->plt_data_HSV)[i][3] == 0)
									continue;

								// replace dot
								mat_val(buf1_0, x, y, x_size, y_size)	= (data->plt_data_RGB)[i][0];
								mat_val(buf1_1, x, y, x_size, y_size)	= (data->plt_data_RGB)[i][1];
								mat_val(buf1_2, x, y, x_size, y_size)	= (data->plt_data_RGB)[i][2];

								// compute new HSV
								bmp2FRfont_calc_gaussian_hsv(buf1_0, buf1_1, buf1_2, x, y, x_size, y_size, &ave_h, &ave_s, &ave_v);

								bmp2FRfont_hsv_location(ave_h, ave_s, ave_v, &ave_x, &ave_y, &ave_z);
								curdist	= bmp2FRfont_hsv_distance_from_loc(ave_x, ave_y, ave_z, org_x, org_y, org_z);

								if (first == 1 || curdist < mindist) {
									first		= 0;
									mindist		= curdist;
									tblpos		= i;
								}
							}	// for i

							if (rand() % 3 != 1) {
								mat_val(buf1_0, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][0];
								mat_val(buf1_1, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][1];
								mat_val(buf1_2, x, y, x_size, y_size)	= (data->plt_data_RGB)[tblpos][2];

								mat_val(buf1b_0, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][0];
								mat_val(buf1b_1, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][1];
								mat_val(buf1b_2, x, y, x_size, y_size)	= (data->plt_data_HSV)[tblpos][2];
							}
							else {
								mat_val(buf1_0, x, y, x_size, y_size)	= bak_r;
								mat_val(buf1_1, x, y, x_size, y_size)	= bak_g;
								mat_val(buf1_2, x, y, x_size, y_size)	= bak_b;

								mat_val(buf1b_0, x, y, x_size, y_size)	= bak_h;
								mat_val(buf1b_1, x, y, x_size, y_size)	= bak_s;
								mat_val(buf1b_2, x, y, x_size, y_size)	= bak_v;
							}
						}	// if

					}	// for x
				}	// for y
			}	// for pattern
		}	// for l
	}	// block

	//////////////////////////////////////////////////////////////
	// copy BUF1 to outdat
	//////////////////////////////////////////////////////////////
	if (data->rdata != (uint8 *)NULL)
		free(data->rdata);
	if (data->gdata != (uint8 *)NULL)
		free(data->gdata);
	if (data->bdata != (uint8 *)NULL)
		free(data->bdata);
	if (data->plt_map != (uint8 *)NULL)
		free(data->plt_map);

	data->rdata		= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	data->gdata		= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	data->bdata		= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);
	data->plt_map	= (uint8 *)malloc(sizeof (uint8) * x_size * y_size);

	for (x = 0; x < x_size; x++) {
		for (y = 0; y < y_size; y++) {
			uint8	r, g, b;
			uint8	ro, go, bo;
			int i;

			r	= mat_val(buf1_0, x, y, x_size, y_size);
			g	= mat_val(buf1_1, x, y, x_size, y_size);
			b	= mat_val(buf1_2, x, y, x_size, y_size);

			for (i = 0; i < 16; i++) {
				if ((data->plt_data_RGB)[i][0] == r && (data->plt_data_RGB)[i][1] == g && (data->plt_data_RGB)[i][2] == b)
					break;
			}
			if (i == 16)
				i = 0;

			ro	= bmp_rval(data, 1, x, y);
			go	= bmp_gval(data, 1, x, y);
			bo	= bmp_bval(data, 1, x, y);

			if (forcePLTflag != 0 && forceR == ro && forceG == go && forceB == bo) {	// draw grid line
				mat_val((data->rdata), x, y, x_size, y_size)	= ro;
				mat_val((data->gdata), x, y, x_size, y_size)	= go;
				mat_val((data->bdata), x, y, x_size, y_size)	= bo;

				mat_val((data->plt_map), x, y, x_size, y_size)	= forcePLT;
			}
			else {
				mat_val((data->rdata), x, y, x_size, y_size)	= r;
				mat_val((data->gdata), x, y, x_size, y_size)	= g;
				mat_val((data->bdata), x, y, x_size, y_size)	= b;

				mat_val((data->plt_map), x, y, x_size, y_size)	= i;
			}
		}
	}

	free(buf0_0);
	free(buf0_1);
	free(buf0_2);

	free(buf0b_0);
	free(buf0b_1);
	free(buf0b_2);

	free(buf1_0);
	free(buf1_1);
	free(buf1_2);

	free(buf1b_0);
	free(buf1b_1);
	free(buf1b_2);
}


int bmp2FRfont_read24bitBMP(const char *fname, BMP2FRFONT_DATA *data)
{
	FILE	*fpin;
	int		ch, v0, v1, v2, v3;
	int		i, h, v;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return (0);
	if (fname == (char *)NULL)
		return (0);
	if ((fpin = fopen(fname, "rb")) == (FILE *)NULL)
		return (0);

	/////////////////////////
	// file header
	/////////////////////////
	ch	= fgetc(fpin);
	if ((char)ch != 'B') {
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}
	ch	= fgetc(fpin);
	if ((char)ch != 'M') {
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}
	for (i = 0; i < 12; i++)
		ch	= fgetc(fpin);		// skip file header

	/////////////////////////
	// information header
	/////////////////////////
	ch	= fgetc(fpin);
	if ((char)ch != 40) {
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}
	ch	= fgetc(fpin);
	ch	= fgetc(fpin);
	ch	= fgetc(fpin);

	// biWidth
	v0	= fgetc(fpin) & 0xFF;
	v1	= fgetc(fpin) & 0xFF;
	v2	= fgetc(fpin) & 0xFF;
	v3	= fgetc(fpin) & 0xFF;
	data->width	= v3 * 0x1000000 + v2 * 0x10000 + v1 * 0x100 + v0;
	// biHeight
	v0	= fgetc(fpin) & 0xFF;
	v1	= fgetc(fpin) & 0xFF;
	v2	= fgetc(fpin) & 0xFF;
	v3	= fgetc(fpin) & 0xFF;
	data->height	= v3 * 0x1000000 + v2 * 0x10000 + v1 * 0x100 + v0;

	ch	= fgetc(fpin);	// biPlanes
	ch	= fgetc(fpin);	// biPlanes

	ch	= fgetc(fpin);	// biBitCount
	if (ch != 24) {		// (not 24bit bmp)
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}
	ch	= fgetc(fpin);	// biBitCount

	ch	= fgetc(fpin);	// biCompression
	if (ch != 0) {		// (compression ON)
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}
	ch	= fgetc(fpin);	// biCompression
	ch	= fgetc(fpin);	// biCompression
	ch	= fgetc(fpin);	// biCompression

	for (i = 0; i < 20; i++)
		ch	= fgetc(fpin);		// skip info header

	/////////////////////////
	// read data body
	/////////////////////////
	if ((data->bmp_data = (uint8 *)malloc(sizeof (uint8) * 3 /* RGB */ * data->width * data->height)) == (uint8 *)NULL) {
		fclose(fpin);
		data->width		= 0;
		data->height	= 0;
		data->bmp_data	= (uint8 *)NULL;
		return (0);
	}

	for (v = (int)(data->height) - 1; v >= 0; v--) {
		for (h = 0; h < (int)(data->width); h++) {
			int 	r, g, b;
			int		offset;
			// NOTE: 'BGR' order
			b	= fgetc(fpin) & 0xFF;
			g	= fgetc(fpin) & 0xFF;
			r	= fgetc(fpin) & 0xFF;

			// NOTE: data order
			offset	= (v * data->width * 3 /* RGB */) + (h * 3 /* RGB */);
			*(data->bmp_data + offset + 0)	= r;
			*(data->bmp_data + offset + 1)	= g;
			*(data->bmp_data + offset + 2)	= b;
		}

		// data word boundary
		if (((data->width * 3) % 4) != 0) {
			int	i;
			for (i = 0; i < 4 - (((int)(data->width) * 3) % 4); i++)
				fgetc(fpin);		// cut dummy byte
		}
	}

	fclose(fpin);
	return (!0);
}


int bmp2FRfont_write24bitBMP(const char *fname, BMP2FRFONT_DATA *data, int grid_flag)
{
	FILE			*fpout;
	int				h, v;
	unsigned int	val;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return (0);
	if (data->rdata == (uint8 *)NULL || data->gdata == (uint8 *)NULL || data->bdata == (uint8 *)NULL)
		return (0);

	if (fname == (char *)NULL)
		return (0);
	if ((fpout = fopen(fname, "wb")) == (FILE *)NULL)
		return (0);

	/////////////////////////
	// file header
	/////////////////////////
	fprintf(fpout, "BM");
	{
		//	val	= 14 + 40 + data->width * data->height * 3;	// file size
		int	width_bnd;
		if (((data->width * 3) % 4) != 0)
			width_bnd	= (data->width * 3) + 4 - ((data->width * 3) % 4);
		else
			width_bnd	= data->width * 3;
		val	= 14 + 40 + width_bnd * data->height;	// file size
	}
	fprintf(fpout, "%c%c%c%c", (val % 0x100) & 0xFF, (val / 0x100) & 0xFF, (val / 0x10000) & 0xFF, (val / 0x1000000) & 0xFF);
	fprintf(fpout, "%c%c%c%c%c%c%c%c", 0, 0, 0, 0, 0x36, 0, 0, 0);

	/////////////////////////
	// information header
	/////////////////////////
	fprintf(fpout, "%c%c%c%c", 0x28, 0, 0, 0);	// header size
	fprintf(fpout, "%c%c%c%c", (data->width % 0x100) & 0xFF, (data->width / 0x100) & 0xFF, (data->width / 0x10000) & 0xFF, (data->width / 0x1000000) & 0xFF);
	fprintf(fpout, "%c%c%c%c", (data->height % 0x100) & 0xFF, (data->height / 0x100) & 0xFF, (data->height / 0x10000) & 0xFF, (data->height / 0x1000000) & 0xFF);
	fprintf(fpout, "%c%c", 1, 0);				// # of plane
	fprintf(fpout, "%c%c", 24, 0);				// bit count
	fprintf(fpout, "%c%c%c%c", 0, 0, 0, 0);		// compression
	val	= data->width * data->height * 3;		// data size
	fprintf(fpout, "%c%c%c%c", (val % 0x100) & 0xFF, (val / 0x100) & 0xFF, (val / 0x10000) & 0xFF, (val / 0x1000000) & 0xFF);
	fprintf(fpout, "%c%c%c%c", 0, 0, 0, 0);
	fprintf(fpout, "%c%c%c%c", 0, 0, 0, 0);
	fprintf(fpout, "%c%c%c%c", 0, 0, 0, 0);
	fprintf(fpout, "%c%c%c%c", 0, 0, 0, 0);

	/////////////////////////
	// write data body
	/////////////////////////
	for (v = (int)(data->height) - 1; v >= 0; v--) {
		for (h = 0; h < (int)(data->width); h++) {
			uint8	r, g, b;

			r	= mat_val(data->rdata, h, v, data->width, data->height);
			g	= mat_val(data->gdata, h, v, data->width, data->height);
			b	= mat_val(data->bdata, h, v, data->width, data->height);

			if (grid_flag == 1) {
				if ((h % 24) == 23 || (v % 32) == 31) {
					r	^= 0xFF;
					g	^= 0xFF;
					b	^= 0xFF;
				}
			}

			// NOTE: 'BGR' order
			fprintf(fpout, "%c%c%c", b, g, r);
		}

		// data word boundary
		if (((data->width * 3) % 4) != 0) {
			int	i;
			for (i = 0; i < 4 - (((int)(data->width) * 3) % 4); i++)
				fprintf(fpout, "%c", 0xFF);
		}
	}

	fclose(fpout);
	return (!0);
}


void bmp2FRfont_write_palette_C(char *fname, BMP2FRFONT_DATA *data)
{
	FILE	*fp;
	int		i;
	int		r, g, b, val;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return;
	if (fname == (char *)NULL)
		return;
	if ((fp = fopen(fname, "wt")) == (FILE *)NULL)
		return;

	fprintf(fp, "\t// definition of color palette\n");
	for (i = 0; i < 16; i++) {
		r	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][0]);
		g	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][1]);
		b	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][2]);

		val	= r * 0x100 + g * 0x10 + b;

		fprintf(fp, "\tIO_OSDC.IO_OSD_PLR%d.hword=0x%04x;\n", i, val);
	}
	fclose(fp);
}


void bmp2FRfont_write_palette_PAL(char *fname, BMP2FRFONT_DATA *data)
{
	FILE	*fp;
	int		i;
	int		r, g, b, val;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return;
	if (fname == (char *)NULL)
		return;
	if ((fp = fopen(fname, "wt")) == (FILE *)NULL)
		return;

	fprintf(fp, "[General]\n");
	fprintf(fp, "Name=MB91310(OSDC)\n");
	fprintf(fp, "Num=16\n");
	fprintf(fp, "[Palette]\n");

	for (i = 0; i < 16; i++) {
		r	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][0]);
		g	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][1]);
		b	= bmp2FRfont_v8to3((data->plt_data_RGB)[i][2]);

		r	= bmp2FRfont_v3to8(r);
		g	= bmp2FRfont_v3to8(g);
		b	= bmp2FRfont_v3to8(b);

//		val	= r * 0x10000 + g * 0x100 + b;
		val	= b * 0x10000 + g * 0x100 + r;		// PLT format

		fprintf(fp, "Color%02d=%d\n", i, val);
	}
	fclose(fp);
}


void bmp2FRfont_write_flash_hex(char *fname, int chrnum_start, int box_flag, int from_x, int from_y, int to_x, int to_y, BMP2FRFONT_DATA *data)
{
	int				addr;
	int				fontx, fonty, font_xsize, font_ysize;
	unsigned int	x, y;
	uint8			*plt_tbl;
	FILE			*fp;

	if (data == (BMP2FRFONT_DATA *)NULL)
		return;
	if (data->plt_map == (uint8 *)NULL)
		return;

	if (fname == (char *)NULL)
		return;
	if ((fp = fopen(fname, "wt")) == (FILE *)NULL)
		return;

	font_xsize	= data->width;
	if ((font_xsize % 24) != 0)
		font_xsize	= font_xsize + (24 - (font_xsize % 24));

	font_ysize	= data->height;
	if (font_ysize % 32 != 0)
		font_ysize	= font_ysize + (32 - (font_ysize % 32));

	plt_tbl	= (uint8 *)malloc(sizeof (uint8) * font_xsize * font_ysize);	// (RESIZED BUFFER)

	// make palette matrix (RESIZED)
	for (y = 0; y < (unsigned int)font_ysize; y++) {
		for (x = 0; x < (unsigned int)font_xsize; x++) {
			if (x >= data->width && y >= data->height)
				mat_val(plt_tbl, x, y, font_xsize, font_ysize)	= mat_val(plt_tbl, data->width - 1, data->height - 1, font_xsize, font_ysize);
			else if (x >= data->width)
				mat_val(plt_tbl, x, y, font_xsize, font_ysize)	= mat_val(plt_tbl, data->width - 1, y, font_xsize, font_ysize);
			else if (y >= data->height)
				mat_val(plt_tbl, x, y, font_xsize, font_ysize)	= mat_val(plt_tbl, x, data->height - 1, font_xsize, font_ysize);
			else
				mat_val(plt_tbl, x, y, font_xsize, font_ysize)	= mat_val(data->plt_map, x, y, data->width, data->height);
		}
	}

	fprintf(fp, "S01300004F53444330303234203138783230203545\n");

	addr	= 0x180000 + 0x400 * chrnum_start;

	if (box_flag == 0) {
		from_x	= 1;
		from_y	= 1;
		to_x	= (data->width) / 24;
		to_y	= (data->height) / 32;
	}
	for (fonty = from_y - 1; fonty <= to_y - 1 && fonty < font_ysize / 32; fonty++) {
		for (fontx = from_x - 1; fontx <= to_x - 1 && fontx < font_xsize / 24; fontx++) {
		// scan FONT
			unsigned int	xseg;
			for (xseg = 0; xseg < 4; xseg++) {
				for(y = 0; y < 32; y += 2) {
					int		sum, i;
					char	linebuf[256], tmp[16];
					uint8	val;

					// write LINE
					sprintf(linebuf, "14%06X", addr);
					strcat(linebuf, "FFFFFFFFFF");
					for (i = 0; i < 6; i++) {
						val	= mat_val(plt_tbl, fontx * 24 + xseg * 6 + i, fonty * 32 + y, font_xsize, font_ysize);
						sprintf(tmp, "%X", val);
						strcat(linebuf, tmp);
					}
					strcat(linebuf, "FFFFFFFFFF");
					for (i = 0; i < 6; i++) {
						val	= mat_val(plt_tbl, fontx * 24 + xseg * 6 + i, fonty * 32 + y + 1, font_xsize, font_ysize);
						sprintf(tmp, "%X", val);
						strcat(linebuf, tmp);
					}

					// compute sum
					sum	= 0;
					for (i = 0; i < 40; i += 2) {
						int		val;
						char	nstr[3];
						nstr[0]	= *(linebuf + i);
						nstr[1]	= *(linebuf + i + 1);
						nstr[2]	= '\0';
						sscanf(nstr, "%02X", &val);
						sum	+= val;
					}

					sum	&=	0xFF;
					sum	^=	0xFF;

					fprintf(fp, "S2%s%02X\n", linebuf, sum & 0xFF);

					addr	+= 0x10;
				}
			}
		}
	}

	fprintf(fp, "S804000000FB\n");

	fclose(fp);
	free(plt_tbl);
}

#endif	// BMP2FRFONT_MAIN_C
// end of file
