/*
	ARTemis (Graphic Editor for FM-TOWNS)
	(c) MATSUUCHI Ryosuke 1992

	plt16.c

    16色モードにおいてパレットどうしの混色や近似パレット検索を行うときの処理
*/


#include <stdio.h>
#include <malloc.h>
#include <egb.h>
#include <memory.h>

#include <math2.h>
#include <decimal.h>

#include "ge.h"
#include "plt16.h"


#if NO16==0


//     (local) 指定したパレット番号のパレットを得る
//-----------------------------------------------------------------------


static grb_t _getplt(int plt)
{
	unsigned char pal_[4+8*16];
	EGB_getPalette(1,(char *)pal_);
	return	(int)((pal_[4+8*plt+4+2]>>4)&15)*256 +
			(int)((pal_[4+8*plt+4+1]>>4)&15)* 16 +
			(int)((pal_[4+8*plt+4  ]>>4)&15);
}


//     (local) 指定したパレット番号のパレットを設定する
//-----------------------------------------------------------------------


static void _setplt(int plt, grb_t grb)
{
	char pal_[4+8*16];
	DWORD(pal_ + 0) = 1;
	DWORD(pal_ + 4) = plt;
	BYTE (pal_ + 8) = (grb&15) << 4;
	BYTE (pal_ + 9) = ((grb>>4)&15)<<4;
	BYTE (pal_ +10) = ((grb>>8)&15)<<4;
	BYTE (pal_ +11) = 0;
	EGB_palette(_egbwork,0,pal_);
}


//     (local) grb_t カラーどうしの「相違度(difference)」を計算する
//-----------------------------------------------------------------------


/*
	static int _coldiff(grb_t grb1, grb_t grb2)
	{
		static int exp2[] =
			{ 0,1,4,9,16,25,36,49,64,81,
			  100,121,144,169,196,225,256 };
		return	  exp2[_abs((int)((grb1>>8)&15)-(int)((grb2>>8)&15))]
				+ exp2[_abs((int)((grb1>>4)&15)-(int)((grb2>>4)&15))]
				+ exp2[_abs((int)(grb1&15)-(int)(grb2&15))];
	}
*/


static void RGB_YCrCb(int *grb, deci *ycc)
{
	static const int A[4][4] = {
		{FDeci( 0.29900), FDeci( 0.58700), FDeci( 0.11400)},
		{FDeci( 0.50000), FDeci(-0.41869), FDeci(-0.08131)},
		{FDeci(-0.16874), FDeci(-0.33126), FDeci( 0.50000)}
	};
	ycc[0] = A[0][0] * grb[1] + A[0][1] * grb[0] + A[0][2] * grb[2];
	ycc[1] = A[1][0] * grb[1] + A[1][1] * grb[0] + A[1][2] * grb[2];
	ycc[2] = A[2][0] * grb[1] + A[2][1] * grb[0] + A[2][2] * grb[2];
	return;
}


static int _coldiff(grb_t grb1, grb_t grb2)
{
	int grb[2][4];
	deci ycc[2][4];
	grb[0][0]=(grb1>>8)&15; grb[0][1]=(grb1>>4)&15; grb[0][2]=(grb1)&15;
	grb[1][0]=(grb2>>8)&15; grb[1][1]=(grb2>>4)&15; grb[1][2]=(grb2)&15;
	RGB_YCrCb(grb[0], ycc[0]);
	RGB_YCrCb(grb[1], ycc[1]);
	int t,d;
	d = 0;
	// 色相（Cr,Cb）の相違
	t = ycc[0][1] - ycc[1][1];		t = MulDeci(MulDeci(t,t),FDeci(0.7));
	d += DeciToInt(t);
	t = ycc[0][2] - ycc[1][2];		t = MulDeci(MulDeci(t,t),FDeci(0.8));
	d += DeciToInt(t);
	// 輝度（Y）の相違
	t = ycc[0][0] - ycc[1][0];		t = MulDeci(t,t);
	d += DeciToInt(t);
	return d;
}


//     (local) 近似パレットリストのための型定義・変数定義
//-----------------------------------------------------------------------


typedef struct _nearplt_e {		// 近似パレットリストの１要素(NEARPLTの補助)
	struct _nearplt_e *next;
	bool			used;
	char			plt;
	short int		dist;
} NEARPLTe;


typedef	struct {				// ある grb_t カラーに対する近似パレットリスト
	NEARPLTe	*neartop;
	NEARPLTe	near[16];
} NEARPLT;


static	NEARPLT	*nearplt;	// 全 grb_t カラーに対する近似パレットリスト
static	grb_t	pltgrb[16];	// 各パレットの現在の設定状態


//     (local) 近似パレットリストへの要素の挿入
//-----------------------------------------------------------------------


static void NEARPLT_ins(grb_t grb, int plt)
// ［grb_tカラー grb に対する近似パレットリストに、plt 番のパレットを挿入する］
//		この関数は、grb_tカラー grb に対する近似パレットリストに plt 番の
//		パレットがまだ登録されていない状態で呼ばなくてはいけない。
{
	NEARPLT *nplt;
	int dist;
	nplt = nearplt + grb;
	dist = _coldiff(grb, pltgrb[plt]);
	if (nplt->neartop == NULL)
	{
		NEARPLTe *p;
		p = &(nplt->near[0]);
		p->next = NULL;
		p->used = YES;
		p->plt = plt;
		p->dist = dist;
		nplt->neartop = p;
	}
	else
	{
		int i;
		register NEARPLTe *p1,*p2;
		bool done;
		NEARPLTe *p;
		done = NO;
		for (p1=nplt->neartop,p2=NULL;  p1!=NULL;  p2=p1,p1=p1->next)
		{
			if (dist < p1->dist)
			{
				done = YES;
				for (i=0; i<16; i++)
					if (! nplt->near[i].used)	break;
				if (i==16)
					return;
				p = &(nplt->near[i]);
				p->used = YES;
				p->next = p1;
				p->plt = plt;
				p->dist = dist;
				if (p2 == NULL)
					nplt->neartop = p;
				else
					p2->next = p;
				break;
			}
		}
		if (!done)
		{
			for (i=0; i<16; i++)
				if (! nplt->near[i].used)	break;
			if (i==16)
				return;
			p = &(nplt->near[i]);
			p->used = YES;
			p->next = NULL;
			p->plt = plt;
			p->dist = dist;
			if (p2 == NULL)
				nplt->neartop = p;
			else
				p2->next = p;
		}
	}
}


//     (local) 近似パレットリストの要素の削除
//-----------------------------------------------------------------------


static void NEARPLT_del(int grb, int plt)
// ［grb_tカラー grb に対する近似パレットリストの中から、
//   plt番のパレットの要素を削除する］
//          この関数は、リスト中に見つかった plt 番のパレット要素をすべて
//			削除する。また、リスト中に plt 番のパレット要素が存在しなくても
//			問題はない。
{
	NEARPLT *nplt;
	nplt = nearplt + grb;
	NEARPLTe *p1,*p2;
	for (p1=nplt->neartop,p2=NULL;  p1!=NULL;  p2=p1,p1=p1->next)
	{
		if (p1->plt == plt)
		{
			if (p2==NULL)
				nplt->neartop = p1->next;
			else
				p2->next = p1->next;
			p1->used = NO;
		}
	}
}


#endif /* if NO16==0 */



//     (global) grb_t カラー 000 に対する近似パレットリストのダンプ出力
//				（デバッグ用関数）
//-----------------------------------------------------------------------


void __dumpNEARPLT0()
{
#if NO16==0
	if (mode != MODE16)
		return;
	int tc = 0*256+0*16+0;
	sprintf(debugmsg,"nearplt[%03x] dump:",tc);
	DEBUG_MSG(debugmsg);
	NEARPLTe *p;
	p = (nearplt+tc)->neartop;
	for ( ; p!=NULL; p=p->next)
	{
		sprintf(debugmsg, " %1x[%03x] d%4d", p->plt, plt16_get(p->plt), p->dist);
		DEBUG_MSG(debugmsg);
	}
#endif
}


//     (global) 近似パレットリストおよびこのモジュールの初期化
//-----------------------------------------------------------------------


int plt16_init()
{
#if NO16==0
	int i,j;
	if (nearplt == NULL)
	{
		nearplt = calloc(sizeof(NEARPLT), 4096);
		if (nearplt == NULL)
		{
			DEBUG_MSG("近似色リストバッファ　確保失敗");
			return -1;
		}
		else
			DEBUG_MSG("近似色リストバッファ　確保成功");
	}
	else
		memset(nearplt, 0, sizeof(NEARPLT)*4096);
	for (i=0; i<16; i++)
		pltgrb[i] = _getplt(i);
	for (i=0; i<4096; i++)
		for (j=0; j<16; j++)
			NEARPLT_ins(i,j);
	__dumpNEARPLT0();
#endif
	return 0;
}


//     (global) 指定したパレット番号に対するパレットの設定
//-----------------------------------------------------------------------


void	plt16_set(int plt, grb_t grb)
{
#if NO16==0
	int i;
	for (i=0; i<4096; i++)
		NEARPLT_del(i,plt);
	_setplt(plt, grb);
	pltgrb[plt] = grb;
	for (i=0; i<4096; i++)
		NEARPLT_ins(i,plt);
#endif
}


//     (global) 指定したパレット番号に対するパレット内容を得る
//-----------------------------------------------------------------------


grb_t	plt16_get(int plt)
{
#if NO16==0
	return pltgrb[plt];
#else
	return 0;
#endif
}


//     (global) 指定した grb_t カラーの近似パレットを得る
//-----------------------------------------------------------------------


int		plt16_getnear(grb_t grb, int source_plt, int object_plt)
// 指定されたRGBカラーに最も近いパレットを返す
{
#if NO16==0
	NEARPLT *nplt;
	nplt = nearplt + grb;
	NEARPLTe *p;
	p = nplt->neartop;
	if (p->plt != source_plt)
		return p->plt;
	else	// 「一番近い色」がもとの色と同じ色の場合
	{
		int distmax = (nearplt+pltgrb[p->plt])->near[object_plt].dist;
		// distmax = _min(distmax, _coldiff(pltgrb[source_plt], pltgrb[object_plt]));
		for (p=p->next;  p!=NULL;  p=p->next)
		{
			if ((nearplt+pltgrb[p->plt])->near[object_plt].dist < distmax)
				return p->plt;
		}
		return object_plt;
	}
#else
	return 0;
#endif
}


//     (global) 指定した２つの grb_t カラーを混ぜ合わせる
//-----------------------------------------------------------------------


grb_t	plt16_gray(grb_t grb1, grb_t grb2, int gray)
// gray : 0..256（grb2の「強さ」）
{
#if NO16==0
	if (gray == 0)
		return grb1;
	else if (gray == 256)
		return grb2;
	else
	{
		gray = _min(256,_max(gray,0));
		int gg,r1,r2,g1,g2,b1,b2,r,g,b;
		gg = 256-gray;
		g1 = (grb1>>8)&15;
		r1 = (grb1>>4)&15;
		b1 =  grb1 & 15;
		g2 = (grb2>>8)&15;
		r2 = (grb2>>4)&15;
		b2 =  grb2 & 15;
		g = (gg*g1 + gray*g2 + 128) / 256;
		r = (gg*r1 + gray*r2 + 128) / 256;
		b = (gg*b1 + gray*b2 + 128) / 256;
		return (g<<8)|(r<<4)|b;
	}
#else
	return 0;
#endif
}


/* end of plt16.c */
