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

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

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

namespace icubic
{

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

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

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

/**************************************************************************************************
"FontAlphaCash" class 
**************************************************************************************************/
class FontAlphaCash
{
// member class
public:
	class FontAlphaData
	{
	public:
		iSurfaceSource		m_alphamap;
		ivector2			m_basepos;
		FontAlphaData**		m_prev;
		FontAlphaData*		m_next;
		wchar_t				m_ch;
		FontAlphaData() : m_prev( 0 ) , m_next( 0 ) , m_ch( 0 ){}
		FontAlphaData
				(
				wchar_t			ch , 
				iSurfaceSource&	alphamap , 
				ivector2&		basepos
				) : m_ch( ch ) , m_alphamap( alphamap ) , m_basepos( basepos ) , m_prev( 0 ) , m_next( 0 ){}
	};
	typedef FontAlphaData*	FontAlphaDataPtr;
// variable member
private:
	FontAlphaDataPtr*	m_list[256];
	FontAlphaData*		m_first;
	FontAlphaData*		m_last;
	int					m_cashbyte;
	const int			m_cashbyte_max;

// private functions
private:
//=================================================================================================
void CompactCash()
{
	if( m_cashbyte <= m_cashbyte_max )
		return;
	if( m_first == 0 )
		return;
	FontAlphaData*	data = m_first;
	m_first	= data->m_next;
	if( data->m_next == 0 )
		m_last	= 0;
	else
		data->m_next->m_prev = &m_first;
	if( data->m_alphamap == true )
	{
		isize	ss = data->m_alphamap->GetSourceSize();
		m_cashbyte -= ss.width * ss.height;
	}
	m_list[ data->m_ch >> 8 ][ data->m_ch & 0xFF ] = 0;
	delete data;
}
// public functions
public:
//=================================================================================================
FontAlphaCash
		(
		int		cashbyte_max = 512 * 1024
		) : 
		m_first( 0 ) , 
		m_last( 0 ) , 
		m_cashbyte( 0 ) , 
		m_cashbyte_max( cashbyte_max )
{
	MemoryZero( m_list , sizeof( m_list ) );
}
//=================================================================================================
~FontAlphaCash()
{
	Reset();
}
//=================================================================================================
void Reset()
{
	FontAlphaData*	p = m_first;
	while( p != 0 )
	{
		FontAlphaData*	n = p->m_next;
		delete p;
		p	= n;
	}
	int		off , num = _countof( m_list );
	for( off = 0 ; off < num ; off++ )
	{
		if( m_list[off] != 0 )
			delete[] m_list[off];
	}
	MemoryZero( m_list , sizeof( m_list ) );
	m_first		= 0;
	m_last		= 0;
	m_cashbyte	= 0;
}
//=================================================================================================
FontAlphaData* GetFontAlphaData
		(
		wchar_t		ch
		)
{
	if( ch > 0xFFFF )
		return 0;
	int		hi	= ch >> 8;
	int		low	= ch & 0xFF;
	if( m_list[hi] == 0 )
		return 0;
	FontAlphaData*	data = m_list[hi][low];
	if( data == 0 )
		return 0;
	if( data == m_last )
		return data;

	*data->m_prev			= data->m_next;
	data->m_next->m_prev	= data->m_prev;
	m_last->m_next			= data;
	data->m_prev			= &m_last->m_next;
	data->m_next			= 0;
	m_last					= data;
	return data;
}
//=================================================================================================
FontAlphaData* CreateFontAlphaData
		(
		wchar_t			ch , 
		iSurfaceSource&	alphamap , 
		ivector2&		basepos
		)
{
	if( ch > 0xFFFF )
		return 0;
	int		hi	= ch >> 8;
	int		low	= ch & 0xFF;
	if( m_list[hi] == 0 )
	{
		m_list[hi]	= new FontAlphaDataPtr[256];
		MemoryZero( m_list[hi] , sizeof( FontAlphaDataPtr ) * 256 );
	}
	if( m_list[hi][low] != 0 )
		return m_list[hi][low];
	
	// compact cash
	if( alphamap == true )
	{
		isize	ss	= alphamap->GetSourceSize();
		m_cashbyte += ss.width * ss.height;
		CompactCash();
	}
	// add cash	
	FontAlphaData*	data = new FontAlphaData( ch , alphamap , basepos );
	if( m_last == 0 )
	{
		data->m_prev	= &m_first;
		m_first	= data;
		m_last	= data;
	}
	else
	{
		data->m_prev	= &m_last->m_next;
		m_last->m_next	= data;
		m_last			= data;
	}
	m_list[hi][low]	= data;
	return data;
}
};

#ifdef cb_windows
/**************************************************************************************************
"Font" class 
**************************************************************************************************/
class Font_bs : 
	public IFont_win
{
// variable member
private:
	Array< uint8 >						m_glyphbuf;
	Array<KERNINGPAIR>					m_kerningbuf;
	Array<float>						m_kerning;
	float								m_widthlist[256];

	// info
	float								m_ascent;
	float								m_descent;
	float								m_width_ave;

	// cash
	FontAlphaCash::FontAlphaData		m_alphadata;
	FontAlphaCash						m_cash;

	// draw
	instance<Path>					m_path;
	instance<PathToOutline>			m_to_outline;
	instance<OutlineGenPaint>		m_gen;
	instance<Outline>				m_outline;
	instance<EdgemapCell>			m_map;
	instance<PaintSolidColor>		m_source;
	instance<BlenderOnezero>			m_blender;
	instance<RendererCell>			m_renderer;

	const float							m_samplescale;

protected:
	// char info
	HDC									m_hdc;
	HFONT								m_font;
	LOGFONTW							m_logfont;
	FontQuarity							m_quarity;
	
// protected functions
protected:
//=================================================================================================
FIXED ToFix
		(
		float			v
		)
{
	FIXED	r;
	*(LONG*)&r = ( LONG )( v * 65536.0 );
	return r;
}
//=================================================================================================
fvector2 FixToPoint 
		(
		POINTFX&		pfx		
		)const
{
	return fvector2( ( float )( ( double )( *( LONG* )&pfx.x ) / 65536.0 ) , -( float )( ( double )( *( LONG* )&pfx.y ) / 65536.0 ) );
}
//=================================================================================================
void GlyphBezierQ
		(
		iPath&			path , 
		TTPOLYCURVE		*pcurve
		)
{
	cb_assert( pcurve->cpfx > 1 , L"TTPOLYCURVE:BezierQ invalid point num." );

	int			pntoff , pntnum	= pcurve->cpfx;
	fvector2	before	= FixToPoint( pcurve->apfx[ 0 ] );
	for( pntoff = 1 ; pntoff < pntnum - 1 ; pntoff++ )
	{
		fvector2	now		= FixToPoint( pcurve->apfx[ pntoff ] );
		fvector2	center	= ( now + before ) / 2.0f;
		
		path->BezierQ( before , center );
		
		//update
		before	= now;
	}
	// last
	fvector2	now = FixToPoint( pcurve->apfx[ pntoff ] );
	{
		path->BezierQ( before , now );
	}
}
//=================================================================================================
void GlyphBezierC
		(
		iPath&			path , 
		TTPOLYCURVE		*pcurve
		)
{
	cb_assert( pcurve->cpfx % 3 == 0 , L"TTPOLYCURVE::cpfx is not multiple of 3\n" );
	
	int		pntoff , pntnum = pcurve->cpfx;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff+= 3 )
	{
		path->BezierC
				(
				FixToPoint( pcurve->apfx[ pntoff + 0 ] ), 
				FixToPoint( pcurve->apfx[ pntoff + 1 ] ), 
				FixToPoint( pcurve->apfx[ pntoff + 2 ] )
				);
	}
}
//=================================================================================================
void GlyphLine
		(
		iPath&			path , 
		TTPOLYCURVE		*pcurve
		)
{
	int		pntoff , pntnum	= pcurve->cpfx;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		path->Line( FixToPoint( pcurve->apfx[ pntoff ] ) );
	}
}
//=================================================================================================
FontAlphaCash::FontAlphaData* GetFontAlphaData
		(
		wchar_t		ch
		)
{
	FontAlphaCash::FontAlphaData* data = m_cash.GetFontAlphaData( ch );
	if( data != 0 )
		return data;
		
	m_path->Reset();
	if( false == CreateCharPath( (iPath)m_path , ch , faffine() ) )
	{
		m_cash.CreateFontAlphaData( ch , iSurfaceSource() , ivector2() );
		return 0;
	}
	m_outline->Reset();
	m_gen->SetSampleScale( m_samplescale );
	m_to_outline->ToOutline( (iOutline)m_outline , *m_path->GetPathInfo() , (iOutlineGen)m_gen , faffine() );

	OutlineInfo		oi;
	if( false == m_outline->GetOutlineInfo( &oi ) )
		return 0;
	irect		ibb( ( int )floorf( oi.m_boundbox.xmin ) , ( int )floorf( oi.m_boundbox.ymin ) , ( int )ceilf( oi.m_boundbox.xmax ) , ( int )ceilf( oi.m_boundbox.ymax ) );
	iSurface	sm = CreateSurface( ibb.Size() , a_pixelformat );

	// clear
	{
		int		pitch;
		irect	aa		= sm->GetDestAvailableArea();
		uint8*	p		= ( uint8* )sm->GetDestPixelPtr( &pitch );
		p				+= aa.ymin * pitch + aa.xmin * get_pixel_byte( a_pixelformat );
		int		dw	= aa.Size().width;
		int		dh	= aa.Size().height;
		int		dy;
		for( dy = 0 ; dy < dh ; dy++ )
		{
			MemoryZero( p , dw * get_pixel_byte( a_pixelformat ) );
			p += pitch;
		}		
	}
	
	faffine		aff	= faffine::GetMove( fvector2( -ibb.xmin , -ibb.ymin ) );
	m_map->SetOutline( (iOutline)m_outline , aff );
	m_source->SetColor( rgba( 0 , 0 , 0 , 255 ) );
	m_renderer->Render( ( iSurfaceDest )sm , (iEdgemapCellInfo)m_map , 0 , 255 );

	ivector2	basepos( ibb.xmin , ibb.ymin );
	data	= m_cash.CreateFontAlphaData( ch , (iSurfaceSource)sm , basepos );
	if( data == 0 )
	{
		m_alphadata.m_alphamap	= (iSurfaceSource)sm;
		m_alphadata.m_basepos	= basepos;
		data	= &m_alphadata;
	}
	return data;
}
//=================================================================================================
void cb_call DrawCharacter_icubic
		(
		iSurfaceDest&		surface , 
		wchar_t				ch , 
		const ivector2&		pos , 
		const rgba&			color , 
		const irect*		cp
		)
{
	FontAlphaCash::FontAlphaData*	ad = GetFontAlphaData( ch );
	if( ad == 0 || ad->m_alphamap == false )
		return;
	SurfaceBlt( surface , pos + ad->m_basepos , ad->m_alphamap , color , 255 , Overlap_BltBlendType , cp );
}
//=================================================================================================
void DrawString_icubic
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const irect*		cp
		)
{
	irect		area = surface->GetDestAvailableArea();
	if( cp != 0 )
		area	= area.And( *cp );

	ivector2	ipos( ( int )floorf( pos.x + 0.5f ) , ( int )floorf( pos.y + 0.5f ) );
	int			h	= ( int )ceilf( GetCharHeight() );
	if( ipos.y + h < area.ymin )
		return;	
	if( ipos.y - h > area.ymax )
		return;	

	ivector2	spos = ipos;
	int			off;
	for( off = 0 ; off < length ; off++ )
	{
		if( area.xmax < spos.x )
			return;
		wchar_t	ch	= str[ off ];
		float	fw;
		GetCharWidth( &ch , 1 , &fw );
		int		w	= ( int )floorf( fw + 0.5f );
		if( area.xmin < spos.x + w )
		{
			DrawCharacter_icubic( surface , ch , spos , color , cp );
		}
		spos.x += w;
	}
}
//=================================================================================================
fsize GetStringSize_icubic
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length
		)
{
	int		h	= ( int )ceilf( GetCharHeight() );
	int		off , w = 0;		
	for( off = 0 ; off < length ; off++ )
	{
		float	fw;
		GetCharWidth( &str[off] , 1 , &fw );
		w	+= ( int )floorf( fw + 0.5f );
	}
	return fsize( (float)w , (float)h );
}
//=================================================================================================
void DrawString_gdi
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const irect*		cp
		)
{
	pixelformat		destformat = surface->GetDestFormat();
	if( destformat != rgb_pixelformat )
	{
		DrawString_icubic( surface , str , length , pos , color , cp );
		return;
	}
	irect	origin , update;
	HDC		hdc	= surface->GetDestHDC( &origin , &update );
	if( hdc == NULL )
	{
		DrawString_icubic( surface , str , length , pos , color , cp );
		return;
	}
	if( update.IsExist() == false )
		return;
	
	irect	r	= ( cp == 0 ) ? update : update.And( *cp );
	r = r.Move( ivector2( origin.xmin , origin.ymin ) );
	RECT	cr = { r.xmin , r.ymin , r.xmax , r.ymax };
	
	::SetBkMode( hdc , TRANSPARENT );
	::SetTextAlign( hdc , TA_LEFT | TA_BASELINE );
	::SetTextColor( hdc , RGB( color.r , color.g , color.b ) );
	HFONT	oldfont = (HFONT)::SelectObject( hdc , m_font );
	::ExtTextOutW( hdc , (int)( pos.x + origin.xmin ) , (int)(pos.y + origin.ymin) , ETO_CLIPPED , &cr , str , length , NULL );
	::SelectObject( hdc , oldfont );
/*		
	Gdiplus::Graphics		graphics( hdc );
	Gdiplus::Font			font( hdc , m_font );
	Gdiplus::SolidBrush		brush( Gdiplus::Color( color.a , color.r , color.g , color.b ) );
	graphics.SetTextRenderingHint( m_renderhint );

	irect	r	= ( cp == 0 ) ? update : update.And( *cp );
	r.Move( ivector2( origin.xmin , origin.ymin ) );
	graphics.SetClip( Gdiplus::Rect( r.xmin , r.ymin , r.Width() , r.Height() ) );

	Gdiplus::RectF		box;
	graphics.MeasureString( str , 1 , &font , Gdiplus::PointF( 0.0f , 0.0f ) , &box );
	float	dx = ( box.Width - GetCharWidth( str[0] ) ) / 2.0f;

	graphics.DrawString( str , length , &font , Gdiplus::PointF( pos.x + (float)origin.xmin - dx , pos.y + (float)origin.ymin - GetAscent() ) , &brush );
*/
}
//=================================================================================================
fsize GetStringSize_gdi
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length
		)
{
	pixelformat		destformat = surface->GetDestFormat();
	if( destformat != rgb_pixelformat )
		return GetStringSize_icubic( surface , str , length );
	irect	origin , update;
	HDC		hdc	= surface->GetDestHDC( &origin , &update );
	if( hdc == NULL )
		return GetStringSize_icubic( surface , str , length );

	HFONT	oldfont = (HFONT)::SelectObject( hdc , m_font );
	SIZE	size = { 0 , 0 };
	::GetTextExtentPoint32W( hdc, str , length , &size );
	::SelectObject( hdc , oldfont );

	return fsize( (float)size.cx , (float)size.cy );
/*
	Gdiplus::Graphics		graphics( hdc );
	Gdiplus::Font			font( hdc , m_font );
	graphics.SetTextRenderingHint( m_renderhint );

	Gdiplus::RectF		box;
	graphics.MeasureString( str , 1 , &font , Gdiplus::PointF( 0.0f , 0.0f ) , &box );
	float	dx = ( box.Width - GetCharWidth( str[0] ) ) / 2.0f;

	graphics.MeasureString( str , length , &font , Gdiplus::PointF( 0.0f , 0.0f ) , &box );
	return fsize( box.Width - dx , GetAscent() + GetDescent() );
*/
}
//=================================================================================================
void Destroy()
{
	if( m_font != NULL )
	{
		cb_verify( TRUE == ::DeleteObject( m_font ) );
		m_font	= NULL;
	}
	m_ascent	= 0.0f;
	m_descent	= 0.0f;
	m_width_ave	= 0.0f;
	m_cash.Reset();
}
//=================================================================================================
bool UpdateFont()
{
	if( m_font != NULL )
		return true;

	m_font	= ::CreateFontIndirectW( &m_logfont );
	if( m_font == NULL )
		return false;
	::SelectObject( m_hdc , m_font );

	// info
	TEXTMETRIC	metric;
	cb_verify( TRUE == ::GetTextMetrics( m_hdc , &metric ) );
	m_ascent	= ( float )metric.tmAscent;
	m_descent	= ( float )metric.tmDescent;
	m_width_ave	= ( float )metric.tmAveCharWidth;

	// width
	ABCFLOAT	abc[_countof(m_widthlist)];
	cb_verify( TRUE == ::GetCharABCWidthsFloatW( m_hdc , 0 , _countof(m_widthlist) - 1 , abc ) );
	int			off;
	for( off = 0 ; off < _countof(m_widthlist) ; off++ )
	{
		m_widthlist[ off ] = abc[off].abcfA + abc[off].abcfB + abc[off].abcfC;
	}
	return true;
}
//=================================================================================================
int ToFontWidth
		(
		float	width
		)const
{
	return ( width == 0.0f ) ? 0 : ( int )max( floorf( width + 0.5f ) , 1.0f );
}
//=================================================================================================
int ToFontHeight
		(
		float	height
		)const
{
	return -( int )max( 1.0f , floorf( height + 0.5f ) );
}
// "IFont" interface functions
public:
//=================================================================================================
bool cb_call Create
		(
		const fsize&		size , 
		const wstring&		facename , 
		FontCharsetType		charset , 
		int					weight	= 400 , 
		bool				italic	= false , 
		FontQuarity			quality	= IFont::Default
		)
{
	Destroy();

	// quality
	BYTE	q	= DEFAULT_QUALITY;
	if( quality == Antialias )
		q = ANTIALIASED_QUALITY;
	else if( quality == ClearType )
		q = CLEARTYPE_QUALITY;
	else if( quality == Alias )
		q = NONANTIALIASED_QUALITY;
	m_quarity	= quality;
	
	// logfont
	MemoryZero( &m_logfont , sizeof( m_logfont ) );
	m_logfont.lfClipPrecision		= CLIP_DEFAULT_PRECIS;
	m_logfont.lfOutPrecision		= OUT_OUTLINE_PRECIS;
	m_logfont.lfPitchAndFamily		= DEFAULT_PITCH | FF_DONTCARE;
	m_logfont.lfQuality				= q;
	m_logfont.lfWeight				= ( weight < 0 ) ? 0 : ( ( weight > 1000 ) ? 1000 : weight );//( bold == true ) ? FW_BOLD : FW_NORMAL;
	m_logfont.lfItalic				= (italic == true ) ? TRUE : FALSE;
	m_logfont.lfWidth				= ToFontWidth( size.width );
	m_logfont.lfHeight				= ToFontHeight( size.height );
	::wcscpy_s( m_logfont.lfFaceName , _countof( m_logfont.lfFaceName ) , facename.c_str() );
	FontCharsetTypeTolfCharset( charset , &m_logfont.lfCharSet );

	if( false == UpdateFont() )
	{
		Destroy();
		return false;
	}
	return true;
}
//=================================================================================================
void cb_call SetFontSize
		(
		const fsize&	size
		)
{
	isize	s( ToFontWidth( size.width ) , ToFontHeight( size.height ) );
	if( m_logfont.lfWidth == s.width && m_logfont.lfHeight == s.height )
		return;

	Destroy();
	m_logfont.lfWidth	= s.width;
	m_logfont.lfHeight	= s.height;
	UpdateFont();
}
//=================================================================================================
float cb_call GetAscent()
{
	return m_ascent;
}
//=================================================================================================
float cb_call GetDescent()
{
	return m_descent;
}
//=================================================================================================
float cb_call GetCharWidthAve()
{
	return m_width_ave;
}
//=================================================================================================
float cb_call GetCharWidthAve
		(
		float	height
		)
{
	LOGFONTW	logfont = m_logfont;
	logfont.lfHeight	= ToFontHeight( height );
	logfont.lfWidth		= 0;
	HFONT	font		= ::CreateFontIndirectW( &logfont );
	if( font == NULL )
		return 0.0f;
	HFONT	oldfont = (HFONT)::SelectObject( m_hdc , font );
	TEXTMETRIC	metric;
	cb_verify( TRUE == ::GetTextMetrics( m_hdc , &metric ) );
	::SelectObject( m_hdc , oldfont );
	cb_verify( TRUE == ::DeleteObject( font ) );
	
	return ( float )metric.tmAveCharWidth;
}
//=================================================================================================
void cb_call GetCharWidth
		(
		const wchar_t*	str , 
		int32			len , 
		float*			w		//!< [in] it need allocate len
		)
{
	if( len <= 0 )
		return;
	int		off;
	for( off = 0 ; off < len ; off++ )
	{
		if( str[off] < _countof( m_widthlist ) )
			*w	= m_widthlist[str[off]];
		else
		{
			ABCFLOAT	abc;
			cb_verify( TRUE == ::GetCharABCWidthsFloatW( m_hdc , str[off] , str[off] , &abc ) );
			*w	= abc.abcfA + abc.abcfB + abc.abcfC;
		}
		w++;
	}
}
//=================================================================================================
float cb_call GetCharHeight()
{
	return m_ascent + m_descent;
}
//=================================================================================================
void cb_call GetKerningPair
		(
		const wchar_t*	str , 
		int32			len , 
		float*			kerning		//!< [in] it need allocate len - 1
		)
{
	if( len <= 1 )
		return;
	m_kerningbuf.Resize( len - 1 );
	KERNINGPAIR*	p = m_kerningbuf.GetPtr();
	int		off , num = len - 1;
	for( off = 0 ; off < len - 1 ; off++ )
	{
		p->wFirst		= str[ off ];
		p->wSecond		= str[ off + 1 ];
		p->iKernAmount	= 0;
		p++;
	}
	if( len - 1 == ::GetKerningPairsW( m_hdc , len - 1 , m_kerningbuf.GetPtr() ) )
	{
		p = m_kerningbuf.GetPtr();
		for( off = 0 ; off < len - 1 ; off++ )
		{
			kerning[ off ] = (float)p->iKernAmount;
			p++;
		}
	}
	else
	{
		for( off = 0 ; off < len - 1 ; off++ )
			kerning[ off ] = 0.0f;
	}
}
//=================================================================================================
bool cb_call CreateCharPath
		(
		iPath&			path , 
		wchar_t			ch , 
		const faffine&	transform
		)
{
	//get m_GlyphBuf
	float	dx = 0.0f;
	{
		GLYPHMETRICS	gm;
		MAT2			mt;
		MemoryZero( &gm, sizeof(gm) );
		MemoryZero( &mt, sizeof(mt) );
		mt.eM11.value = 1;
		mt.eM22.value = 1;
	
		LONG	size = ::GetGlyphOutlineW( m_hdc , ch, GGO_NATIVE , &gm, 0, NULL, &mt );
		if( size < 0 )
			return false;
		m_glyphbuf.Resize( size );
		if( 0 >= ::GetGlyphOutlineW( m_hdc , ch , GGO_NATIVE , &gm , size , m_glyphbuf.GetPtr() , &mt ) )
			return false;
		dx	= (float)gm.gmptGlyphOrigin.x;
	}
//	faffine		aff;
//	aff.m[0][0]	= 1.0f;//m_rate;
//	aff.m[1][1]	= 1.0f;//m_rate;
//	aff.m[0][2]	= -dx;
	
//	path->Reset();
//	path->SetAffine( aff );
	
	//get path
	uint8		*pdata	= m_glyphbuf.GetPtr();
	uint8		*pend	= pdata + m_glyphbuf.GetDatanum();
	while( pdata < pend )
	{
		//TTPOLYGONHEADER
		TTPOLYGONHEADER		*tth = ( TTPOLYGONHEADER* )pdata;
		cb_assert( tth->dwType == TT_POLYGON_TYPE , L"TTPOLYGONHEADER::dwType is not TT_POLYGON_TYPE\n" );
		
		//start
		path->Move( FixToPoint( tth->pfxStart ) , transform , true );
		
		//TPOLYCURVE
		TTPOLYCURVE			*ttc				= ( TTPOLYCURVE* )( pdata + sizeof( TTPOLYGONHEADER ) );
		TTPOLYCURVE			*ttc_end			= ( TTPOLYCURVE* )( pdata + tth->cb );

		while( ttc < ttc_end )
		{
			if ( ttc->wType == TT_PRIM_QSPLINE ) 
			{
				GlyphBezierQ( path , ttc );
			}
			else if( ttc->wType == TT_PRIM_CSPLINE )
			{
				GlyphBezierC( path , ttc );
			}
			else if( ttc->wType == TT_PRIM_LINE )
			{
				GlyphLine( path , ttc );
			}
			else
			{
				cb_assert( false , L"unknown type in TTPOLYCURVE::wType" );
			}
			//update
			ttc	= ( TTPOLYCURVE* )( ( ( uint8* )ttc ) + sizeof( TTPOLYCURVE ) + ( ttc->cpfx - 1 ) * sizeof( POINTFX ) );
		}
		//update
		pdata	= ( uint8* )ttc;
	}
	return true;
}
//=================================================================================================
void cb_call DrawString
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length , 
		const fvector2&		pos , 
		const rgba&			color , 
		const frect*		cp
		)
{
	irect	icp = ( cp == 0 ) ? irect() : irect( *cp );
	if( m_quarity == iCubic )
		DrawString_icubic( surface , str , length , pos , color , cp == 0 ? 0 : &icp );
	else
		DrawString_gdi( surface , str , length , pos , color , cp == 0 ? 0 : &icp );
}
//=================================================================================================
fsize cb_call GetStringSize
		(
		iSurfaceDest&		surface , 
		const wchar_t*		str , 
		int					length
		)
{
	if( m_quarity == iCubic )
		return GetStringSize_icubic( surface , str , length );
	else
		return GetStringSize_gdi( surface , str , length );
}
//=================================================================================================
void cb_call CreateStringPath
		(
		iPath&			path , 
		const wchar_t*	str , 
		int				length , 
		const faffine&	transform
		)
{
	fvector2	pos( 0.0f , 0.0f );
	int			off;
	for( off = 0 ; off < length ; off++ )
	{
		CreateCharPath( path , str[ off ] , transform * faffine::GetMove( pos ) );
		float	w;
		GetCharWidth( &str[ off ] , 1 , &w );
		pos.x += floorf( w + 0.5f );
	}
}
// "IFont_win" interface functions
public:
//=================================================================================================
bool cb_call Create
		(
		const LOGFONTW&		logfont
		)
{
	Destroy();

	// quality
	m_quarity	= Default;
	if( logfont.lfQuality == ANTIALIASED_QUALITY )
		m_quarity = Antialias;
	else if( logfont.lfQuality == CLEARTYPE_QUALITY )
		m_quarity = ClearType;
	else if( logfont.lfQuality == NONANTIALIASED_QUALITY )
		m_quarity = Alias;
	m_logfont	= logfont;
	
	if( false == UpdateFont() )
	{
		Destroy();
		return false;
	}
	return true;
}
//=================================================================================================
LOGFONTW cb_call GetLOGFONT()
{
	return m_logfont;
}
//=================================================================================================
HFONT cb_call GetHFONT()
{
	return m_font;
}
// public functions
public:
//=================================================================================================
Font_bs() : 
		m_font( NULL ) , 
		m_hdc( NULL ) , 
		m_ascent( 0.0f ) , 
		m_descent( 0.0f ) , 
		m_glyphbuf( Expand_ArrayCashType , 128 ) , 
		m_kerningbuf( Expand_ArrayCashType , 128 ) , 
		m_kerning( Expand_ArrayCashType , 128 ) , 
		m_samplescale( 2.0f ) , 
		m_cash( 512 * 1024 ) , 
		m_quarity( Default )
{
	MemoryZero( &m_logfont , sizeof( m_logfont ) );
	HDC		hdc	= GetDC( NULL );
	m_hdc	= ::CreateCompatibleDC( hdc );
	ReleaseDC( NULL , hdc );
	::SetMapMode( m_hdc , MM_TEXT );

	m_renderer->SetPaint( (iPaint)m_source );
	m_renderer->SetBlender( (iBlender)m_blender );	
}
//=================================================================================================
virtual
~Font_bs()
{
	Destroy();
	if( m_hdc != NULL )
	{
		::ReleaseDC( NULL , m_hdc );
		m_hdc	= NULL;	
	}
}
//=================================================================================================
void SetGamma
		(
		iNormTable_i16&		gamma
		)
{
	m_cash.Reset();
	m_renderer->SetGamma( gamma );
}
};
#endif

/**************************************************************************************************
"Font" class 
**************************************************************************************************/
class Font : 
	virtual public object_base , 
	public Font_bs
{
// query
	query_begin();
	iface_hook( IFont , IFont_IID )
	iface_hook( IFont_win , IFont_win_IID )
	query_end( object_base );
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

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

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
