/*************************************************************************************************/
/*!
   	@file		Graphics.h
	@author 	Fanzo
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iGraphics.h"

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

namespace icubic
{

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define
enum JointType_gp
{
	Bevel_JointType_gp , 
	Miter_JointType_gp , 
	Round_JointType_gp , 
};
enum CapType_gp
{
	Flat_CapType_gp , 
	Roung_CapType_gp , 
};
enum BlenderType_gp
{
	Overlap_BlenderType_gp , 
	Onezero_BlenderType_gp , 
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"PaintImage_gp" class 
**************************************************************************************************/
class PaintImage_gp : 
	virtual public object_base , 
	public IPaintImage_gp
{
// query
	query_begin();
	iface_hook( IPaintImage_gp , IPaintImage_gp_IID )
	iface_hook( IPaint , IPaint_IID )
	query_end( object_base );
	
// variable member
protected:
	iTextureImage	m_gen;
	faffine			m_transform;
	faffine			m_dtouv;
	
// "IPaint" interface functions
public:
//=================================================================================================
IPaint::Type cb_call PaintType()
{
	return ( ITexture::Color == m_gen->TextureType() ) ? IPaint::Color : IPaint::Image;
}
//=================================================================================================
rgba cb_call PaintColor()
{
	return m_gen->TextureColor();
}
//=================================================================================================
pixelformat	cb_call PaintImageFormat()
{
	return m_gen->TextureImageFormat();
}
//=================================================================================================
bool cb_call BeginPaintImage()
{
	if( m_gen == false )
		return false;
	isize	size = m_gen->TextureSize();
	if( size.width == 0 || size.height == 0 )
		return false;
	DPI		dpi	= m_gen->TextureDPI();
	m_dtouv	= faffine::GetScale( 1.0f / size.width , 1.0f / size.height ) * m_transform;
	return m_gen->BeginTextureImage();
}
//=================================================================================================
void cb_call PaintImage
		(
		void				*image , 
		int					len , 
		const fvector2&		x , 
		const fvector2&		y
		)
{
	m_gen->TextureImage( image , len , m_dtouv.Transform( x ) , m_dtouv.Transform( y ) );
}
//=================================================================================================
void cb_call EndPaintImage()
{
	m_gen->EndTextureImage();
}
// "PaintImage_gp" interface functions
public:
//=================================================================================================
void cb_call SetTransform
		(
		const faffine&		trans
		)
{
	m_transform	= trans;
}
//=================================================================================================
faffine cb_call GetTransform()const
{
	return m_transform;
}
//=================================================================================================
void cb_call SetWraptype
		(
		Wraptype	wrap
		)
{
	m_gen->SetWraptype( wrap );
}
//=================================================================================================
Wraptype cb_call GetWraptype()
{
	return m_gen->GetWraptype();
}

// public functions
public:
//=================================================================================================
void SetSource
		(
		iTextureImage	&gen
		)
{
	m_gen	= gen;
}
//=================================================================================================
void ReleaseSource()
{
	m_gen.release();
}
};
//=================================================================================================
//!	create solidbrush
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPaintSolid_gp CreatePaintSolid
		(
		const rgba& color = rgba()
		)
{
	instance<PaintSolidColor>	brush;
	brush->SetColor( color );
	return (iPaintSolid_gp)brush;
}
//=================================================================================================
//!	create linear
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPaintGradLinear_gp CreatePaintGradLinear()
{
	instance<PaintGradLinear>	brush;
	return (iPaintGradLinear_gp)brush;
}
//=================================================================================================
//!	create radial
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPaintGradRadial_gp CreatePaintGradRadial()
{
	instance<PaintGradRadial>	brush;
	return (iPaintGradRadial_gp)brush;
}
//=================================================================================================
//!	create surface brush
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPaintImage_gp CreatePaintImage
		(
		iSurfaceSource&				surface , 
		SourceInterpolateType_gp	interpolate = Linear_SourceInterpolateType_gp
		)
{
	instance<PaintImage_gp>	brush;
	if( interpolate == Nearest_SourceInterpolateType_gp )
	{
		instance<TextureImageNearest>	source;
		source->SetTexture( surface );
		brush->SetSource( (iTextureImage)source );
	}
	else if( interpolate == Linear_SourceInterpolateType_gp )
	{
		instance<TextureImageWeight4>	source;
		source->SetTexture( surface );
		brush->SetSource( (iTextureImage)source );
	}
	else
	{
		instance<TextureImageWeight16>	source;
		source->SetTexture( surface );
		brush->SetSource( (iTextureImage)source );
	}
	return (iPaintImage_gp)brush;
}
//=================================================================================================
//!	create joint
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iJoint CreateJoint
		(
		JointType_gp	joint
		)
{
	if( joint == Miter_JointType_gp )
		return (iJoint)instance<JointMiter>();
	else if( joint == Round_JointType_gp )
		return (iJoint)instance<JointRound>();
	else
		return (iJoint)instance<JointBevel>();
}
//=================================================================================================
//!	cap
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iCap CreateCap
		(
		CapType_gp	cap
		)
{
	if( cap == Roung_CapType_gp )
		return (iCap)instance<CapRound>();
	else
		return (iCap)instance<CapFlat>();
}
//=================================================================================================
//!	cap
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iCap CreateCapArrow
		(
		float	head_scale	= 1.0f , 
		float	width_scale	= 0.5f
		)
{
	instance<CapArrow>	cap;
	cap->SetArrowSize( head_scale , width_scale );
	return (iCap)cap;
}
//=================================================================================================
//!	create solid pen
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPen_gp CreatePenStroke
		(
		iJoint&			joint	= CreateJoint( Bevel_JointType_gp ) , 
		iCap&			cap_s	= CreateCap( Flat_CapType_gp ) , 
		iCap&			cap_e	= CreateCap( Flat_CapType_gp ) , 
		InnerBridgeType	bridge	= Round_InnerBridgeType
		)
{
	instance<OutlineGenStroke>	pen;
	pen->SetInnerBridgeType( bridge );
	pen->SetJoint( joint );
	pen->SetCap( cap_s , cap_e );
	return (iPen_gp)pen;
}
//=================================================================================================
//!	create solid pen
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iPen_gp CreatePenDash
		(
		int				dashnum , 
		float			dash[] , 
		iJoint&			joint	= CreateJoint( Bevel_JointType_gp ) , 
		iCap&			cap_s	= CreateCap( Flat_CapType_gp ) , 
		iCap&			cap_d	= CreateCap( Flat_CapType_gp ) , 
		iCap&			cap_e	= CreateCap( Flat_CapType_gp ) , 
		InnerBridgeType	bridge	= Round_InnerBridgeType
		)
{
	instance<OutlineGenDash>	pen;
	pen->SetDash( dash , dashnum , 0.0f );
	pen->SetInnerBridgeType( bridge );
	pen->SetJoint( joint );
	pen->SetCap( cap_s , cap_d , cap_e );
	return (iPen_gp)pen;
}
//=================================================================================================
//!	create solid pen
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iFont_gp CreateFont
		(
		const fsize&		size		= fsize( 0.0f , 10.0f ) , 
		const wchar_t*		facename	= 0 , 
		FontCharsetType		charset		= Roman_FontCharsetType , 
		int					weight		= 400 , 
		bool				italic		= false , 
		IFont::FontQuarity	quarity		= IFont::Default
		)
{
	wstring	fn;
	if( facename == 0 )
	{
		FontManager_bs	fm;
		FontsetInfo	def = fm.GetDefaultFontsetInfo();
		fn	= def.m_facename;
	}
	else
		fn	= facename;
#ifdef cb_windows
	instance<Font>	font;
	font->Create
			(
			size , 
			fn , 
			charset , 
			weight , 
			italic , 
			quarity
			);
	return (iFont_gp)font;
#else
	#error	Unknown os.
#endif		
}
//=================================================================================================
//!	create blender
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iBlender CreateBlender
		(
		BlenderType_gp	type	= Overlap_BlenderType_gp
		)
{
	if( type == Overlap_BlenderType_gp )
		return (iBlender)instance<BlenderOverlap>();
	else
		return (iBlender)instance<BlenderOnezero>();
}
/**************************************************************************************************
"SaveAttr" class 
**************************************************************************************************/
class SaveAttr
{
// variable member
private:
	iGraphics_gp	m_graphics;
	int				m_id;
public:
//=================================================================================================
SaveAttr
		(
		iGraphics_gp&		g
		) : m_id( 0 )
{
	m_graphics	= g;
	if( m_graphics == true )
		m_id = m_graphics->SaveAttr();
}
//=================================================================================================
~SaveAttr()
{
	if( m_graphics == true )
		m_graphics->RestoreAttr( m_id );
}
};
/**************************************************************************************************
"AttrStack" class 
**************************************************************************************************/
template< class t_attr >
class AttrStack
{
// variable member
private:
	Stackdata<t_attr>		m_stack;
	
// public functions
public:
//=================================================================================================
AttrStack()
{
	Reset();
}
//=================================================================================================
void Reset()
{
	m_stack.Reset();
	m_stack.Push();
	m_stack->Initialize();
}
//=================================================================================================
int Save()
{
	int		id = m_stack.GetRestoreId();
	t_attr*	prev = &( *m_stack );
	m_stack.Push();
	( *m_stack ) = ( *prev );
	return id;
}
//=================================================================================================
bool Pop()
{
	if( m_stack.GetDatanum() <= 1 )
		return false;
	return m_stack.Pop();
}
//=================================================================================================
bool Restore
		(
		int		id
		)
{
	if( id < 1 || id > m_stack.GetDatanum() )
		return false;
	m_stack.Restore( id );
	return true;
}
//=================================================================================================
t_attr& GetAttr()
{
	return ( *m_stack );
}
//=================================================================================================
const t_attr& GetAttr()const
{
	return ( *m_stack );
}
};

/**************************************************************************************************
"Graphics_bs" class 
**************************************************************************************************/
class Graphics_bs : 
	public IGraphics_gp
{
// member class
protected:
	class Clip
	{
	public:
		class Min
		{
		public:
			template<class t_type>
			static
			bool Inside
					(
					const t_type&	v , 
					const t_type&	limit
					)
			{
				return ( v >= limit );
			}
		};
		class Max
		{
		public:
			template<class t_type>
			static
			bool Inside
					(
					const t_type&	v , 
					const t_type&	limit
					)
			{
				return ( v <= limit );
			}
		};
		template<class t_compare , int xy>
		static
		int ClipUV
				(
				fvector2*		destpnt , 
				fvector2*		destuv , 
				int				srcnum , 
				const fvector2*	srcpnt , 
				const fvector2*	srcuv , 
				float			limit
				)
		{
			int		destnum = 0;
			bool	in_f[2];
			in_f[0]	= t_compare::Inside( srcuv[0].m[xy] , limit );
			
			int		srcoff;
			for( srcoff = 0 ; srcoff < srcnum ; srcoff++ )
			{
				int		tgtoff	= ( srcoff + 1 ) < srcnum ? ( srcoff + 1 ) : 0;
				in_f[1]	= t_compare::Inside( srcuv[tgtoff].m[xy] , limit );
			
				if( in_f[0] == true )
				{
					destpnt[destnum]	= srcpnt[srcoff];
					destuv[destnum]		= srcuv[srcoff];
					destnum++;
				}
				if( in_f[0] != in_f[1] )
				{
					float	r = ( limit - srcuv[srcoff].m[xy] ) / ( srcuv[tgtoff].m[xy] - srcuv[srcoff].m[xy] );
					destpnt[destnum]	= ( srcpnt[tgtoff] - srcpnt[srcoff] ) * r + srcpnt[srcoff];
					destuv[destnum]		= ( srcuv[tgtoff] - srcuv[srcoff] ) * r + srcuv[srcoff];
					destnum++;
				}
				in_f[0]	= in_f[1];
			}
			return destnum;
		}
	};
	class Attr
	{
	// variable
	public:
		// view
		bool				m_viewport_f;
		irect				m_viewport;

		// common
		uint8				m_alpha;
		
		// clip
		iPathLogicInfo		m_clip;
		iEdgemapOutline		m_clip_edgemap;
		
		// path
		antialias			m_antialias;
		PathFillRule		m_pathrule;
		
		// stroke
		float				m_stroke_width;
		bool				m_stroke_close;
		
		// paint
		iPaint_gp			m_paint_brush;
		iPen_gp				m_pen;
		iPaint_gp			m_stroke_brush;
		iBlender			m_blender;
		
		// text
		iFont_gp			m_font;
		fsize				m_font_size;
		bool				m_font_f;
		TextAlignHorz_gp	m_align_h;
		TextAlignVert_gp	m_align_v;
		
	// public functions
	public:
	//=================================================================================================
	Attr() : 
			m_viewport_f( false ) , 
			m_antialias( x4_antialias ) , 
			m_pathrule( Winding_PathFillRule ) , 
			m_align_h( Left_TextAlignHorz_gp ) , 
			m_align_v( Top_TextAlignVert_gp ) , 
			m_alpha( 255 ) , 
			m_stroke_width( 1.0f ) , 
			m_stroke_close( false ) , 
			m_font_f( false ) , 
			m_font_size( 0.0f , 10.0f )
	{
	}
	//=================================================================================================
	void Initialize()
	{
		// view
		m_viewport_f	= false;
		m_viewport		= irect();

		// common
		m_alpha			= 255;

		// clip
		m_clip_edgemap.release();		
		m_clip.release();

		// path
		m_antialias		= x4_antialias;
		m_pathrule		= Winding_PathFillRule;
		
		// stroke
		m_stroke_width	= 1.0f;
		m_stroke_close	= false;
		
		// paint
		m_paint_brush.release();
		m_pen.release();
		m_stroke_brush.release();
		m_blender.release();
		
		// text
		m_font.release();
		m_font_size		= fsize( 0.0f , 10.0f );
		m_font_f		= false;
		m_align_h		= Left_TextAlignHorz_gp;
		m_align_v		= Top_TextAlignVert_gp;
	}
	};
	
// variable member
protected:
	AttrStack<Attr>						m_stack;
	
	// surface
	iSurface							m_surface;
	instance<SurfaceViewport>		m_surface_view;

	// path
	instance<EdgemapOutline>			m_edgemap;
	instance<OutlineGenPaint>		m_outlinegen_paint;
	PathToEdgemap_bs					m_path_to_edgemap;
	instance<PathSegmentToOutline>	m_segment_to_outline;
	
	// outline
	instance<Outline>				m_outline;
	
	// polygon
	instance<EdgemapPolygon>			m_edgemap_polygon;
	
	// render
	instance<RendererOutline>		m_render;
	instance<RendererPolygon>		m_render_polygon;
	
	// source
	SourceInterpolateType_gp			m_source_interpolate;
	Wraptype							m_source_wraptype;

	// default
	iBlender							m_blender_default;
	iFont_gp							m_font_default;
	iPaint_gp							m_paint_default;
	iPaint_gp							m_stroke_default;
	iPen_gp								m_pen_default;
	
	// work
	Array<fvector2>						m_pt1_work;
	Array<fvector2>						m_uv1_work;
	Array<fvector2>						m_pt2_work;
	Array<fvector2>						m_uv2_work;
	Array<uint8>						m_work;

	// text
	struct TextLine
	{
		const wchar_t*	m_ptr;
		int				m_length;
		float			m_width;
		float			m_height;
	};
	Array<TextLine>						m_text_line;
	instance<Surface>				m_surface_text;
	iTexture							m_texture_text;
	
// private functions
protected:
//=================================================================================================
irect GetArea()
{
	return irect( m_surface_view->GetDestSize() );
}
//=================================================================================================
float GetSampleScale
		(
		const faffine&	transform
		)const
{
	return (transform.Transform( fvector2( 1.0f , 1.0f ) ) - transform.Transform( fvector2( 0.0f , 0.0f ) ) ).Length();
}
//=================================================================================================
/*
Coord_ld GetCoord
		(
		const Attr&		attr
		)const
{
	ivector2	mov	= attr.m_viewport_ava.Min() - attr.m_viewport.Min();
	if( mov.x == 0 && mov.y == 0 )
		return attr.m_coord;
	else
		return Coord_ld( faffine::GetMove( -mov ) * attr.m_coord.GetDtoPD() , attr.m_coord.GetLogical() );
}
*/
//=================================================================================================
/*
faffine SurfaceToLogical
		(
		const Attr&		attr
		)
{
	return GetCoord( attr ).PDtoL() * faffine::GetMove( -attr.m_viewport_ava.Min() );
}
*/
//=================================================================================================
/*
faffine LogicalToSurface
		(
		const Attr&		attr
		)
{
	 return faffine::GetMove( attr.m_viewport_ava.Min() ) * GetCoord( attr ).LtoPD();
}
*/
//=================================================================================================
iEdgemapOutline GetClip
		(
		Attr&		attr
		)
{
	if( attr.m_clip == false )
		return iEdgemapOutline();
	if( attr.m_clip_edgemap == true )
		return attr.m_clip_edgemap;

	instance<EdgemapOutline>		edgemap;
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_path_to_edgemap.ToEdgemap
		(
		(iEdgemapOutline)edgemap , 
		attr.m_clip , 
		faffine() ,
		(iOutlineGen)m_outlinegen_paint , 
		Winding_PathFillRule , 			
		faffine() , 
		irect( m_surface_view->GetDestSize() ) , 
		attr.m_antialias
		);
	attr.m_clip_edgemap	= (iEdgemapOutline)edgemap;
	return attr.m_clip_edgemap;
}
//=================================================================================================
DPI GetDPI()
{
	return m_surface_view->GetDestDPI();	
}
//=================================================================================================
iBlender& GetBlender
		(
		Attr&	attr
		)
{
	if( attr.m_blender == false )
	{
		if( m_blender_default == false )
			m_blender_default	= (iBlender)instance<BlenderOverlap>();
		attr.m_blender = m_blender_default;
	}
	return attr.m_blender;
}
//=================================================================================================
iBlender& GetBlenderText()
{
	if( m_blender_default == false )
		m_blender_default	= (iBlender)instance<BlenderOverlap>();
	return m_blender_default;
}
//=================================================================================================
iTexture& GetTextureText()
{
	if( m_texture_text == false )
	{
		instance<TextureImageNearest>	tex;
		tex->SetTexture( (iSurfaceSource)m_surface_text );
		m_texture_text	= (iTexture)tex;
	}
	return m_texture_text;
}
//=================================================================================================
iPaint_gp& GetPaint
		(
		Attr&	attr
		)
{
	if( attr.m_paint_brush == false )
	{
		if( m_paint_default == false )
			m_paint_default = (iPaint_gp)CreatePaintSolid();
		attr.m_paint_brush	= m_paint_default;
	}
	return attr.m_paint_brush;
}
//=================================================================================================
iPaint_gp& GetStrokePaint
		(
		Attr&	attr
		)
{
	if( attr.m_stroke_brush == false )
	{
		if( m_stroke_default == false )
			m_stroke_default = (iPaint_gp)CreatePaintSolid(); 
		attr.m_stroke_brush	= m_stroke_default;
	}
	return attr.m_stroke_brush;
}
//=================================================================================================
iPen_gp& GetPen
		(
		Attr&		attr , 
		float		samplescale
		)
{
	if( attr.m_pen == false )
	{
		if( m_pen_default == false )
			m_pen_default = CreatePenStroke();
		attr.m_pen	= m_pen_default;
	}
	attr.m_pen->SetStrokeWidth( attr.m_stroke_width );
	attr.m_pen->SetSampleScale( samplescale );
	return attr.m_pen;
}
//=================================================================================================
iFont_gp& GetFont
		(
		Attr&	attr
		)
{
	if( attr.m_font == false )
	{
		if( m_font_default == false )
			m_font_default	= CreateFont();
		attr.m_font	= m_font_default;
	}
	return attr.m_font;
}
//=================================================================================================
void DrawOutline
		(
		Attr&			attr , 
		PathFillRule	rule , 
		iPaint_gp&		brush , 
		const faffine&	trans
		)
{
	irect	area	= irect( m_surface_view->GetDestSize() );

	// edgemap
	m_edgemap->Initialize( attr.m_antialias , area );
	m_edgemap->BeginOutline();
	m_edgemap->SetOutline( (iOutline)m_outline , trans );
	m_edgemap->EndOutline( rule );

	// clip
	iEdgemapOutline	clip = GetClip( attr );
	if( clip == true )
		m_edgemap->Operate( And_PathOperator , clip );

	// paint
	m_render->SetBlender( GetBlender( attr ) );
	m_render->SetPaint( (iPaint)brush );
	m_render->Render( (iSurfaceDest)m_surface_view , (iEdgemapOutlineInfo)m_edgemap , &area , 1 , attr.m_alpha );
}
//=================================================================================================
bool PolygonToRect
		(
		irect*			r , 
		fvector2		ruv[4] , 
		int				vertnum , 
		const fvector2*	vert , 
		const fvector2*	uv , 
		const faffine&	l_to_pd
		)const
{
	if( vertnum != 4 )
		return false;
	ivector2	pnt[4];
	int		i;
	for( i = 0 ; i < 4 ; i++ )
	{
		pnt[i]	= l_to_pd.Transform( vert[i] );
		if( (float)pnt[i].x != vert[i].x
		 || (float)pnt[i].y != vert[i].y )
			return false;
	}
	int		indexbuf[5]	= { 0 , 1 , 2 , 3 , 0 };
	int*	index	= &indexbuf[0];
	if( pnt[index[0]].y != pnt[index[1]].y )
		index++;
	if( pnt[index[0]].y != pnt[index[1]].y
	||  pnt[index[1]].x != pnt[index[2]].x
	||  pnt[index[2]].y != pnt[index[3]].y
	||  pnt[index[3]].x != pnt[index[0]].x )
		return false;
	
	// normalize
	if( pnt[index[0]].x > pnt[index[2]].x )
	{
		swap( &index[0] , &index[1] );
		swap( &index[2] , &index[3] );
	}
	if( pnt[index[0]].y > pnt[index[2]].y )
	{
		swap( &index[0] , &index[3] );
		swap( &index[1] , &index[2] );
	}
	// store
	store( r , irect( pnt[index[0]] , pnt[index[2]] ) );
	if( uv != 0 )
	{
		for( i = 0 ; i < 4 ; i++ )
			ruv[i]	= uv[index[i]];
	}
	return true;
}
//=================================================================================================
void DrawRectClipped_image
		(
		Attr&			attr , 
		const irect&		rect , 
		const fvector2		map[4] , 
		iTexture&			source , 
		iBlender&			blender
		)
{
	// clip
	if( rect.IsExist() == false )
		return;
	isize	size	= rect.Size();
		
	// suv
	fvector2	luv		= map[0];
	fvector2	ruv		= map[1];
	fvector2	dluv	= ( map[3] - map[0] ) / ( float )size.height;
	fvector2	druv	= ( map[2] - map[1] ) / ( float )size.height;
	luv += dluv * 0.5f;
	ruv += druv * 0.5f;
		
	// dest
	int			d_pitchbyte;
	pixelformat	d_fmt		= m_surface_view->GetDestFormat();
	int			d_pixelbyte	= get_pixel_byte( d_fmt );
	uint8*		dp			= (uint8*)m_surface_view->GetDestPixelPtr( &d_pitchbyte );
	dp += rect.ymin * d_pitchbyte + rect.xmin * d_pixelbyte;

	// source
	pixelformat	s_fmt		= source->TextureImageFormat();
	int			s_pixelbyte	= get_pixel_byte( s_fmt );
	m_work.Resize( size.width * s_pixelbyte );
		
	// render
	if( false == source->BeginTextureImage() )
		return;
	int		y;
	for( y = rect.ymin ; y < rect.ymax ; y++ )
	{
		source->TextureImage( m_work.GetPtr() , size.width , luv , ruv );
		blender->BlendImage( d_fmt , dp , s_fmt , m_work.GetPtr() , size.width , rgba() , attr.m_alpha );

		dp += d_pitchbyte;		
		luv += dluv;
		ruv += druv;
	}
	source->EndTextureImage();
}
//=================================================================================================
void DrawRectClipped_color
		(
		Attr&			attr , 
		const irect&		rect , 
		iTexture&			source , 
		iBlender&			blender
		)
{
	// clip
	if( rect.IsExist() == false )
		return;
	isize	size	= rect.Size();
		
	// dest
	int			d_pitchbyte;
	pixelformat	d_fmt		= m_surface_view->GetDestFormat();
	int			d_pixelbyte	= get_pixel_byte( d_fmt );
	uint8*		dp			= (uint8*)m_surface_view->GetDestPixelPtr( &d_pitchbyte );
	dp += rect.ymin * d_pitchbyte + rect.xmin * d_pixelbyte;

	// source
	rgba		c = source->TextureColor().MulAlpha( attr.m_alpha );
		
	// render
	int		y;
	for( y = rect.ymin ; y < rect.ymax ; y++ )
	{
		blender->BlendColor( d_fmt , dp , size.width , c );
		dp += d_pitchbyte;		
	}
}
//=================================================================================================
void PaintRectClipped_image
		(
		Attr&			attr , 
		const irect&		rect , 
		iPaint_gp&			source
		)
{
	// clip
	if( rect.IsExist() == false )
		return;
	isize	size	= rect.Size();
		
	// dest
	int			d_pitchbyte;
	pixelformat	d_fmt		= m_surface_view->GetDestFormat();
	int			d_pixelbyte	= get_pixel_byte( d_fmt );
	uint8*		dp			= (uint8*)m_surface_view->GetDestPixelPtr( &d_pitchbyte );
	dp += rect.ymin * d_pitchbyte + rect.xmin * d_pixelbyte;

	// source
	pixelformat	s_fmt		= source->PaintImageFormat();
	int			s_pixelbyte	= get_pixel_byte( s_fmt );
	m_work.Resize( size.width * s_pixelbyte );
		
	// render
	iBlender&	blender	= GetBlender( attr );
	if( false == source->BeginPaintImage() )
		return;
	int		y;
	for( y = rect.ymin ; y < rect.ymax ; y++ )
	{
		fvector2	l( (float)rect.xmin , (float)y + 0.5f );
		fvector2	r( (float)rect.xmax , (float)y + 0.5f );
		source->PaintImage( m_work.GetPtr() , size.width , l , r );
		blender->BlendImage( d_fmt , dp , s_fmt , m_work.GetPtr() , size.width , rgba() , attr.m_alpha );

		dp += d_pitchbyte;		
	}
	source->EndPaintImage();
}
//=================================================================================================
void PaintRectClipped_color
		(
		Attr&			attr , 
		const irect&		rect , 
		iPaint_gp&			source
		)
{
	// clip
	if( rect.IsExist() == false )
		return;
	isize	size	= rect.Size();
		
	// dest
	int			d_pitchbyte;
	pixelformat	d_fmt		= m_surface_view->GetDestFormat();
	int			d_pixelbyte	= get_pixel_byte( d_fmt );
	uint8*		dp			= (uint8*)m_surface_view->GetDestPixelPtr( &d_pitchbyte );
	dp += rect.ymin * d_pitchbyte + rect.xmin * d_pixelbyte;

	// source
	rgba		c = source->PaintColor().MulAlpha( attr.m_alpha );
		
	// render
	iBlender&	blender		= GetBlender( attr );
	int		y;
	for( y = rect.ymin ; y < rect.ymax ; y++ )
	{
		blender->BlendColor( d_fmt , dp , size.width , c );
		dp += d_pitchbyte;		
	}
}
//=================================================================================================
void DrawRect_pd
		(
		Attr&			attr , 
		const irect&		r , 
		const fvector2		uv[4] , 
		iTexture&			source , 
		iBlender&			blender
		)
{
	irect	rt = r & m_surface_view->GetDestAvailableArea();
	if( rt.IsExist() == false )
		return;
	
	ITexture::Type	st		= source->TextureType();
	if( st == ITexture::Color )
		DrawRectClipped_color( attr , 	rt , source , blender );
	else
	{
		fvector2		cuv[4];
		const fvector2*	puv = uv;
		if( rt != r )
		{
			fsize		rs	= r.Size();
			float		rx[2]	= { ( rt.xmin - r.xmin ) / rs.width , ( rt.xmax - r.xmin ) / rs.width };
			float		ry[2]	= { ( rt.ymin - r.ymin ) / rs.height, ( rt.ymax - r.ymin ) / rs.height };
			fvector2	tuv[] = 
				{
					(uv[1]-uv[0])*rx[0]+uv[0] , 
					(uv[1]-uv[0])*rx[1]+uv[0] , 
					(uv[2]-uv[3])*rx[1]+uv[3] , 
					(uv[2]-uv[3])*rx[0]+uv[3] , 
				};
			cuv[0]	= ( tuv[3] - tuv[0] ) * ry[0] + tuv[0];
			cuv[1]	= ( tuv[2] - tuv[1] ) * ry[0] + tuv[1];
			cuv[2]	= ( tuv[2] - tuv[1] ) * ry[1] + tuv[1];
			cuv[3]	= ( tuv[3] - tuv[0] ) * ry[1] + tuv[0];
			puv	= cuv;
		}
		DrawRectClipped_image( attr , 	rt , puv , source , blender );
	}
}
//=================================================================================================
void PaintRect_pd
		(
		const irect&		r , 
		iPaint_gp&			source
		)
{
	irect	rt = r & m_surface_view->GetDestAvailableArea();
	if( rt.IsExist() == false )
		return;

	Attr&		attr	= m_stack.GetAttr();
	IPaint::Type	st		= source->PaintType();
	if( st == IPaint::Color )
		PaintRectClipped_color( attr , rt , source );
	else if( st == IPaint::Image )
		PaintRectClipped_image( attr , rt , source );
}
//=================================================================================================
bool DrawRect
		(
		Attr&		attr , 
		int				vertnum , 
		const fvector2*	vert , 
		const fvector2*	uv , 
		const faffine&	trans , 
		iTexture&		source , 
		iBlender&		blender
		)
{
	irect		r;
	fvector2	ruv[4];
	if( false == PolygonToRect( &r , ruv , vertnum , vert , uv , trans ) )
		return false;
	DrawRect_pd( attr , r , ruv , source , blender );
	return true;	
}
//=================================================================================================
bool PaintRect
		(
		int				vertnum , 
		const fvector2*	vert , 
		const faffine&	trans , 
		iPaint&	source
		)
{
	irect		r;
	if( false == PolygonToRect( &r , 0 , vertnum , vert , 0 , trans ) )
		return false;
	PaintRect_pd( r , source );
	return true;	
}
//=================================================================================================
void cb_call DrawPolygon_blender
		(
		Attr&		attr , 
		int				vertexnum , 
		const fvector2	vertex[] , 
		const fvector2	uv[] , 
		const faffine&	transform , 
		iTexture&		source , 
		iBlender&		blender
		)
{
	if( vertexnum < 3 )
		return;
	irect		area	= GetArea();
	if( attr.m_clip == false && true == DrawRect( attr , vertexnum , vertex , uv , transform , source , blender ) )
		return;

	m_edgemap_polygon->Initialize( attr.m_antialias , area );
	m_edgemap_polygon->AddPolygon( vertexnum , vertex , uv , transform );

	// clip
	iEdgemapOutline	clip = GetClip( attr );
	if( clip == true )
		m_edgemap_polygon->Operate( And_PolygonOperator , clip );

	m_render_polygon->SetBlender( blender );
	m_render_polygon->SetTexture( source );
	m_render_polygon->Render( (iSurfaceDest)m_surface_view , (iEdgemapPolygon)m_edgemap_polygon , &area , 1 , attr.m_alpha );
	m_render_polygon->ReleaseTexture();
}
//=================================================================================================
void cb_call BitBlt_blender
		(
		Attr&			attr , 
		const fvector2&		dpos , 
		iTexture&			source , 
		const fvector2&		spos , 
		iBlender&			blender
		)
{
	fsize		ss		= source->TextureSize();
	fsize		ts		= ss;
	fvector2	tp		= spos;
	frect		tr		= frect( ts ).Move( -tp ).Move( dpos );
	fvector2	vert[4]	= 
		{
		fvector2( tr.xmin , tr.ymin ) , 
		fvector2( tr.xmax , tr.ymin ) , 
		fvector2( tr.xmax , tr.ymax ) , 
		fvector2( tr.xmin , tr.ymax )
		};
	fvector2	uv[4]	= 
		{
		fvector2( 0.0f , 0.0f ) , 
		fvector2( 1.0f , 0.0f ) , 
		fvector2( 1.0f , 1.0f ) , 
		fvector2( 0.0f , 1.0f )
		};
	DrawPolygon_blender( attr , 4 , vert , uv , faffine() , source , blender );
}
//=================================================================================================
fsize SetTextline
		(
		Array<TextLine>&	text_line , 
		iSurfaceDest&		dest , 
		iFont_gp&			font , 
		const wchar_t*		str , 
		int					length
		)
{
	text_line.Resize( 0 );

	fsize			size;
	const wchar_t*	line	= str;
	int				column	= 0;
	int				i;
	for( i = 0 ; i < length ; i++ )
	{
		if( *str != L'\n' )
			column++;
		if( *str == L'\n' || i == length - 1 )
		{
			fsize	ls	= font->GetStringSize( dest , line , column );
			int		loff= text_line.Add();
			text_line[loff].m_length	= column;
			text_line[loff].m_ptr		= line;
			text_line[loff].m_width		= ls.width;
			text_line[loff].m_height	= ls.height;
			size.width	= max( size.width , ls.width );
			size.height+= ls.height;
			
			line	= str + 1;				
			column	= 0;
		}
		str++;
	}
	return size;
}
//=================================================================================================
void DrawTextline
		(
		Attr&			attr , 
		iSurfaceDest&		dest , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const frect*		clip , 
		iFont_gp&			font , 
		Array<TextLine>&	text_line , 
		const fsize&		text_size
		)
{
	fvector2		p	= fvector2( pos.x , pos.y + font->GetAscent() );
	if( attr.m_align_v == Center_TextAlignVert_gp )
		p.y -= text_size.height / 2.0f;
	else if( attr.m_align_v == Bottom_TextAlignVert_gp )
		p.y -= text_size.height;

	int		loff , lnum = text_line.GetDatanum();
	for( loff = 0 ; loff < lnum ; loff++ )
	{
		fvector2	dp = p;
		if( attr.m_align_h == Right_TextAlignHorz_gp )
			dp.x -= text_line[loff].m_width;
		else if( attr.m_align_h == Center_TextAlignHorz_gp )	
			dp.x -= text_line[loff].m_width / 2.0f;
		font->DrawString( dest , text_line[loff].m_ptr , text_line[loff].m_length , dp , color , clip );
		p.y += text_line[loff].m_height;
	}		
}
//=================================================================================================
void DrawTextD
		(
		Attr&				attr , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const frect*		clip , 
		iFont_gp&			font
		)
{
	iSurfaceDest	dest	= (iSurfaceDest)m_surface_view;
	fsize			size	= SetTextline( m_text_line , dest , font , str , length );
	DrawTextline( attr , dest , str , length , pos , color.MulAlpha( attr.m_alpha ) , clip , font , m_text_line , size );
}
//=================================================================================================
void DrawTextD_clip
		(
		Attr&			attr , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const frect*		clip , 
		iFont_gp&			font
		)
{
	fvector2		p		= pos;
	iSurfaceDest	dest	= (iSurfaceDest)m_surface_view;
	fsize			size	= SetTextline( m_text_line , dest , font , str , length );

	if( attr.m_align_h == Center_TextAlignHorz_gp )
		p.x -= size.width / 2.0f;
	else if( attr.m_align_h == Right_TextAlignHorz_gp )
		p.x -= size.width;
	if( attr.m_align_v == Center_TextAlignVert_gp )
		p.y -= size.height / 2.0f;
	else if( attr.m_align_v == Bottom_TextAlignVert_gp )
		p.y -= size.height;

	irect	draw_rect( (int)floorf( p.x ) , (int)floorf( p.y ) , ( int )ceilf( p.x + size.width ) , ( int )ceilf( p.y + size.height ) );
	irect	ava_rect	= draw_rect & m_surface_view->GetDestAvailableArea();
	if( clip != 0 )
		ava_rect &= irect( *clip );
	if( ava_rect.IsExist() == false )
		return;
	
	// clear	
	cb_verify( true == m_surface_text->Create( ava_rect.Size() , rgba_pixelformat , DPI() ) );
	{
		irect	r			= m_surface_text->GetDestAvailableArea();
		int		pixbyte		= get_pixel_byte( m_surface_text->GetDestFormat() );
		int		pitchbyte;
		uint8*	p			= (uint8*)m_surface_text->GetDestPixelPtr( &pitchbyte );
		int		wbyte		= r.Width() * pixbyte;
		int		h			= r.Height();
		p += r.ymin * pitchbyte + r.xmin * pixbyte;
		
		int		hoff;
		for( hoff = 0 ; hoff < h ; hoff++ )
		{
/*		
			int	woff;
			for( woff = 0 ; woff < r.Width() ;woff++ )
			{
				p[woff*pixbyte]	  = 0;
				p[woff*pixbyte+1] = 255;
				p[woff*pixbyte+2] = 255;
				p[woff*pixbyte+3] = 255;
			}
*/
			MemoryZero( p , wbyte );
			p += pitchbyte;
		}		
	}
	// draw
	DrawTextline( attr , (iSurfaceDest)m_surface_text , str , length , pos - ava_rect.Min() , color , 0 , font , m_text_line , size );
	BitBlt_blender( attr , ava_rect.Min() , GetTextureText() , fvector2(0.0f,0.0f) , GetBlenderText() );
}
// "IPaint" interface functions
public:
//=================================================================================================
IPaint::Type cb_call PaintType()
{
	return IPaint::Image;
}
//=================================================================================================
rgba cb_call PaintColor()
{
	return rgba();
}
//=================================================================================================
pixelformat	cb_call PaintImageFormat()
{
	return m_surface_view->GetSourceFormat();
}
//=================================================================================================
bool cb_call BeginPaintImage()
{
	if( m_surface == false )
		return false;
	isize	size = m_surface_view->GetSourceSize();
	if( size.width == 0 || size.height == 0 )
		return false;
	return true;
}
//=================================================================================================
void cb_call PaintImage
		(
		void				*image , 
		int					len , 
		const fvector2&		sp ,		//!< [in] device unit
		const fvector2&		tp			//!< [in] device unit
		)
{
	isize	size = m_surface_view->GetSourceSize();
	int			pitchbyte;
	const void	*p = m_surface_view->GetSourcePixelPtr( &pitchbyte );
	if( m_source_interpolate == Nearest_SourceInterpolateType_gp )
	{
		pp_texture_nearest_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				sp.x / (float)size.width , 
				sp.y / (float)size.height , 
				tp.x / (float)size.width , 
				tp.y / (float)size.height , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype )
				);
	}
	else if( m_source_interpolate == Linear_SourceInterpolateType_gp )
	{
		pp_texture_weight4f_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				sp.x / (float)size.width , 
				sp.y / (float)size.height , 
				tp.x / (float)size.width , 
				tp.y / (float)size.height , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype ) , 
				TableSinR_f()
				);
	}
	else if( m_source_interpolate == Bicubic_SourceInterpolateType_gp )
	{
		pp_texture_weight16f_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				sp.x / (float)size.width , 
				sp.y / (float)size.height , 
				tp.x / (float)size.width , 
				tp.y / (float)size.height , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype ) , 
				TableBicubic1_f() , 
				TableBicubic2_f()
				);
	}
}
//=================================================================================================
void cb_call EndPaintImage()
{
}
// "ITexture" interface functions
public:
//=================================================================================================
isize cb_call TextureSize()
{
	if( m_surface == false )
		return isize();
	return m_surface_view->GetSourceSize();
}
//=================================================================================================
DPI cb_call TextureDPI()
{
	return GetDPI();
}
//=================================================================================================
ITexture::Type cb_call TextureType()
{
	return ITexture::Image;
}
//=================================================================================================
rgba cb_call TextureColor()
{
	return rgba();
}
//=================================================================================================
pixelformat	cb_call TextureImageFormat()
{
	return m_surface_view->GetSourceFormat();
}
//=================================================================================================
bool cb_call BeginTextureImage()
{
	if( m_surface == false )
		return false;
	isize	size = m_surface_view->GetSourceSize();
	if( size.width == 0 || size.height == 0 )
		return false;
	return true;
}
//=================================================================================================
void cb_call TextureImage
		(
		void				*image , 
		int					len , 
		const fvector2&		suv ,			//!< [in] uv map
		const fvector2&		tuv				//!< [in] uv map
		)
{
	int			pitchbyte;
	const void	*p = m_surface_view->GetSourcePixelPtr( &pitchbyte );
	if( m_source_interpolate == Nearest_SourceInterpolateType_gp )
	{
		pp_texture_nearest_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				suv.x , 
				suv.y , 
				tuv.x , 
				tuv.y , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype )
				);
	}
	else if( m_source_interpolate == Linear_SourceInterpolateType_gp )
	{
		pp_texture_weight4f_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				suv.x , 
				suv.y , 
				tuv.x , 
				tuv.y , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype ) , 
				TableSinR_f()
				);
	}
	else if( m_source_interpolate == Bicubic_SourceInterpolateType_gp )
	{
		pp_texture_weight16f_m
				(
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				image , 
				len , 
				suv.x , 
				suv.y , 
				tuv.x , 
				tuv.y , 
				to_pp_format( m_surface_view->GetSourceFormat() ) , 
				p , 
				pitchbyte , 
				m_surface_view->GetSourceSize().width , 
				m_surface_view->GetSourceSize().height , 
				to_pp_color( rgba() ) , 
				to_pp_alpha( 255 ) , 
				to_pp_wraptype( m_source_wraptype ) , 
				TableBicubic1_f() , 
				TableBicubic2_f()
				);
	}
}
//=================================================================================================
void cb_call EndTextureImage()
{
}
// "IGraphics" interface functions
public:
//=================================================================================================
void cb_call SetSurface
		(
		iSurface&		surface
		)
{
	m_surface			= surface;
	m_surface_view->SetSurface(m_surface);
	m_stack.Reset();
}
//=================================================================================================
void cb_call ReleaseSurface()
{
	m_surface.release();
	m_surface_view->ReleaseSurface();
	m_stack.Reset();
}
//=================================================================================================
iSurface cb_call GetSurface()
{
	return m_surface;
}
//=================================================================================================
isize cb_call GetSurfaceSize()
{
	return m_surface_view->GetSurfaceSize();
}
//=================================================================================================
void cb_call SetSourceInterpolate
		(
		SourceInterpolateType_gp	type
		)
{
	m_source_interpolate	= type;
}
//=================================================================================================
SourceInterpolateType_gp cb_call GetSourceInterpolate()const
{
	return m_source_interpolate;
}
//=================================================================================================
void cb_call SetSourceWraptype
		(
		Wraptype	wraptype
		)
{
	m_source_wraptype	= wraptype;
}
//=================================================================================================
Wraptype cb_call GetSourceWraptype()const
{
	return m_source_wraptype;
}
//=================================================================================================
int cb_call SaveAttr()
{
	return m_stack.Save();
}
//=================================================================================================
bool cb_call RestoreAttr()
{
	if( false == m_stack.Pop() )
		return false;
		
	Attr&	attr		= m_stack.GetAttr();
	if( attr.m_viewport_f == false )
		m_surface_view->ReleaseViewport();
	else
		m_surface_view->SetViewport( attr.m_viewport );
	return true;
}
//=================================================================================================
bool cb_call RestoreAttr
		(
		int		id
		)
{
	if( false == m_stack.Restore( id ) )
		return false;
		
	Attr&	attr		= m_stack.GetAttr();
	if( attr.m_viewport_f == false )
		m_surface_view->ReleaseViewport();
	else
		m_surface_view->SetViewport( attr.m_viewport );
	return true;
}
//=================================================================================================
void cb_call SetViewport
		(
		const irect&	viewport
		)
{
	Attr&	attr		= m_stack.GetAttr();
	if( attr.m_viewport_f == true && attr.m_viewport == viewport )
		return;
	if( attr.m_viewport_f == false )
	{
		attr.m_viewport_f	= true;
		attr.m_viewport		= viewport.Normalize();
	}
	else
	{
		attr.m_viewport		= viewport.Normalize();
	}
	m_surface_view->SetViewport( attr.m_viewport );
	attr.m_clip_edgemap.release();
}
//=================================================================================================
irect cb_call GetViewport()const
{
	const Attr&	attr	= m_stack.GetAttr();
	return attr.m_viewport;
}
//=================================================================================================
void cb_call SetClip
		(
		IPathLogicInfo*		clip , 
		const faffine&		transform
		)
{
	Attr&	attr		= m_stack.GetAttr();
	instance<PathLogic>	path;
	path->Copy( clip );
	path->MulTransform( transform );
	if( attr.m_clip == true )
		path->Operate( And_PathOperator , attr.m_clip , faffine() );
	attr.m_clip	= (iPathLogicInfo)path;
	attr.m_clip_edgemap.release();
}
//=================================================================================================
void cb_call ReleaseClip()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_clip.release();
	attr.m_clip_edgemap.release();
}
//=================================================================================================
void cb_call SetAlpha
		(
		uint8		alpha
		)
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_alpha	= alpha;
}
//=================================================================================================
uint8 cb_call GetAlpha()
{
	Attr&	attr	= m_stack.GetAttr();
	return attr.m_alpha;
}
//=================================================================================================
void cb_call SetBlender
		(
		iBlender&	blender
		)
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_blender = blender;
}
//=================================================================================================
void cb_call ReleaseBlender()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_blender.release();
}
//=================================================================================================
void cb_call SetPaint
		(
		iPaint_gp&		brush
		)
{
	if( brush == false )
		return;
	Attr&	attr	= m_stack.GetAttr();
	attr.m_paint_brush	= brush;
}
//=================================================================================================
void cb_call ReleasePaint()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_paint_brush.release();
}
//=================================================================================================
void cb_call SetStrokePen
		(
		iPen_gp&		pen
		)
{
	if( pen == false )
		return;
	Attr&	attr	= m_stack.GetAttr();
	attr.m_pen		= pen;
}
//=================================================================================================
void cb_call ReleaseStrokePen()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_pen.release();
}
//=================================================================================================
void cb_call SetStrokePaint
		(
		iPaint_gp&		brush
		)
{
	if( brush == false )
		return;
	Attr&	attr	= m_stack.GetAttr();
	attr.m_stroke_brush	= brush;
}
//=================================================================================================
void cb_call ReleaseStrokePaint()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_stroke_brush.release();
}
//=================================================================================================
void cb_call SetPaintPathFillRule
		(
		PathFillRule	rule
		)
{
	m_stack.GetAttr().m_pathrule	= rule;
}
//=================================================================================================
PathFillRule cb_call GetPaintPathFillRule()const
{
	return m_stack.GetAttr().m_pathrule;
}
//=================================================================================================
void cb_call SetAntialias
		(
		antialias	anti
		)
{
	Attr&	attr	= m_stack.GetAttr();
	if( attr.m_antialias == anti )
		return;
	attr.m_antialias	= anti;
	attr.m_clip_edgemap.release();
}
//=================================================================================================
antialias cb_call GetAntialias()const
{
	return m_stack.GetAttr().m_antialias;
}
//=================================================================================================
void cb_call SetStrokeWidth
		(
		float		w
		)
{
	m_stack.GetAttr().m_stroke_width	= w;
}
//=================================================================================================
float cb_call GetStrokeWidth()const
{
	return m_stack.GetAttr().m_stroke_width;
}
//=================================================================================================
void cb_call SetStrokeClose
		(
		bool	close
		)
{
	m_stack.GetAttr().m_stroke_close	= close;
}
//=================================================================================================
bool cb_call GetStrokeClose()const
{
	return m_stack.GetAttr().m_stroke_close;
}
//=================================================================================================
void cb_call PaintLine
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform
		)
{
	if( pntnum < 2 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	if( attr.m_clip == false && true == PaintRect( pntnum , pnt , transform , GetPaint( attr ) ) )
		return;
	// make outline
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)m_outlinegen_paint );
	m_segment_to_outline->Move( pnt[0] , transform , true );
	m_segment_to_outline->Line( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , attr.m_pathrule , GetPaint( attr ) , faffine() );
}
//=================================================================================================
void cb_call PaintBezierQ
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform
		)
{
	if( pntnum < 3 )
		return;
	if( ( ( pntnum - 1 ) & 1 ) != 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)m_outlinegen_paint );
	m_segment_to_outline->Move( pnt[0] , transform , true );
	m_segment_to_outline->BezierQ( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , attr.m_pathrule , GetPaint( attr ) , faffine() );
}
//=================================================================================================
void cb_call PaintBezierC
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform
		)
{
	if( pntnum < 4 )
		return;
	if( ( ( pntnum - 1 ) % 3 ) != 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)m_outlinegen_paint );
	m_segment_to_outline->Move( pnt[0] , transform , true );
	m_segment_to_outline->BezierC( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , attr.m_pathrule , GetPaint( attr ) , faffine() );
}
//=================================================================================================
fvector2 cb_call PaintArc
		(
		const fvector2&	sp , 
		const fvector2&	cp , 
		float			ry , 
		float			angle , 
		const faffine&	transform
		)
{
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)m_outlinegen_paint );
	m_segment_to_outline->Move( sp , transform , true );
	m_segment_to_outline->Arc( cp , ry , angle );
	fvector2	ep = m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , attr.m_pathrule , GetPaint( attr ) , faffine() );
	return ep;
}
//=================================================================================================
void cb_call PaintArc
		(
		const fvector2&	sp , 
		float			rx , 
		float			ry , 
		float			x_axis_rot , 
		bool			large , 
		bool			sweep , 
		const fvector2&	tp , 
		const faffine&	transform
		)
{
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)m_outlinegen_paint );
	m_segment_to_outline->Move( sp , transform , true );
	m_segment_to_outline->Arc( rx , ry , x_axis_rot , large , sweep , tp );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , attr.m_pathrule , GetPaint( attr ) , faffine() );
}
//=================================================================================================
void cb_call PaintCircle
		(
		const fvector2&	cp , 
		float			rx , 
		float			ry , 
		const faffine&	transform
		)
{
//	rx	= max( rx , 0.00001f );
	PaintArc( fvector2( cp.x - rx , cp.y ) , cp , ry / rx , PI_2_f , transform );
}
//=================================================================================================
void cb_call PaintRect
		(
		const frect*		rect , 
		const faffine&		trans
		)
{
	Attr&	attr	= m_stack.GetAttr();
	if( rect == 0 )
	{
		if( attr.m_clip == false )
			PaintRect_pd( m_surface_view->GetDestAvailableArea() , GetPaint( attr ) );
		else
		{
			irect	aa = m_surface_view->GetDestAvailableArea();
			fvector2	pnt[4] = 
					{ 
					fvector2( aa.xmin , aa.ymin ) , 
					fvector2( aa.xmax , aa.ymin ) , 
					fvector2( aa.xmax , aa.ymax ) , 
					fvector2( aa.xmin , aa.ymax )
					};
			PaintLine( 4 , pnt , faffine() );
		}
	}
	else
	{
		fvector2	pnt[4] = 
				{ 
				fvector2( rect->xmin , rect->ymin ) , 
				fvector2( rect->xmax , rect->ymin ) , 
				fvector2( rect->xmax , rect->ymax ) , 
				fvector2( rect->xmin , rect->ymax )
				};
		PaintLine( 4 , pnt , trans );
	}
}
//=================================================================================================
void cb_call PaintPath
		(
		IPathLogicInfo*	path , 
		const faffine&	transform
		)
{
	if( path == 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();
	irect		area	= GetArea();
	
	// edgemap
	m_outlinegen_paint->SetSampleScale( 1.0f );
	m_path_to_edgemap.ToEdgemap
		(
		(iEdgemapOutline)m_edgemap , 
		path , 
		transform , 
		(iOutlineGen)m_outlinegen_paint , 
		attr.m_pathrule , 
		faffine() , 
		area , 
		attr.m_antialias
		);
	// clip
	iEdgemapOutline	clip = GetClip( attr );
	if( clip == true )
		m_edgemap->Operate( And_PathOperator , clip );

	// paint
	m_render->SetBlender( GetBlender( attr ) );
	m_render->SetPaint( (iPaint)GetPaint( attr ) );
	m_render->Render( (iSurfaceDest)m_surface_view , (iEdgemapOutlineInfo)m_edgemap , &area , 1 , attr.m_alpha );
}
//=================================================================================================
void cb_call StrokeLine
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	if( pntnum < 2 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)GetPen( attr , GetSampleScale( transform2 ) ) );
	m_segment_to_outline->Move( pnt[0] , transform1 , attr.m_stroke_close );
	m_segment_to_outline->Line( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , Winding_PathFillRule , GetStrokePaint( attr ) , transform2 );
}
//=================================================================================================
void cb_call StrokeBezierQ
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	if( pntnum < 3 )
		return;
	if( ( ( pntnum - 1 ) & 1 ) != 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)GetPen( attr , GetSampleScale( transform2 ) ) );
	m_segment_to_outline->Move( pnt[0] , transform1 , attr.m_stroke_close );
	m_segment_to_outline->BezierQ( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , Winding_PathFillRule , GetStrokePaint( attr ) , transform2 );
}
//=================================================================================================
void cb_call StrokeBezierC
		(
		int				pntnum , 
		const fvector2	pnt[] , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	if( pntnum < 4 )
		return;
	if( ( ( pntnum - 1 ) % 3 ) != 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)GetPen( attr , GetSampleScale( transform2 ) ) );
	m_segment_to_outline->Move( pnt[0] , transform1 , attr.m_stroke_close );
	m_segment_to_outline->BezierC( pntnum-1 , &pnt[1] );
	m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , Winding_PathFillRule , GetStrokePaint( attr ) , transform2 );
}
//=================================================================================================
fvector2 cb_call StrokeArc
		(
		const fvector2&	sp , 
		const fvector2&	cp , 
		float			ry , 
		float			angle , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	Attr&	attr	= m_stack.GetAttr();

	// make outline
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)GetPen( attr , GetSampleScale( transform2 ) ) );
	m_segment_to_outline->Move( sp , transform1 , attr.m_stroke_close );
	m_segment_to_outline->Arc( cp , ry , angle );
	fvector2	ep = m_segment_to_outline->End();

	// draw outline
	DrawOutline( attr , Winding_PathFillRule , GetStrokePaint( attr ) , transform2 );
	return ep;
}
//=================================================================================================
void cb_call StrokeArc
		(
		const fvector2&	sp , 
		float			rx , 
		float			ry , 
		float			x_axis_rot , 
		bool			large , 
		bool			sweep , 
		const fvector2&	tp , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	Attr&	attr	= m_stack.GetAttr();
	m_outline->Reset();
	m_segment_to_outline->Begin( (iOutline)m_outline , (iOutlineGen)GetPen( attr , GetSampleScale( transform2 ) ) );
	m_segment_to_outline->Move( sp , transform1 , attr.m_stroke_close );
	m_segment_to_outline->Arc( rx , ry , x_axis_rot , large , sweep , tp );
	m_segment_to_outline->End();
	DrawOutline( attr , Winding_PathFillRule , GetStrokePaint( attr ) , transform2 );
}
//=================================================================================================
void cb_call StrokeCircle
		(
		const fvector2&	cp , 
		float			rx , 
		float			ry , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	rx	= max( rx , 0.00001f );
	bool	close = GetStrokeClose();
	SetStrokeClose( true );
	StrokeArc( fvector2( cp.x - rx , cp.y ) , cp , ry / rx , PI_2_f , transform1 , transform2 );
	SetStrokeClose( close );
}
//=================================================================================================
void cb_call StrokeRect
		(
		const frect&	rect , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	fvector2	pnt[4] = 
			{ 
			fvector2( rect.xmin , rect.ymin ) , 
			fvector2( rect.xmax , rect.ymin ) , 
			fvector2( rect.xmax , rect.ymax ) , 
			fvector2( rect.xmin , rect.ymax )
			};
	StrokeLine( 4 , pnt , transform1 , transform2 );
}
//=================================================================================================
void cb_call StrokePath
		(
		IPathLogicInfo*	path , 
		const faffine&	transform1 , 
		const faffine&	transform2
		)
{
	if( path == 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();
	irect		area	= GetArea();

	// pen
	iPen_gp&	pen		= GetPen( attr , GetSampleScale( transform2 ) );

	// edgemap
	m_path_to_edgemap.ToEdgemap
		(
		(iEdgemapOutline)m_edgemap , 
		path , 
		transform1 , 
		(iOutlineGen)pen , 
		Winding_PathFillRule , 
		transform2 , 
		area , 
		attr.m_antialias
		);
	// clip
	iEdgemapOutline	clip = GetClip( attr );
	if( clip == true )
		m_edgemap->Operate( And_PathOperator , clip );

	// paint
	m_render->SetBlender( GetBlender( attr ) );
	m_render->SetPaint( (iPaint)GetStrokePaint( attr ) );
	m_render->Render( (iSurfaceDest)m_surface_view , (iEdgemapOutlineInfo)m_edgemap , &area , 1 , attr.m_alpha );
}
//=================================================================================================
void cb_call DrawPolygon
		(
		int				vertexnum , 
		const fvector2	vertex[] , 
		const fvector2	uv[] , 
		const faffine&	transform , 
		iTexture&		source
		)
{
	Attr&	attr	= m_stack.GetAttr();
	iBlender&	blender	= GetBlender( attr );
	DrawPolygon_blender( attr , vertexnum , vertex , uv , transform , source , blender );
}
//=================================================================================================
void cb_call BitBlt
		(
		const fvector2&		dpos , 
		iTexture&			source , 
		const fvector2&		spos
		)
{
	Attr&	attr	= m_stack.GetAttr();
	iBlender&	blender	= GetBlender( attr );
	BitBlt_blender( attr , dpos , source , spos , blender );
}
//=================================================================================================
void cb_call StretchBlt
		(
		int				pntnum , 
		const fvector2*	t_pnt , 
		const fvector2*	s_pnt , 
		const faffine&	t_trans , 
		const faffine&	s_trans , 
		iTexture&	source
		)
{
	Attr&	attr	= m_stack.GetAttr();
	fsize		ss		= source->TextureSize();
	faffine		s_to_uv	= faffine::GetScale( 1.0f / ss.width , 1.0f / ss.height ) *  s_trans;
	m_pt1_work.Resize( pntnum * 16 );
	m_uv1_work.Resize( pntnum * 16 );
	m_pt2_work.Resize( pntnum * 16 );
	m_uv2_work.Resize( pntnum * 16 );
	
	int		i;
	for( i = 0 ; i < pntnum ; i++ )
	{
		m_pt1_work[i]	= t_pnt[i];
		m_uv1_work[i]	= s_to_uv.Transform( s_pnt[i] );
	}
	int	pntnum1	= pntnum;
	int	pntnum2;
	
	pntnum2 = Clip::ClipUV<Clip::Min,0>( m_pt2_work.GetPtr() , m_uv2_work.GetPtr() , pntnum1 , m_pt1_work.GetConstPtr() , m_uv1_work.GetConstPtr() , 0.0f );
	pntnum1 = Clip::ClipUV<Clip::Max,0>( m_pt1_work.GetPtr() , m_uv1_work.GetPtr() , pntnum2 , m_pt2_work.GetConstPtr() , m_uv2_work.GetConstPtr() , 1.0f );
	pntnum2 = Clip::ClipUV<Clip::Min,1>( m_pt2_work.GetPtr() , m_uv2_work.GetPtr() , pntnum1 , m_pt1_work.GetConstPtr() , m_uv1_work.GetConstPtr() , 0.0f );
	pntnum1 = Clip::ClipUV<Clip::Max,1>( m_pt1_work.GetPtr() , m_uv1_work.GetPtr() , pntnum2 , m_pt2_work.GetConstPtr() , m_uv2_work.GetConstPtr() , 1.0f );

	DrawPolygon( pntnum1 , m_pt1_work.GetConstPtr() , m_uv1_work.GetConstPtr() , t_trans , source );
}
//=================================================================================================
void cb_call SetFont
		(
		iFont_gp&		font
		)
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_font		= font;
	attr.m_font_f	= false;
}
//=================================================================================================
void cb_call ReleaseFont()
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_font.release();
	attr.m_font_f	= false;
}
//=================================================================================================
void cb_call SetTextAlign
		(
		TextAlignHorz_gp	align_h , 
		TextAlignVert_gp	align_v
		)
{
	Attr&	attr	= m_stack.GetAttr();
	attr.m_align_h	= align_h;
	attr.m_align_v	= align_v;
}
//=================================================================================================
void cb_call GetTextAlign
		(
		TextAlignHorz_gp*	align_h , 
		TextAlignVert_gp*	align_v
		)
{
	Attr&	attr	= m_stack.GetAttr();
	store( align_h , attr.m_align_h );
	store( align_v , attr.m_align_v );
}
//=================================================================================================
frect cb_call GetTextRect
		(
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos
		)
{
	Attr&		attr	= m_stack.GetAttr();
	iFont_gp&	font	= GetFont( attr );

	iSurfaceDest	dest	= (iSurfaceDest)m_surface_view;
	fsize			size	= SetTextline( m_text_line , dest , font , str , length );

	fvector2		p	= pos;
	if( attr.m_align_v == Center_TextAlignVert_gp )
		p.y -= size.height / 2.0f;
	else if( attr.m_align_v == Bottom_TextAlignVert_gp )
		p.y -= size.height;

	if( attr.m_align_h == Center_TextAlignHorz_gp )
		p.x -= size.width / 2.0f;
	else if( attr.m_align_h == Right_TextAlignHorz_gp )
		p.x -= size.width;

	return frect( p , size );
}
//=================================================================================================
void cb_call DrawText
		(
		const wchar_t*	str , 
		int				length , 
		const fvector2&	pos , 
		const rgba&		color , 
		const frect*	clip
		)
{
	if( length <= 0 )
		return;
	Attr&	attr	= m_stack.GetAttr();
	iFont_gp&	font	= GetFont( attr );

	// not rotate
	if( attr.m_clip == false  )
		DrawTextD( attr , str , length ,  pos , color , clip , font );
	else
		DrawTextD_clip( attr , str , length , pos , color , clip , font );
}
// public functions
public:
//=================================================================================================
Graphics_bs() : 
		m_work( Expand_ArrayCashType , 128 ) , 
		m_pt1_work( Expand_ArrayCashType , 128 ) , 
		m_uv1_work( Expand_ArrayCashType , 128 ) , 
		m_pt2_work( Expand_ArrayCashType , 128 ) , 
		m_uv2_work( Expand_ArrayCashType , 128 ) , 
		m_text_line( Expand_ArrayCashType , 128 ) , 
		m_source_interpolate( Linear_SourceInterpolateType_gp ) , 
		m_source_wraptype( Repeat_Wraptype )
{
}
};

/**************************************************************************************************
"Graphics_gp" class 
**************************************************************************************************/
class Graphics_gp : 
		virtual public object_base , 
		public Graphics_bs
{
// query
	query_begin();
	iface_hook( IGraphics_gp , IGraphics_gp_IID )
	iface_hook( IPaint , IPaint_IID )
	iface_hook( ITexture , ITexture_IID )
	query_end( object_base );
};
//=================================================================================================
//!	create graphics
//!	@retval			---
//-------------------------------------------------------------------------------------------------
cb_inline
iGraphics_gp CreateGraphics()
{
	return (iGraphics_gp)instance<Graphics_gp>();
}

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

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

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
