/*************************************************************************************************/
/*!
   	@file		rendererpolygon.h
	@author 	Fanzo
 	@date 		2008/3/23
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iTexture.h"
#include	"iFace/iBlender.h"
#include	"iFace/iRendererPolygon.h"

#pragma pack( push , 8 )		//set align

namespace icubic
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define

///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"RendererPolygon_bs" class 
**************************************************************************************************/
class RendererPolygon_bs : 
	public IRendererPolygon
{
	cb_copy_impossible( RendererPolygon_bs );
	
protected:
	struct RenderEdge
	{
		int		m_cover;
		int		m_sp;
		int		m_x;
		RenderEdge*	m_next;
	};

// variable member
private:
	IMemAllocLump*				m_allocator;
	rgba						m_color;
	Array< RenderEdge*>			m_line;
	
	Array< uint8 >				m_texture;
	Array< uint8 >				m_oversample;

	iTexture					m_source;
	iBlender					m_blender;
	
// private functions
private:
//=================================================================================================
//!	create RenderEdge
//!	@retval			---
//-------------------------------------------------------------------------------------------------
RenderEdge* CreateRenderEdge()
{
	return ( RenderEdge* )m_allocator->Allocate( sizeof( RenderEdge ) );
}
//=================================================================================================
//!	area
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool Area
		(
		iSurfaceDest&				surface , 
		const EdgemapPolygonInfo&	epi , 
		const irect					arealist[] , 
		int							areanum , 
		irect*						r_area
		)
{
	// draw area
	irect	area = arealist[0];
	int		areaoff;
	for( areaoff = 1 ; areaoff < areanum ; areaoff++ )
		area = area.Or( arealist[ areaoff ] );
	area = area.And( surface->GetDestAvailableArea() );
	if( area.IsExist() == false )
		return false;
	
	// polygon area
	area.ymin	= epi.m_start_y > area.ymin ? epi.m_start_y : area.ymin;
	area.ymax	= epi.m_end_y   < area.ymax ? epi.m_end_y   : area.ymax;
	if( area.IsExist() == false )
		return false;
	*r_area	= area;
	return true;
}
//=================================================================================================
//!	combine polygon edge
//!	@retval			---
//-------------------------------------------------------------------------------------------------
RenderEdge* CombinePolygonEdge
		(
		RenderEdge*		d_edge , 
		PolygonEdge*	s_edge , 
		antialias		anti
		)
{
	if( s_edge == 0 )
		return d_edge;
		
	RenderEdge	first;
	first.m_cover	= 0;
	first.m_next	= 0;
	first.m_x		= INT_MIN;
	RenderEdge*	prev	= &first;
	int			s_dir	= 1;
	
	while( d_edge != 0 || s_edge != 0 )
	{
		int		sx	= s_edge == 0 ? 0 : ( s_edge->m_x >> outlinedgemap_anti_shift_x() );
		if( s_edge == 0 || ( d_edge != 0 && s_edge != 0 && d_edge->m_x <= sx ) )
		{
			if( prev->m_x == d_edge->m_x )
				prev->m_cover += d_edge->m_cover;
			else
			{
				prev->m_next	= d_edge;
				prev			= d_edge;
			}
			d_edge = d_edge->m_next;
		}
		else if( d_edge == 0 || ( d_edge != 0 && s_edge != 0 && sx < d_edge->m_x ) )
		{
			if( prev->m_x == sx )
				prev->m_cover += s_dir;
			else
			{
				RenderEdge*	e = CreateRenderEdge();
				e->m_cover		= s_dir;
				e->m_x			= sx;
				prev->m_next	= e;
				prev			= e;
			}
			s_dir	= ( s_dir == -1 ) ? 1 : -1;
			s_edge	= s_edge->m_next;
		}
	}
	prev->m_next = 0;
	return first.m_next;	
}
//=================================================================================================
//!	anti to step
//!	@retval			---
//-------------------------------------------------------------------------------------------------
int ToStep
		(
		int		cover , 
		int		antinum
		)
{
	return ( cover == 0 ) ? 0 : ( cover == antinum ? 2 : 1 );
}
//=================================================================================================
//!	compare step
//!	@retval			---
//-------------------------------------------------------------------------------------------------
int CompareStep
		(
		int		dest_sp , 
		int		src_sp
		)
{
	static
	int		tbl[3][3] = 
	{
		{ 0 , 1 , 2 } , 
		{ 1 , 1 , 1 } , 
		{ 2 , 1 , 2 } , 
	};
	return tbl[ src_sp ][ dest_sp ];
}
//=================================================================================================
//!	combine render edge
//!	@retval			---
//-------------------------------------------------------------------------------------------------
RenderEdge* CombineRenderEdge
		(
		RenderEdge*		d_edge , 
		RenderEdge*		s_edge , 
		int				antinum
		)
{
		
	RenderEdge	first;
	first.m_sp			= -1;
	RenderEdge*	prev	= &first;
	int			s_cover	= 0;
	int			s_sp	= 0;
	int			d_sp	= 0;

	while( d_edge != 0 || s_edge != 0 )
	{
		if( d_edge != 0 && s_edge != 0 && d_edge->m_x == s_edge->m_x )
		{
			s_cover			+= s_edge->m_cover;
			s_sp			= ToStep( s_cover , antinum );
			d_sp			= d_edge->m_sp;
			d_edge->m_sp	= CompareStep( d_sp , s_sp );
			if( d_edge->m_sp != prev->m_sp )
			{
				prev->m_next	= d_edge;
				prev			= d_edge;
			}
			d_edge	= d_edge->m_next;
			s_edge	= s_edge->m_next;
		}
		else if( s_edge == 0 || ( d_edge != 0 && s_edge != 0 && d_edge->m_x < s_edge->m_x ) )
		{
			d_sp			= d_edge->m_sp;
			d_edge->m_sp	= CompareStep( d_sp , s_sp );
			if( d_edge->m_sp != prev->m_sp )
			{
				prev->m_next	= d_edge;
				prev			= d_edge;
			}
			d_edge	= d_edge->m_next;
		}
		else if( d_edge == 0 || ( d_edge != 0 && s_edge != 0 && d_edge->m_x > s_edge->m_x ) )
		{
			s_cover			+= s_edge->m_cover;
			s_sp			= ToStep( s_cover , antinum );
			s_edge->m_sp	= CompareStep( d_sp , s_sp );
			if( s_edge->m_sp != prev->m_sp )
			{
				prev->m_next	= s_edge;
				prev			= s_edge;
			}
			s_edge	= s_edge->m_next;
		}
	}	
	prev->m_next = 0;
	return first.m_next;
}
//=================================================================================================
//!	CreateRenderEdge
//!	@retval			---
//-------------------------------------------------------------------------------------------------
RenderEdge* CreateRenderEdge
		(
		PolygonInfo*		pi , 
		int					y , 
		antialias			anti
		)
{
	int			anti_sf	= anti_shift( anti );
	int			antinum	= anti_scale( anti );
	RenderEdge*	re	= 0;
	while( pi != 0 )
	{
		if( pi->m_start_y <= y && y < pi->m_end_y )
		{
			// create polygon edge
			int			dy		= ( y - pi->m_start_y ) << anti_sf;		 
			RenderEdge*	edge	= 0;
			int			antioff;
			for( antioff = 0 ; antioff < antinum ; antioff++ )
				edge = CombinePolygonEdge( edge , pi->m_line[ dy + antioff ].m_edge , anti );

			// create render edge
			re	= CombineRenderEdge( re , edge , anti_scale( anti ) );
		}		
		// update
		pi	= pi->m_next;
	}
	return re;
}
//=================================================================================================
//!	render line
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void RenderLineSupersample
		(
		pixelformat				destformat , 
		uint8*					dest , 
		int						dest_pitchbyte , 
		PolygonInfo*			pi , 
		int						y , 
		int						sx_fx , 
		int						tx_fx , 
		antialias				anti , 
		uint8					alpha
		)
{
	if( sx_fx >= tx_fx )
		return;

	int		sf[2]	= { outlinedgemap_anti_shift_x() , anti_shift( anti ) };
	int		scale[2]= { outlinedgemap_anti_scale_x() , anti_scale( anti ) };

	pixelformat		srcformat	= m_source->TextureImageFormat();
	m_texture.Resize( ( tx_fx - sx_fx ) * get_pixel_byte( srcformat ) );
	
	rgba			color;
	ITexture::Type	st = m_source->TextureType();
	if( st == ITexture::Image )
	{
		if( false == m_source->BeginTextureImage() )
			return;
	}
	else
	{
		color	= m_source->TextureColor().MulAlpha( alpha );
	}
	while( pi != 0 )
	{
		if( pi->m_start_y <= y && y < pi->m_end_y )
		{
			int		dy = ( y - pi->m_start_y ) << sf[1];
			uint8*	t_dest	= dest;

			int		anti_off;
			for( anti_off = 0 ; anti_off < scale[1] ; anti_off++ )
			{			
				PolygonScanlineInfo*	ps	= &pi->m_line[ dy + anti_off ];
				PolygonEdge*			pe	= ps->m_edge;
				while( pe != 0 )
				{
					int		ssx	= pe->m_x >> ( sf[0] - sf[1] );
					int		ttx	= pe->m_next->m_x >> ( sf[0] - sf[1] );
					ssx	= clip( ssx , sx_fx , tx_fx );
					ttx	= clip( ttx , sx_fx , tx_fx );
					if( ssx < ttx )
					{
						if( st == ITexture::Color )
							m_blender->BlendColor( destformat , t_dest + ssx  * get_pixel_byte( destformat ) , ttx - ssx , color );
						else if( st == ITexture::Image )
						{
							fvector2	suv	= fvector2( ps->m_su , ps->m_sv );
							fvector2	tuv	= fvector2( ps->m_tu , ps->m_tv );
							
							fvector2	ssuv= ( tuv - suv ) * ( ( ssx << (sf[0]-sf[1]) ) - ps->m_sx * scale[0] ) / ( ( ps->m_tx - ps->m_sx ) * scale[0] ) + suv;
							fvector2	ttuv= ( tuv - suv ) * ( ( ttx << (sf[0]-sf[1]) ) - ps->m_sx * scale[0] ) / ( ( ps->m_tx - ps->m_sx ) * scale[0] ) + suv;
						
							m_source->TextureImage( m_texture.GetPtr() , ttx - ssx , ssuv , ttuv );
							m_blender->BlendImage( destformat , t_dest + ssx * get_pixel_byte( destformat ) , srcformat , m_texture.GetConstPtr() , ttx - ssx , m_color , alpha );
						}
					}
					pe	= pe->m_next->m_next;
				}
				t_dest += dest_pitchbyte;
			}
		}
		pi	= pi->m_next;
	}
	if( st == ITexture::Image )
		m_source->EndTextureImage();
}
//=================================================================================================
//!	render line
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void RenderLine_anti
		(
		uint8*					dest , 
		pixelformat				destformat , 
		PolygonInfo*			pi , 
		int						y , 
		int						sx , 
		int						tx , 
		antialias				anti , 
		uint8					alpha
		)
{
	if( sx >= tx )
		return;

	pixelformat	srcformat	= m_source->TextureImageFormat();
	int			anti_num	= anti_scale( anti );
	int			anti_sf		= anti_shift( anti );
	int			sx_fx		= sx << anti_sf;
	int			tx_fx		= tx << anti_sf;

	int				pitchbyte	= ( tx_fx - sx_fx ) * get_pixel_byte( srcformat );
	m_oversample.Resize( anti_num * pitchbyte );

	pp_oversample_m
			(
			to_pp_format( srcformat ) , 
			m_oversample.GetPtr() , 
			pitchbyte , 
			to_pp_format( destformat ) , 
			dest + sx * get_pixel_byte( destformat ) , 
			tx - sx , 
			anti_sf , 
			to_pp_color( rgba() ) , 
			to_pp_alpha( 255 )
			);
	RenderLineSupersample
			( 
			srcformat , 
			( uint8* )m_oversample.GetPtr() - sx_fx * get_pixel_byte( srcformat ) , 
			pitchbyte , 
			pi , 
			y , 
			sx_fx , 
			tx_fx , 
			anti , 
			alpha
			);
	pp_downsample_m
			(
			to_pp_format( destformat ) , 
			dest + sx * get_pixel_byte( destformat ) , 
			tx - sx , 
			to_pp_format( srcformat ) , 
			m_oversample.GetConstPtr() , 
			pitchbyte , 
			anti_sf , 
			to_pp_color( rgba() ) , 
			to_pp_alpha( 255 ) 
			);
}
//=================================================================================================
//!	render line
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void RenderLine_alias
		(
		uint8*					dest , 
		pixelformat				destformat , 
		PolygonInfo*			pi , 
		int						y , 
		int						sx , 
		int						tx , 
		antialias				anti , 
		uint8					alpha
		)
{
	if( sx >= tx )
		return;

	int		anti_sf_x	= outlinedgemap_anti_shift_x();
	int		anti_sf		= anti_shift( anti );
	int		anti_num	= anti_scale( anti );
	int		anti_dy[2] = { (anti_num-1)/2 , anti_num/2 };
	
	rgba	color;
	ITexture::Type	st = m_source->TextureType();
	if( st == ITexture::Image )
	{
		if( false == m_source->BeginTextureImage() )
			return;
	}
	else
	{
		color	= m_source->TextureColor().MulAlpha( alpha );
	}
	while( pi != 0 )
	{
		if( pi->m_start_y <= y && y < pi->m_end_y )
		{
			int						dy = ( y - pi->m_start_y ) << anti_sf;
			PolygonScanlineInfo*	ps[2];
			ps[0]	= &pi->m_line[ dy + anti_dy[0] ];
			ps[1]	= &pi->m_line[ dy + anti_dy[1] ];
			PolygonEdge*		pe	= ps[0]->m_edge;
			while( pe != 0 )
			{
				int		ssx	= pe->m_x >> anti_sf_x;
				int		ttx	= pe->m_next->m_x >> anti_sf_x;
				ssx	= clip( ssx , sx , tx );
				ttx	= clip( ttx , sx , tx );
				if( ssx < ttx )
				{
					if( st == ITexture::Color )
					{
						m_blender->BlendColor( destformat , dest + ssx * get_pixel_byte( destformat ) , ttx - ssx , color );
					}
					else if( st == ITexture::Image )
					{
						fvector2	suv	= ( fvector2( ps[0]->m_su , ps[0]->m_sv ) + fvector2( ps[1]->m_su , ps[1]->m_sv ) ) / 2.0f;
						fvector2	tuv	= ( fvector2( ps[0]->m_tu , ps[0]->m_tv ) + fvector2( ps[1]->m_tu , ps[1]->m_tv ) ) / 2.0f;
						float		sssx= ( ps[0]->m_sx + ps[1]->m_sx ) / 2.0f;
						float		tttx= ( ps[0]->m_tx + ps[1]->m_tx ) / 2.0f;
						fvector2	ssuv= ( tuv - suv ) * ( ssx - sssx ) / ( tttx - sssx ) + suv;
						fvector2	ttuv= ( tuv - suv ) * ( ttx - sssx ) / ( tttx - sssx ) + suv;
					
						pixelformat		srcformat	= m_source->TextureImageFormat();
						m_texture.Resize( ( ttx - ssx ) * get_pixel_byte( srcformat ) );
						m_source->TextureImage( m_texture.GetPtr() , ttx - ssx , ssuv , ttuv );
						m_blender->BlendImage( destformat , dest + ssx * get_pixel_byte( destformat ) , srcformat , m_texture.GetConstPtr() , ttx - ssx , m_color , alpha );
					}
				}
				pe	= pe->m_next->m_next;
			}
		}
		pi	= pi->m_next;
	}
	if( st == ITexture::Image )
		m_source->EndTextureImage();
}
//=================================================================================================
//!	render line
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void RenderLine
		(
		pixelformat				destformat , 
		uint8*					dest , 
		int						y , 
		PolygonInfo*			pi , 
		RenderEdge*				re , 
		int						sx , 
		int						tx , 
		antialias				anti , 
		uint8					alpha
		)
{
	int		ssx;
	int		sp	= 0;
	while( re != 0 )
	{
		if( sp == 0 && re->m_sp == 0 )
		{
			RenderLine_anti( dest , destformat , pi , y , clip( re->m_x , sx , tx ) , clip( re->m_x+1 , sx , tx ) , anti , alpha );
			ssx	= re->m_x + 1;
		}
		else if( sp == 0 && re->m_sp == 1 )
		{
			ssx	= re->m_x;
		}
		else if( sp == 0 && re->m_sp == 2 )
		{
			RenderLine_anti( dest , destformat , pi , y , clip( re->m_x , sx , tx ) , clip( re->m_x+1 , sx , tx ) , anti , alpha );
			ssx	= re->m_x + 1;
		}
		else if( sp == 1 && re->m_sp == 0 )
		{
			RenderLine_anti( dest , destformat , pi , y , clip( ssx , sx , tx ) , clip( re->m_x+1 , sx , tx ) , anti , alpha );
			ssx	= re->m_x + 1;
		}
		else if( sp == 1 && re->m_sp == 1 )
		{
		}
		else if( sp == 1 && re->m_sp == 2 )
		{
			RenderLine_anti( dest , destformat , pi , y , clip( ssx , sx , tx ) , clip( re->m_x+1 , sx , tx ) , anti , alpha );
			ssx	= re->m_x + 1;
		}
		else if( sp == 2 && re->m_sp == 0 )
		{
			RenderLine_alias( dest , destformat , pi , y , clip( ssx , sx , tx ) , clip( re->m_x , sx , tx ) , anti , alpha );
			RenderLine_anti( dest , destformat , pi , y , clip( re->m_x , sx , tx ) , clip( re->m_x+1 , sx , tx ) , anti , alpha );
			ssx	= re->m_x + 1;
		}
		else if( sp == 2 && re->m_sp == 1 )
		{
			RenderLine_alias( dest , destformat , pi , y , clip( ssx , sx , tx ) , clip( re->m_x , sx , tx ) , anti , alpha );
			ssx	= re->m_x;
		}
		else if( sp == 2 && re->m_sp == 2 )
		{
		}
		sp	= re->m_sp;
		re	= re->m_next;
	}
}

// "IRendererPolygon" interface functions
public:
//=================================================================================================
void cb_call Render
		(
		iSurfaceDest&			surface , 
		IEdgemapPolygonInfo*	info , 
		const irect				arealist[] , 
		int						areanum , 
		uint8					alpha = 255
		)
{
	if( areanum <= 0 )
		return;
	EdgemapPolygonInfo		epi;
	if( false == info->GetEdgemapPolygonInfo( &epi ) )
		return;
	
	// area
	irect	area;
	if( false == Area( surface , epi , arealist , areanum , &area ) )
		return;
	irect	area_fx	= area << anti_shift( epi.m_antialias );

	// renderedge
	m_allocator->DeallocateAll();
	m_line.Resize( area.Height() );
	int		dy;
	for( dy = 0 ; dy < m_line.GetDatanum() ; dy++ )
		m_line[ dy ]	= CreateRenderEdge( epi.m_polygon , area.ymin + dy , epi.m_antialias );

	// render
	pixelformat	destformat	= surface->GetDestFormat();
	int			pitchbyte;
	uint8*		dest = ( uint8* )surface->GetDestPixelPtr( &pitchbyte );
	int		areaoff;
	for( areaoff = 0 ; areaoff < areanum ; areaoff++ )
	{
		int		y;
		irect	rarea = area.And( arealist[ areaoff ] );
		if( false == rarea.IsExist() )
			continue;
		uint8*	t_dest	= dest + rarea.ymin * pitchbyte;
		for( y = rarea.ymin ; y < rarea.ymax ; y++ )
		{
			RenderLine( destformat , t_dest , y , epi.m_polygon , m_line[ y - area.ymin ] , rarea.xmin , rarea.xmax , epi.m_antialias , alpha );
			t_dest += pitchbyte;
		}
	}
}
// public functions
public:
//=================================================================================================
RendererPolygon_bs
		(
		IMemAllocLump*	alloc
		) : 
		m_allocator( alloc ) , 
		m_texture( Expand_ArrayCashType , 512 ) , 
		m_oversample( Expand_ArrayCashType , 512 ) , 
		m_line( Expand_ArrayCashType , 512 )
{
	cb_assert( m_allocator != 0 , L"Allocator isn't exist." );
}
//=================================================================================================
~RendererPolygon_bs()
{
	ReleaseAllocator();
}
//=================================================================================================
//!	ReleaseAllocator
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void ReleaseAllocator()
{
	if( m_allocator == 0 )
		return;
	m_allocator->DeallocateAll();
	m_allocator = 0;
	m_line.Resize( 0 );
}
//=================================================================================================
//!	set blender
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void SetBlender
		(
		iBlender		blender
		)
{
	m_blender	= blender;
}
//=================================================================================================
//!	release blender
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void ReleaseBlender()
{
	m_blender.release();
}
//=================================================================================================
//!	set source generator
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void SetTexture
		(
		iTexture	source
		)
{
	m_source	= source;
}
//=================================================================================================
//!	release sourcegen
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void ReleaseTexture()
{
	m_source.release();
}
};
/**************************************************************************************************
"RendererPolygon" class 
**************************************************************************************************/
class RendererPolygon : 
		public RendererPolygon_bs , 
		virtual public object_base
{
	query_begin()
	iface_hook( IRendererPolygon , IRendererPolygon_IID )
	query_end( object_base )

private:
	MemAllocLump_inc		m_allocator;
	
public:
//=================================================================================================
RendererPolygon() : RendererPolygon_bs( &m_allocator ) , m_allocator( sizeof( RenderEdge ) * 512 )
{
}
//=================================================================================================
~RendererPolygon()
{
	ReleaseAllocator();
}
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

///////////////////////////////////////////////////////////////////////////////////////////////////
// global functions define

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
