#include "Stdafx.h"
#include "Creator.h"
#include "Types.h"

#include <vector>

using namespace System::Runtime::InteropServices;

namespace Mix{ namespace Tool{ namespace Font{

////////////////////////////////////////////////////////////////////////////////////////////////////
// 構造体
////////////////////////////////////////////////////////////////////////////////////////////////////

//フォントファイルヘッダ( 32バイト )
struct FONTFILEHEADER
{
	unsigned int magicNumber;	//マジックナンバー
	unsigned int version;		//バージョン
};

//フォント効果列挙定数
enum FONT_EFFFECTS
{
	FE_ITALIC		= 0x00000001,	//斜体
	FE_BORDERING	= 0x00000002,	//縁取り
};

//フォントデータヘッダ( 24バイト )
struct FONTDATAHEADER
{
	unsigned int imgBuffSize;	//フォントのイメージを取得する際に必要なバッファサイズ
	wchar_t firstCode;			//フォント一覧の最初のコード
	wchar_t lastCode;			//フォント一覧の最後のコード
	unsigned int effects;		//効果
	unsigned int size;			//フォントのサイズ
	unsigned int weight;		//フォントの太さ
	unsigned int borderSize;	//縁取りのサイズ
};

//フォント情報( 16バイト )
struct FONTINFO
{
	unsigned int imgAddr;

	unsigned short cellWidth;
	unsigned short cellHeight;

	short x;
	short y;
	unsigned short width;
	unsigned short height;
};

//フォント作業用構造体
struct FONTWORK
{
	unsigned int pitch;
	unsigned short width;
	unsigned short height;

	unsigned int fatImgWidth;
	unsigned int imgSize;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// TextCollection
////////////////////////////////////////////////////////////////////////////////////////////////////

TextCollection::TextCollection( void ) :
m_List( gcnew System::Collections::Generic::List<System::String^>() )
{
}

int TextCollection::Count::get( void )
{
	return m_List->Count;
}

System::String^ TextCollection::default::get( int index )
{
	return m_List[index];
}

void TextCollection::Clear( void )
{
	m_List->Clear();
}

void TextCollection::Add( System::String^ fileName )
{
	if( m_List->Contains( fileName ) == true )
	{
		return;
	}

	m_List->Add( ( System::String^ )( fileName->Clone() ) );
}

void TextCollection::Remove( System::String^ fileName )
{
	m_List->Remove( fileName );
}

void TextCollection::Remove( int index )
{
	System::String^ textFileName = m_List[index];
	m_List->Remove( textFileName );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Creator
////////////////////////////////////////////////////////////////////////////////////////////////////

Creator::Creator( void ) :
m_Style( L"" ),
m_Size( static_cast<unsigned int>( Creator::DefSize ) ),
m_Weight( static_cast<unsigned int>( Creator::DefWeight ) ),
m_Effects( Mix::Tool::Font::Effects::None ),
m_BorderSize( 1 ),
m_Progress( nullptr ),
m_ChangeState( nullptr ),
m_FilePath( L"" ),
m_Texts( gcnew Mix::Tool::Font::TextCollection() )
{
}

Creator::~Creator( void )
{
	m_Style = L"";
	m_Size = Creator::DefSize;
	m_Weight = Creator::DefWeight;
	m_Progress = nullptr;
	m_ChangeState = nullptr;
	m_FilePath = L"";
	m_Texts->Clear();
}

System::String^ Creator::Style::get( void )
{
	return ( System::String^ )( m_Style->Clone() );
}

void Creator::Style::set( System::String^ value )
{
	m_Style = ( System::String^ )( value->Clone() );
}

int Creator::Size::get( void )
{
	return static_cast<int>( m_Size );
}

void Creator::Size::set( int value )
{
	m_Size = ( value >= 8 )? static_cast<unsigned int>( value ) : 8;
}

int Creator::Weight::get( void )
{
	return static_cast<int>( m_Weight );
}

void Creator::Weight::set( int value )
{
	if( value < FW_THIN )
	{
		m_Weight = FW_THIN;
	}
	else if( value > FW_HEAVY )
	{
		m_Weight = FW_HEAVY;
	}
	else
	{
		m_Weight = static_cast<unsigned int>( value );
	}
}

Mix::Tool::Font::Effects Creator::Effects::get( void )
{
	return m_Effects;
}

void Creator::Effects::set( Mix::Tool::Font::Effects value )
{
	m_Effects = value;
}

int Creator::BorderSize::get( void )
{
	return static_cast<int>( m_BorderSize );
}

void Creator::BorderSize::set( int value )
{
	m_BorderSize = static_cast<unsigned int>( max( 0, value ) );
}

bool Creator::IsBordering::get( void )
{
	if( ( ( m_Effects & Mix::Tool::Font::Effects::Bordering ) != Mix::Tool::Font::Effects::Bordering ) ||
		( m_BorderSize == 0 ) )
	{
		return false;
	}

	return true;
}

Mix::Tool::Font::TextCollection^ Creator::Texts::get( void )
{
	return m_Texts;
}

System::String^ Creator::FilePath::get( void )
{
	return ( System::String^ )( m_FilePath->Clone() );
}

void Creator::FilePath::set( System::String^ value )
{
	m_FilePath = ( System::String^ )( value->Clone() );
}

void Creator::Progress::set( Creator::DelegateProgress^ value )
{
	m_Progress = value;
}

void Creator::ChangeState::set( Creator::DelegateChangeState^ value )
{
	m_ChangeState = value;
}

void Creator::Run( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 定数
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int QUALITY_TABLE[] = { GGO_GRAY8_BITMAP, GGO_GRAY4_BITMAP, GGO_GRAY2_BITMAP, };
	const unsigned int QUALITY_COUNT = ( sizeof( QUALITY_TABLE ) / sizeof( unsigned int ) );

	unsigned int TRANSSHIFT_TABLE[] = { 6, 4, 2, };
	unsigned int TRANSSHIFT_COUNT = ( sizeof( TRANSSHIFT_TABLE ) / sizeof( unsigned int ) );

	unsigned int CALC_PITCHSHIFT = 2;

	const MAT2 FONTMAT = { { 0, 1, }, { 0, 0, }, { 0, 0, }, { 0, 1, }, };

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 変数
	////////////////////////////////////////////////////////////////////////////////////////////////////

	int fontSize;
	int borderSize;
	unsigned short borderSize2;

	HDC hDC;
	HFONT hFont;
	HGDIOBJ hOldFont;
	unsigned int charaBitTable[Creator::MAX_CHARACTER_BITTABLE];
	unsigned int quality;
	unsigned int transShift;
	unsigned int totalHeaderSize;
	unsigned int totalDataSize;
	unsigned int totalInfoSize;
	unsigned int glyphBuffSize;
	unsigned char* pGlyphBuffer;
	unsigned int fatImgBuffSize;
	unsigned char* pFatImgBuff;
	unsigned short* pImgBuff;
	TEXTMETRIC tm;
	GLYPHMETRICS gm;
	FONTFILEHEADER fileHeader;
	FONTDATAHEADER dataHeader;
	std::vector<Mix::Tool::Font::FONTINFO> infoList;
	std::vector<Mix::Tool::Font::FONTWORK> workList;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 作成の開始を通知
	////////////////////////////////////////////////////////////////////////////////////////////////////

	InvokeChangeState( Creator::State::Start );

	Mix::Tool::Logger::Clear();
	Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, L"作成を開始します" );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 基本情報の初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_Effects & Mix::Tool::Font::Effects::Bordering ) == Mix::Tool::Font::Effects::Bordering )
	{
		fontSize = static_cast<int>( m_Size ) - static_cast<int>( m_BorderSize ) * 2 - 1;
		borderSize = static_cast<int>( m_BorderSize );
	}
	else
	{
		fontSize = static_cast<int>( m_Size );
		borderSize = 0;
	}

	if( fontSize <= 0 )
	{
		InvokeChangeState( Creator::State::Error );
		return;
	}

	borderSize2 = static_cast<unsigned short>( borderSize ) * 2;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 出力する文字のビットテーブルを作成する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_Texts->m_List->Count > 0 )
	{
		if( CreateCharacterBitTableFromText( &( charaBitTable[0] ) ) == false )
		{
			InvokeChangeState( Creator::State::Error );
			return;
		}
	}
	else
	{
		CreateCharacterBitTableFromTable( &( charaBitTable[0] ) );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フォントを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		DWORD dwItalic = ( ( m_Effects & Mix::Tool::Font::Effects::Italic ) == Mix::Tool::Font::Effects::Italic );

		System::IntPtr stylePtr;
		const wchar_t* pStyle;

		hDC = ::CreateCompatibleDC( NULL );
		if( hDC == nullptr )
		{
			InvokeChangeState( Creator::State::Error );
			return;
		}

		stylePtr = Marshal::StringToHGlobalUni( m_Style );
		pStyle = static_cast<const wchar_t*>( stylePtr.ToPointer() );

		hFont = ::CreateFontW(	fontSize,
								0,
								0,
								0,
								m_Weight,
								dwItalic,
								FALSE,
								FALSE,
								SHIFTJIS_CHARSET,
								OUT_DEFAULT_PRECIS,
								CLIP_DEFAULT_PRECIS,
								PROOF_QUALITY,
								( FIXED_PITCH | FF_MODERN ),
								pStyle );

		Marshal::FreeHGlobal( stylePtr );

		if( hFont == nullptr )
		{
			::DeleteDC( hDC );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"論理フォントの作成に失敗 : CreateFont がエラーを返しました" );

			return;
		}

		hOldFont = ::SelectObject( hDC, hFont );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フォントの情報を収集
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		unsigned int i;

		::ZeroMemory( &tm, sizeof( tm ) );

		if( ::GetTextMetrics( hDC, &tm ) == FALSE )
		{
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"テキストメトリクスの取得に失敗 : GetTextMetrics がエラーを返しました" );

			return;
		}

		for( i = 0; i < QUALITY_COUNT; i++ )
		{
			::ZeroMemory( &gm, sizeof( gm ) );

			quality = QUALITY_TABLE[i];
			transShift = TRANSSHIFT_TABLE[i];

			if( ::GetGlyphOutline( hDC, L'A', quality, &gm, NULL, NULL, &FONTMAT ) != GDI_ERROR )
			{
				break;
			}
		}

		if( QUALITY_COUNT > i )
		{
			switch( quality )
			{
			case GGO_GRAY8_BITMAP:
				Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, L"グリフの品質[HIGH]" );
				break;
			case GGO_GRAY4_BITMAP:
				Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, L"グリフの品質[MIDDLE]" );
				break;
			case GGO_GRAY2_BITMAP:
				Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, L"グリフの品質[LOW]" );
				break;
			}
		}
		else
		{
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントの品質の取得に失敗 : GetGlyphOutline がエラーを返しました" );

			return;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フォントファイルヘッダの準備
	////////////////////////////////////////////////////////////////////////////////////////////////////

	::ZeroMemory( &fileHeader, sizeof( fileHeader ) );

	fileHeader.magicNumber = Creator::MAGICNUMBER;
	fileHeader.version = Creator::VERSION;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フォントデータヘッダの準備
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		bool bFindFirstCode = false;
		wchar_t code = 0;

		::ZeroMemory( &dataHeader, sizeof( dataHeader ) );

		if( ( m_Effects & Mix::Tool::Font::Effects::Italic ) == Mix::Tool::Font::Effects::Italic ) { dataHeader.effects |= FE_ITALIC; }
		if( ( m_Effects & Mix::Tool::Font::Effects::Bordering ) == Mix::Tool::Font::Effects::Bordering ) { dataHeader.effects |= FE_BORDERING; }

		dataHeader.size = m_Size;
		dataHeader.weight = m_Weight;
		dataHeader.borderSize = static_cast<unsigned int>( borderSize );

		//最初と最後のコードを取得
		for( code = 0; code < 0xFFFF; code++ )
		{
			unsigned int bitMask = ( 1 << code % 32 );

			if( ( charaBitTable[code >> 5] & bitMask ) != 0 )
			{
				if( bFindFirstCode == false )
				{
					dataHeader.firstCode = code;
					bFindFirstCode = true;
				}

				dataHeader.lastCode = code;
			}
		}

		if( bFindFirstCode == false )
		{
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"文字コードの範囲の取得に失敗 : 出力する文字がありません" );

			return;
		}

		//「ファイルヘッダ」「データヘッダ」「フォント情報」のサイズの合計
		totalHeaderSize =	sizeof( FONTFILEHEADER ) +
							sizeof( FONTDATAHEADER ) +
							( sizeof( FONTINFO ) * ( dataHeader.lastCode - dataHeader.firstCode + 1 ) );

		//フォントバッファのサイズを計測
		totalDataSize = 0;
		glyphBuffSize = 0;
		fatImgBuffSize = 0;
		for( code = dataHeader.firstCode; code <= dataHeader.lastCode; code++ )
		{
			FONTINFO info;
			FONTWORK work;

			::ZeroMemory( &info, sizeof( info ) );
			info.imgAddr = 0xFFFFFFFF;

			::ZeroMemory( &work, sizeof( work ) );

			if( ( charaBitTable[code >> 5] & ( 1 << ( code % 32 ) ) ) != 0 )
			{
				unsigned int size = ::GetGlyphOutline( hDC, code, quality, &gm, NULL, NULL, &FONTMAT );

				if( size != GDI_ERROR )
				{
					unsigned int imgSize;
					unsigned short fatImgWidth;
					unsigned short fatImgHeight;

					//フォント情報
					info.imgAddr = ( totalHeaderSize + totalDataSize );
					info.cellWidth = static_cast<unsigned short>( gm.gmCellIncX ) + borderSize2;
					info.cellHeight = static_cast<unsigned short>( m_Size );
					info.x = static_cast<short>( gm.gmptGlyphOrigin.x );
					info.y = static_cast<short>( tm.tmAscent - gm.gmptGlyphOrigin.y );
					info.width = static_cast<unsigned short>( gm.gmBlackBoxX ) + borderSize2;
					info.height = static_cast<unsigned short>( gm.gmBlackBoxY ) + borderSize2;

					//フォントイメージ１のサイズ
					fatImgWidth = info.width + borderSize2;
					fatImgHeight = info.height + borderSize2;

					//フォントイメージ２のサイズ
					imgSize = sizeof( unsigned short ) * ( info.width * info.height );

					//フォントバッファ関係
					if( ( MAX_FILESIZE - ( totalHeaderSize + totalDataSize ) ) < imgSize )
					{
						::SelectObject( hDC, hOldFont );
						::DeleteDC( hDC );
						::DeleteObject( hFont );

						InvokeChangeState( Creator::State::Error );
						Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"フォント情報リストの作成に失敗 : ファイルサイズが {0:D}バイト 以下になるようにフォントのサイズを調整してください", MAX_FILESIZE ) );

						return;
					}

					//フォントグリフ
					glyphBuffSize = max( glyphBuffSize, size );

					//フォントイメージ
					fatImgBuffSize = max( fatImgBuffSize, static_cast<unsigned int>( fatImgWidth * fatImgHeight ) );
					dataHeader.imgBuffSize = max( dataHeader.imgBuffSize, imgSize );

					//フォントワーク
					work.pitch = ( gm.gmBlackBoxX >> CALC_PITCHSHIFT ) << 2;
					if( ( gm.gmBlackBoxX % 4 ) != 0 ) { work.pitch += 4; }
					work.width = static_cast<unsigned short>( gm.gmBlackBoxX );
					work.height = static_cast<unsigned short>( gm.gmBlackBoxY );
					work.fatImgWidth = fatImgWidth;
					work.imgSize = imgSize;

					//トータルデータサイズ
					totalDataSize += imgSize;
				}
			}

			infoList.push_back( info );
			workList.push_back( work );
		}

		if( ( glyphBuffSize == 0 ) || ( fatImgBuffSize == 0 ) || ( dataHeader.imgBuffSize == 0 ) )
		{
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントデータヘッダの作成に失敗 : 出力するすべての文字に対して GetGlyphOutline がエラーを返しました" );

			return;
		}

		totalInfoSize = ( sizeof( FONTINFO ) * static_cast<unsigned int>( infoList.size() ) );

		//グリフバッファを確保
		pGlyphBuffer = static_cast<unsigned char*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, glyphBuffSize ) );
		if( pGlyphBuffer == NULL )
		{
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"グリフバッファの作成に失敗 : システムメモリが不足しています" );

			return;
		}

		//イメージ1( サンプリング用に太らせたもの )
		pFatImgBuff = static_cast<unsigned char*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, fatImgBuffSize ) );
		if( pFatImgBuff == NULL )
		{
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"イメージバッファ(1)の作成に失敗 : システムメモリが不足しています" );

			return;
		}

		//イメージ2( 実際に書き込むもの )
		pImgBuff = static_cast<unsigned short*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, dataHeader.imgBuffSize ) );
		if( pImgBuff == NULL )
		{
			::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"イメージバッファ(2)の作成に失敗 : システムメモリが不足しています" );

			return;
		}

		Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, System::String::Format( L"ファイルサイズ[{0:D}Byte]", ( totalHeaderSize + totalDataSize ) ) );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フォントの書き込み
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		System::IntPtr outputFileNamePtr;

		HANDLE hFile;
		DWORD writeSize;
		wchar_t code;
		unsigned int i;
		unsigned int totalDataWriteSize;
		unsigned int progress;
		const wchar_t* pOutputFileName;

		totalDataWriteSize = 0;
		progress = 0xFFFFFFFF;
		outputFileNamePtr = Marshal::StringToHGlobalUni( m_FilePath );
		pOutputFileName = static_cast<const wchar_t*>( outputFileNamePtr.ToPointer() );

		//出力ファイルを開く
		hFile = ::CreateFile( pOutputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
		Marshal::FreeHGlobal( outputFileNamePtr );
		if( hFile == INVALID_HANDLE_VALUE )
		{
			::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントファイルの作成に失敗 : CreateFile がエラーを返しました" );

			return;
		}

		//フォントファイルヘッダを書き込む
		writeSize = 0;
		if( ( ::WriteFile( hFile, &fileHeader, sizeof( fileHeader ), &writeSize, NULL ) == FALSE ) ||
			( writeSize != sizeof( fileHeader ) ) )
		{
			::CloseHandle( hFile );
			::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントファイルヘッダの書き込みに失敗 : WriteFile がエラーを返しました" );

			return;
		}

		//フォントデータヘッダを書き込む
		writeSize = 0;
		if( ( ::WriteFile( hFile, &dataHeader, sizeof( dataHeader ), &writeSize, NULL ) == FALSE ) ||
			( writeSize != sizeof( dataHeader ) ) )
		{
			::CloseHandle( hFile );
			::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントデータヘッダの書き込みに失敗 : WriteFile がエラーを返しました" );

			return;
		}

		//フォント情報を書き込む
		writeSize = 0;
		if( ( ::WriteFile( hFile, &( infoList[0] ), totalInfoSize, &writeSize, NULL ) == FALSE ) ||
			( writeSize != totalInfoSize ) )
		{
			::CloseHandle( hFile );
			::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
			::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
			::SelectObject( hDC, hOldFont );
			::DeleteDC( hDC );
			::DeleteObject( hFont );

			InvokeChangeState( Creator::State::Error );
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォント情報リストの書き込みに失敗 : WriteFile がエラーを返しました" );

			return;
		}

		//フォントを書き込む
		for( code = dataHeader.firstCode; code <= dataHeader.lastCode; code++ )
		{
			//フォントを取得
			if( ( charaBitTable[code >> 5] & ( 1 << code % 32 ) ) != 0 )
			{
				unsigned int glyphSize;

				::ZeroMemory( &gm, sizeof( gm ) );
				::ZeroMemory( pGlyphBuffer, glyphBuffSize );

				glyphSize = ::GetGlyphOutline( hDC, code, quality, &gm, glyphBuffSize, pGlyphBuffer, &FONTMAT );
				if( glyphSize != GDI_ERROR )
				{
					FONTINFO* pfi = &( infoList[( code - dataHeader.firstCode )] );
					FONTWORK* pfw = &( workList[( code - dataHeader.firstCode )] );

					//イメージを作成
					if( ( dataHeader.effects & FE_BORDERING ) == FE_BORDERING )
					{
						/*
							縁取り
						*/

						::ZeroMemory( pFatImgBuff, fatImgBuffSize );
						::ZeroMemory( pImgBuff, dataHeader.imgBuffSize );

						//256階調に変換してイメージバッファ１へ書き込む
						for( i = 0; i < pfw->height; i++ )
						{
							unsigned char* pgb = ( pGlyphBuffer + ( pfw->pitch * i ) );
							unsigned char* pgb_end = ( pgb + pfw->width );

							unsigned char* pib = pFatImgBuff + ( pfw->fatImgWidth * ( borderSize2 + i ) ) + borderSize2;

							while( pgb != pgb_end )
							{
								*pib++ = ( 255 * *pgb ) >> transShift;
								pgb++;
							}
						}

						//イメージバッファ１をサンプリングをして、縁取りしたものをイメージバッファ２へ書き込む
						for( i = 0; i < pfi->height; i++ )
						{
							unsigned char* pfib = pFatImgBuff + ( ( borderSize + i ) * pfw->fatImgWidth ) + borderSize;
							unsigned char* pfib_end = pfib + pfi->width;

							unsigned short* pib = pImgBuff + ( pfi->width * i );

							while( pfib != pfib_end )
							{
								unsigned char imgAlpha = *pfib;

								unsigned short tint;
								unsigned short alpha;

								if( imgAlpha == 0xFF )
								{
									//Fore
									tint = 0xFF00;
									alpha = 0x00FF;
								}
								else
								{
									if( imgAlpha > 0 )
									{
										//Fore → Border
										tint = ( static_cast<unsigned short>( imgAlpha ) << 8 ) & 0xFF00;
										alpha = 0x00FF;
									}
									else
									{
										//Border → Background
										unsigned int total = 0;
										unsigned char white = 0;

										for( int ox = -borderSize; ox <= +borderSize; ox++ )
										{
											for( int oy = -borderSize; oy <= +borderSize; oy++ )
											{
												unsigned char temp = *( pfib + ( static_cast<int>( pfw->fatImgWidth ) * oy ) + ox );

												total += temp;
												white = max( white, temp );
											}
										}

										if( total > 0 )
										{
//											tint = ( ( 255 - white ) << 8 ) & 0xFF00;
											tint = 0x0000;
											alpha = white & 0x00FF;
										}
										else
										{
											tint = 0x0000;
											alpha = 0x0000;
										}
									}
								}

//								System::Diagnostics::Debug::Write( System::String::Format( gcnew System::String( L"{0:X2}" ), ( tint >> 8 ) ) );
//								System::Diagnostics::Debug::Write( System::String::Format( gcnew System::String( L"{0:X2}" ), alpha ) );

								*pib++ = tint | alpha;
								pfib++;
							}

//							System::Diagnostics::Debug::WriteLine( gcnew System::String( L"" ) );
						}

//						System::Diagnostics::Debug::WriteLine( gcnew System::String( L"" ) );
					}
					else
					{
						/*
							通常
						*/

						unsigned short* pib = pImgBuff;

						::ZeroMemory( pImgBuff, dataHeader.imgBuffSize );

						for( i = 0; i < pfw->height; i++ )
						{
							unsigned char* pgb = ( pGlyphBuffer + ( pfw->pitch * i ) );
							unsigned char* pgb_end = ( pgb + pfw->width );

							while( pgb != pgb_end )
							{
								unsigned short alpha = ( ( 255 * *pgb ) >> transShift ) & 0x00FF;
								unsigned short tint = ( alpha > 0 )? 0xFF00 : 0x0000;

								*pib++ = tint | alpha;
								pgb++;
							}
						}
					}

					//フォントイメージを書き込む
					writeSize = 0;
					if( ( ::WriteFile( hFile, pImgBuff, pfw->imgSize, &writeSize, NULL ) == TRUE ) &&
						( writeSize == pfw->imgSize ) )
					{
						totalDataWriteSize += writeSize;

						unsigned int tempProgress = ( ( totalDataWriteSize * 100 ) / totalDataSize );
						if( tempProgress != progress )
						{
							progress = tempProgress;
							InvokeProgress( static_cast<int>( progress ) );
						}
					}
					else
					{
						::CloseHandle( hFile );
						::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
						::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
						::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
						::SelectObject( hDC, hOldFont );
						::DeleteDC( hDC );
						::DeleteObject( hFont );

						InvokeChangeState( Creator::State::Error );
						Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントグリフの書き込みに失敗 : WriteFile がエラーを返しました" );

						return;
					}
/*
					//フォントのピクセルを256階調へ変換
					for( i = 0; i < pfw->height; i++ )
					{
						unsigned char* pb = ( pGlyphBuffer + ( pfw->pitch * i ) );
						unsigned char* pb_end = ( pb + pfw->width );

						while( pb != pb_end )
						{
							*pb++ = ( ( 255 * *pb ) >> transShift );
						}
					}
				
					//フォントグリフを書き込む
					writeSize = 0;
					if( ( ::WriteFile( hFile, pGlyphBuffer, glyphSize, &writeSize, NULL ) == TRUE ) &&
						( writeSize == glyphSize ) )
					{
						totalDataWriteSize += glyphSize;

						unsigned int tempProgress = ( ( totalDataWriteSize * 100 ) / totalDataSize );
						if( tempProgress != progress )
						{
							progress = tempProgress;
							InvokeProgress( static_cast<int>( progress ) );
						}
					}
					else
					{
						::CloseHandle( hFile );
						::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
						::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
						::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
						::SelectObject( hDC, hOldFont );
						::DeleteDC( hDC );
						::DeleteObject( hFont );

						InvokeChangeState( Creator::State::Error );
						Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, L"フォントグリフの書き込みに失敗 : WriteFile がエラーを返しました" );

						return;
					}
*/
				}
			}
		}

		::CloseHandle( hFile );
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 終了処理
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	::HeapFree( ::GetProcessHeap(), 0, pImgBuff );
	::HeapFree( ::GetProcessHeap(), 0, pFatImgBuff );
	::HeapFree( ::GetProcessHeap(), 0, pGlyphBuffer );
	::SelectObject( hDC, hOldFont );
	::DeleteDC( hDC );
	::DeleteObject( hFont );

	InvokeChangeState( Creator::State::Complete );
	Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, L"作成が正常に終了しました" );
}

bool Creator::CreateCharacterBitTableFromText( unsigned int* pBitTable )
{
	unsigned int charCount = 0;

	::ZeroMemory( pBitTable, sizeof( unsigned int[Creator::MAX_CHARACTER_BITTABLE] ) );

	for each ( System::String^ fileName in m_Texts->m_List )
	{
		HANDLE hFile;
		DWORD fileSize;
		DWORD readSize;
		System::IntPtr fileNamePtr;
		const wchar_t* pFileName;
		wchar_t* pCharBuff;
		wchar_t* pChar;
		wchar_t* pCharEnd;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// テキストファイルを読み込む
		////////////////////////////////////////////////////////////////////////////////////////////////////

		Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, System::String::Format( L"出力する文字のソース[\"{0}\"]", fileName ) );

		readSize = 0;

		fileNamePtr = Marshal::StringToHGlobalUni( fileName );
		pFileName = static_cast<wchar_t*>( fileNamePtr.ToPointer() );

		hFile = ::CreateFileW( pFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
		Marshal::FreeHGlobal( fileNamePtr );
		if( hFile == INVALID_HANDLE_VALUE )
		{
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"テキストファイル \"{0}\" が見つかりません", fileName ) );
			return false;
		}

		fileSize = ::GetFileSize( hFile, NULL );
		if( fileSize == 0xFFFFFFFF )
		{
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"テキストファイル \"{0}\" のサイズの取得に失敗しました", fileName ) );
			::CloseHandle( hFile );
			return false;
		}

		pCharBuff = static_cast<wchar_t*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, fileSize ) );
		if( pCharBuff == nullptr )
		{
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"テキストファイル \"{0}\" のメモリを確保できませんでした", fileName ) );
			::CloseHandle( hFile );
			return false;
		}

		if( ::ReadFile( hFile, pCharBuff, fileSize, &readSize, NULL ) == FALSE )
		{
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"テキストファイル \"{0}\" の読み込みに失敗しました", fileName ) );
			::HeapFree( ::GetProcessHeap(), 0, pCharBuff );
			::CloseHandle( hFile );
			return false;
		}

		::CloseHandle( hFile );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// 文字ビットテーブルを作成
		////////////////////////////////////////////////////////////////////////////////////////////////////

		pChar = &( pCharBuff[0] );
		pCharEnd = ( pChar + ( fileSize >> 1 ) );

		if( ( *pChar ) != 0xFEFF )
		{
			Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Error, System::String::Format( L"テキストファイル \"{0}\" はユニコードファイルではありません", fileName ) );
			::HeapFree( ::GetProcessHeap(), 0, pCharBuff );
			return false;
		}

		while( pChar != pCharEnd )
		{
			unsigned int bitMask;

			if( ( ( *pChar ) == L'\r' ) ||
				( ( *pChar ) == L'\n' ) ||
				( ( *pChar ) == L'\t' ) ||
				( ( *pChar ) == L' '  ) ||
				( ( *pChar ) == L'　' ) )
			{
				pChar++;
				continue;
			}

			bitMask = ( 1 << ( ( *pChar ) % 32 ) );
			if( ( pBitTable[( *pChar ) >> 5] & bitMask ) == 0 )
			{
				pBitTable[( *pChar ) >> 5] |= bitMask;
				charCount++;
			}

			pChar++;
		}

		::HeapFree( ::GetProcessHeap(), 0, pCharBuff );
		pCharBuff = nullptr;
	}

	Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, System::String::Format( L"出力する文字数[{0:D}]", charCount ) );

	return true;
}

void Creator::CreateCharacterBitTableFromTable( unsigned int* pBitTable )
{
	unsigned int charCount = 0;

	const wchar_t* pChar;
	const wchar_t* pCharEnd;

	Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, System::String::Format( L"出力する文字のソース[対応するすべての文字]" ) );

	::ZeroMemory( pBitTable, sizeof( unsigned int[Creator::MAX_CHARACTER_BITTABLE] ) );

	pChar = &( Mix::Tool::Font::CHARACTER_TABLE[0] );
	pCharEnd = ( pChar + Mix::Tool::Font::CHARACTER_COUNT );

	while( pChar != pCharEnd )
	{
		unsigned int bitMask;

		bitMask = ( 1 << ( ( *pChar ) % 32 ) );
		if( ( pBitTable[( *pChar ) >> 5] & bitMask ) == 0 )
		{
			pBitTable[( *pChar ) >> 5] |= bitMask;
			charCount++;
		}

		pChar++;
	}

	Mix::Tool::Logger::Print( Mix::Tool::Logger::Type::Info, System::String::Format( L"出力する文字数[{0:D}]", charCount ) );
}

void Creator::InvokeProgress( int progress )
{
	if( m_Progress != nullptr )
	{
		m_Progress->Invoke( progress );
	}
}

void Creator::InvokeChangeState( Creator::State state )
{
	if( m_ChangeState != nullptr )
	{
		m_ChangeState->Invoke( state );
	}
}

}}}
