/*
*	Yamana's Otomeza Plug-in Tool
*		モザイク
*	
*	1995.07.30	乙女座へ移植
*	1995.08.13	点モード追加
*	
*	
*/
#include	"otome_pi.h"

const char longname[]  = "EFFECT: モザイク";
int			cnfg_max = 3;
PI_CNFG		cnfg[] =
			{	/* 1234567890123456 ,min,max,def,set */
				{ "速い 高品位?  丸", 0,   2,  0,  0 },
				{ "横サイズ"		, 1,  16,  4,  4 },
				{ "縦サイズ"		, 1,  16,  4,  4 },
			};

#define	USE_ENV		PI_SET_ENV
#define	USE_TYPE	PI_EFFC_ALORSL
#include	"otome_pi.c"

/********************************/

void set_vramWidth( vramWidth, xs, pix )
int 	*vramWidth;
int 	xs,pix;
{
		 if( pix < 8 )	*vramWidth = (xs+7)& 0xfff8;
	else if( pix ==8 )	*vramWidth = (xs  );
	else if( pix > 8 )	*vramWidth = (xs*2);
}

/********************************/

void MosaicEasy( buf, fr, xs,ys )
char	*buf;
FRAME	*fr;
int 	xs,ys;
{
	struct
		{	char	*addr;
			short	ds;
			short	sx,sy,ex,ey;
			short	xs,ys;
		}para;
	
	para.addr= buf;
	para.ds  = pi_data->ds;
	para.sx  = fr->lupx;
	para.sy  = fr->lupy;
	para.ex  = fr->rdwx;
	para.ey  = fr->rdwy;
	para.xs  = xs;
	para.ys  = ys;
	EGB_getBlockZoom( EgbPtr, &para );
	EGB_putBlockZoom( EgbPtr, 0x00, &para );
}

/********************************/

int 	getFunc16( x,y )
int 	x,y;
{
	return (pi_imge->image[ (x>>1) + y*(pi_imge->size.x>>1) ]
			>>((x & 1)? 4:0)& 0x0f );
}
int 	getFunc256( x,y )
int 	x,y;
{
	return (pi_imge->image[ x + y*(pi_imge->size.x) ])& 0xff;
}
/********************************/

#define	swap(a,b)\
	{	int 	tmp;\
		tmp=(a);(a)=(b);(b)=tmp;\
	}

#define	TILE_XS		8
#define	TILE_YS		2
#define	TILE_X		(TILE_XS/8)
#define	TILE_Y		TILE_YS

#define	TILEPAT1		8
#define	TILEPAT2		4
#define	USE_TILE		0x40
#define	NOT_USE_TILE	0x20

void MosaicNormal( image,buf, fr, xs,ys, xsize,ysize, allsize, pix )
char	*image;
unsigned int 	*buf;
FRAME	*fr;
int 	xs,ys,xsize,ysize,pix;
POINT	*allsize;
{
	unsigned int 	*hist = &buf[ xsize*ysize ];
	int 	sx,sy,x,y,i,j;
	int 	pos;
	int 	mx,my,ch;
	FRAME	Box;
	int 	h1,h2;
	char	tile[ 32*32 ];
	int 	tilepat[] = { 0, 2, 4, 6,
							9,11,13,15 };
	int 	(*getpix)();
	
	if( pix==8 )	getpix = getFunc256;
			else	getpix = getFunc16;
	
	for( sy=0; sy<ys ; sy+= ysize )
	{	for( sx=0; sx<xs ; sx+= xsize )
		{	
			MOS_rdpos( &ch, &mx, &my );
			if( ch & MOS_RIGHT )	return;
			
			/* 色番号のヒストグラムを作る */
			h1=h2=(-1);
			
			for( y=0; y<ysize; y++ )
			{	for( x=0; x<xsize; x++ )
				{
					pos = x+y*xsize;
					buf [ pos ] = getpix( fr->lupx+sx+x, fr->lupy+sy+y );
					hist[ pos ] = 0;
					
					for( i=0; i<=pos ; i++ )
					{	if( buf [i] == buf[pos] )	/* 同じ色? */
						{	hist[i]++;
							if( hist[i]>=hist[h1] )	/* 頻度の多い２色 */
								h2=h1, h1=i;
							break;
						}
					}
				}
			}
			
			if( h1<0 )	h1 = 0;		/* 全部違う色のときは、何色でもいい！ */
			if( h2<0 )	h2 = 1;		/* てきとーに処理するべし(^^;) */
			
			if( hist[h1] > hist[h2]*3+1 )
			{	
				EGB_paintMode( EgbPtr, NOT_USE_TILE );
				EGB_color( EgbPtr, EGB_PAINTCOL, buf[h1] );
			}
			else	/* タイルパターンを作る */
			{	j=0;
				if( hist[h1]==hist[h2] )	/* 頻度比 1:1 は市松模様 */
				{	pos = TILEPAT1;
					
					/* タイルが汚くならないよう色番号によって位置を決める */
					if( buf[h1]&1 )
					{	if( buf[h2]&1 )
						{	if( h1>=h2 )	swap( h1,h2 );
						}else
							swap( h1,h2 );
					}/* この処理、はっきり言っていい加減である(^^;) */
					
				}else
				{	pos = TILEPAT2;			/* 頻度比 1:3 */
					
					/* タイル開始位置をずらす */
					if( buf[h1]&1 )
					{	if( buf[h2]&1 )
						{	if( h1>=h2 )	j=1;
						}else
							j=1;
					}
				}
				
				if( pix == 8 )
				{	for(i=0; i<TILE_XS*TILE_YS; i++ )
						tile[i] = buf[h1];
					for( i=0; i<pos ;i++ )
						tile[ tilepat[i]+j ] = buf[h2];
				}else
				{	for(i=0; i<TILE_XS*TILE_YS; i++ )
						tile[i] = (buf[h1]& 0x0f) | (buf[h1]<<4) ;
					for( i=0; i<pos ;i++ )
					{	x = (tilepat[i]+j)>>1;
						if( (tilepat[i]+j) & 1 )
							tile[x] = (tile[x] & 0x0f) | (buf[h2]<<4) ;
						else
							tile[x] = (tile[x] & 0xf0) | (buf[h2]) ;
					}
				}
				
				EGB_paintMode  ( EgbPtr, USE_TILE );
				EGB_tilePattern( EgbPtr, EGB_PAINTCOL,TILE_X,TILE_Y, tile );
			}
			
			Box.lupx = fr->lupx + sx;
			Box.lupy = fr->lupy + sy;
			Box.rdwx = Box.lupx + xsize-1 ;
			Box.rdwy = Box.lupy + ysize-1 ;
			EGB_rectangle( EgbPtr, &Box );
			
		}
	}
	
}


void MosaicPoint( image,buf, fr, xs,ys, xsize,ysize, allsize, pix )
char	*image;
unsigned int 	*buf;
FRAME	*fr;
int 	xs,ys,xsize,ysize,pix;
POINT	*allsize;
{
	unsigned int 	*hist = &buf[ xsize*ysize ];
	int 	sx,sy,x,y,i,j;
	int 	pos;
	int 	mx,my,ch;
	FRAME	Box;
	int 	h1,h2;
	char	tile[ 32*32 ];
	int 	tilepat[] = { 0, 2, 4, 6,
							9,11,13,15 };
	int 	(*getpix)();
	
	if( pix==8 )	getpix = getFunc256;
			else	getpix = getFunc16;
	
	for( sy=0; sy<ys ; sy+= ysize )
	{	for( sx=0+(sy % (ysize*2))/2 ; sx<xs ; sx+= xsize )
		{	
			MOS_rdpos( &ch, &mx, &my );
			if( ch & MOS_RIGHT )	return;
			
			/* 色番号のヒストグラムを作る */
			h1=h2=(-1);
			for( y=0; y<ysize; y++ )
			{	for( x=0; x<xsize; x++ )
				{
					pos = x+y*xsize;
					buf [ pos ] = getpix( fr->lupx+sx+x, fr->lupy+sy+y );
					hist[ pos ] = 0;
					
					for( i=0; i<=pos ; i++ )
					{	if( buf [i] == buf[pos] )	/* 同じ色? */
						{	hist[i]++;
							if( hist[i]>=hist[h1] )	/* 頻度の多い２色 */
								h2=h1, h1=i;
							break;
						}
					}
				}
			}
			
			EGB_paintMode( EgbPtr, NOT_USE_TILE );
			EGB_color( EgbPtr, EGB_PAINTCOL, PI_BACKCOL );
			Box.lupx = fr->lupx + sx;
			Box.lupy = fr->lupy + sy;
			Box.rdwx = Box.lupx + xsize-1 ;
			Box.rdwy = Box.lupy + ysize-1 ;
			EGB_rectangle( EgbPtr, &Box );
			
			if( h1<0 )	h1 = 0;		/* 全部違う色のときは、何色でもいい！ */
			if( h2<0 )	h2 = 1;		/* てきとーに処理するべし(^^;) */
			
			if( hist[h1] > hist[h2]*3+1 )
			{	
				EGB_paintMode( EgbPtr, NOT_USE_TILE );
				EGB_color( EgbPtr, EGB_PAINTCOL, buf[h1] );
			}
			else	/* タイルパターンを作る */
			{	j=0;
				if( hist[h1]==hist[h2] )	/* 頻度比 1:1 は市松模様 */
				{	pos = TILEPAT1;
					
					/* タイルが汚くならないよう色番号によって位置を決める */
					if( buf[h1]&1 )
					{	if( buf[h2]&1 )
						{	if( h1>=h2 )	swap( h1,h2 );
						}else
							swap( h1,h2 );
					}/* この処理、はっきり言っていい加減である(^^;) */
					
				}else
				{	pos = TILEPAT2;			/* 頻度比 1:3 */
					
					/* タイル開始位置をずらす */
					if( buf[h1]&1 )
					{	if( buf[h2]&1 )
						{	if( h1>=h2 )	j=1;
						}else
							j=1;
					}
				}
				
				if( pix == 8 )
				{	for(i=0; i<TILE_XS*TILE_YS; i++ )
						tile[i] = buf[h1];
					for( i=0; i<pos ;i++ )
						tile[ tilepat[i]+j ] = buf[h2];
				}else
				{	for(i=0; i<TILE_XS*TILE_YS; i++ )
						tile[i] = (buf[h1]& 0x0f) | (buf[h1]<<4) ;
					for( i=0; i<pos ;i++ )
					{	x = (tilepat[i]+j)>>1;
						if( (tilepat[i]+j) & 1 )
							tile[x] = (tile[x] & 0x0f) | (buf[h2]<<4) ;
						else
							tile[x] = (tile[x] & 0xf0) | (buf[h2]) ;
					}
				}
				
				EGB_paintMode  ( EgbPtr, USE_TILE );
				EGB_tilePattern( EgbPtr, EGB_PAINTCOL,TILE_X,TILE_Y, tile );
			}
			
			Box.lupx = fr->lupx + sx + xsize/2-1 ;
			Box.lupy = fr->lupy + sy + ysize/2-1 ;
			Box.rdwx = xsize/2;
			Box.rdwy = ysize/2;
			EGB_ellipse( EgbPtr, &Box );
			
		}
	}
	
}



/* 32K色だと楽だな〜 */

void MosaicNormal32K( image, mode, fr, xs,ys, xsize,ysize, allsize )
char	*image;
int 	mode;
FRAME	*fr;
int 	xs,ys,xsize,ysize;
POINT	*allsize;
{
	int 	sx,sy,x,y, ss;
	int 	pos,tmp;
	int 	mx,my,ch;
	FRAME	Box;
	unsigned int 	r,g,b;
	
//	clock_t	t;
//	t=clock();
	
	for( ss=0,sy=0; sy<ys ; sy+= ysize )
	{
		if( mode==2 )	ss = (sy % (ysize*2))/2;
		for( sx=0+ss ; sx<xs ; sx+= xsize )
		{	
			MOS_rdpos( &ch, &mx, &my );
			if( ch & MOS_RIGHT )	return;
			
			r=g=b=0;
			for( y=0; y<ysize; y++ )
			{	for( x=0; x<xsize; x++ )
				{	
					pos = ((fr->lupx + sx + x)<<1 )
							 + (fr->lupy + sy + y)*(allsize->x<<1) ;
					
					tmp = image[ pos ] + (image[ pos+1 ]<<8) ;
					g += ((tmp>>10) & 0x1f);
					r += ((tmp>>5 ) & 0x1f);
					b += ((tmp    ) & 0x1f);
				}
			}
			pos = xsize*ysize;
			g /= pos;
			r /= pos;
			b /= pos;
			EGB_paintMode( EgbPtr, NOT_USE_TILE );
			
			Box.lupx = fr->lupx + sx;
			Box.lupy = fr->lupy + sy;
			Box.rdwx = Box.lupx + xsize-1 ;
			Box.rdwy = Box.lupy + ysize-1 ;
			
			if( mode == 1 )
			{	EGB_color( EgbPtr, EGB_PAINTCOL, (int)(g<<10 | r<<5 | b ));
				EGB_rectangle( EgbPtr, &Box );
			}else
			{	EGB_color( EgbPtr, EGB_PAINTCOL, PI_BACKCOL );
				EGB_rectangle( EgbPtr, &Box );
				EGB_color( EgbPtr, EGB_PAINTCOL, (int)(g<<10 | r<<5 | b ));
				
				Box.lupx = fr->lupx + sx + xsize/2-1 ;
				Box.lupy = fr->lupy + sy + ysize/2-1 ;
				Box.rdwx = xsize/2;
				Box.rdwy = ysize/2;
				EGB_ellipse( EgbPtr, &Box );
			}
		}
	}
	
//	printf("time = %d", clock()-t );
	
	/* EGB_point : clock==850 */
	/* 直接取る  : clock==305 */
}

/********************************/

int APL_exec()
{
	int 	mode,xsize,ysize;
	int 	width,height, xs,ys,vramWidth;
	char	*buf;
	int 	mx,my,ch;
	FRAME	fr;
	
	mode  = cnfg[0].val;		/* 0:高速版 1:高品位版 2:点 */
	xsize = cnfg[1].val;		/* Xサイズ */
	ysize = cnfg[2].val;		/* Yサイズ */
	
	if( xsize == 1 && ysize == 1 )	return NOERR;
	
	memcpy( &fr, g_para, sizeof( fr ) );
	
	EGB_writePage( EgbPtr, pi_imge->page );
	EGB_viewport ( EgbPtr, &fr );
	
	fr.lupx = (fr.lupx / xsize * xsize);
	fr.lupy = (fr.lupy / ysize * ysize);
	fr.rdwx = (fr.rdwx / xsize * xsize + xsize-1);
	fr.rdwy = (fr.rdwy / ysize * ysize + ysize-1);
	
	width  = (fr.rdwx - fr.lupx + 1);
	height = (fr.rdwy - fr.lupy + 1);
	xs = (width +xsize-1)/xsize ;
	ys = (height+ysize-1)/ysize ;
	
	/*******************************/
	if( mode == 0 )
	{
		set_vramWidth( &vramWidth, xs, pi_imge->pix );
		
		if( (buf = PI_MALLOC( vramWidth*ys ))==NULL )
			return PI_ERROR_NO_MEMORY;
		
		MosaicEasy( buf, &fr, xs,ys );
		
		PI_FREE( buf );
	}
	else
	{	xs *= xsize;
		ys *= ysize;
		
		if( pi_imge->pix >8 )
		{
			MosaicNormal32K( pi_imge->image, mode,
					&fr, xs,ys, xsize,ysize, &pi_imge->size );
		}
		else
		{	if( (buf = PI_MALLOC( xsize*ysize*sizeof(int)*2 ))==NULL )
				return PI_ERROR_NO_MEMORY;
			
			if( mode ==1 )
				MosaicNormal( pi_imge->image, (unsigned int*)buf,
					&fr, xs,ys, xsize,ysize, &pi_imge->size, pi_imge->pix );
			else
				MosaicPoint( pi_imge->image, (unsigned int*)buf,
					&fr, xs,ys, xsize,ysize, &pi_imge->size, pi_imge->pix );
			
			PI_FREE( buf );
		}
	}
	
	do
	{	MOS_rdpos( &ch, &mx, &my );
	}while( ch & MOS_RIGHT );
	
	
	return NOERR;
}
