/*	
 *	DxlibHelper_SprFnt.cpp
 *
 *	DxLibヘルパー関数(DxLib用のスプライトフォント描画)
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Tue Aug 16 21:54:11 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- DxLib用のスプライトフォント描画を追加しました。
 *	  実装は、DxlibHelper_SprFnt.cppに有ります。
 *	* Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
 *	- スプライトフォントを定義したYamlファイルが、DXアーカイブ内に有っても読み込めるようにしました。
 *	* Wed Oct 19 21:15:15 JST 2016 Naoyuki Sawa
 *	- DxlibHelper_SprFnt.cppに、SprFnt_DrawXF(),CbkQue_SprFnt_DrawXF()を追加しました。
 *	* Thu Oct 20 21:11:23 JST 2016 Naoyuki Sawa
 *	- DxlibHelper_SprFnt.cppに、「CbkQue_SprFnt_DrawXF()の使用例」を追記しました。
 */
#include "clip.h"
/****************************************************************************
 *	構造体
 ****************************************************************************/
/*typedef*/ struct _ST_SprFnt {
	ght_hash_table_t*	ht_Char;	//文字コード(0x0000〜0xFFFF)⇒(ST_SprFnt_Char*)。文字コード0x0000は、フォールバック用の描画設定と見なす。描画しようとした文字のスプライトが定義されていない場合、フォールバック用の描画設定が有ればそれを描画する。フォールバック用の描画設定も無ければ、エラー停止する。
} /*ST_SprFnt*/;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
typedef struct _ST_SprFnt_Char {
	int			SprNo;		//スプライト番号。スプライトが無い場合は(-1)とする。スプライトが無くても良いのは、半角スペース,又は,全角スペースの場合のみです。半角スペース,又は,全角スペースの場合でも、スプライトを指定する事は可能です。
	int			DrawX;		//描画オフセットX。スプライト原点Xを、この位置に描画します。
	int			DrawY;		//描画オフセットY。スプライト原点Yを、この位置に描画します。
	int			NextX;		//列ストライド。この文字を描いた後、この値の分、右へ進みます。
	int			NextY;		//行ストライド。一行に描いた文字の、この値の内、最大の値の分、改行時に下へ進みます。
} ST_SprFnt_Char;
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
static ST_SprFnt* SprFnt_Load_Parse(yaml_document_t* d, const void* pEnmKey, int _RegKey_SprNo, const char* FontName);
static void SprFnt_Draw_subr1(ST_SprFnt* pSprFnt, int* p_size_x, int* p_size_y, const char* s);
static void SprFnt_Draw_subr2(ST_SprFnt* pSprFnt, double x, double y, double scale_x, double scale_y, int rgba, int mode, const char* s);
static void SprFnt_DrawXF_subr2(ST_SprFnt* pSprFnt, int x1, int y1, int x2, int y2, double scale_x, double scale_y, int rgba, int mode, const char* s1, const char* s2, double transition/*0.0〜1.0*/);
/****************************************************************************
 *	
 ****************************************************************************/
ST_SprFnt* SprFnt_Load(const char* fname, const void* pEnmKey, int _RegKey_SprNo, const char* FontName/* = "Font"*/) {
//{{2016/09/24変更:スプライトフォントを定義したYamlファイルが、DXアーカイブ内に有っても読み込めるようにしました。
//	yaml_document_t* d = yaml_document_load(fname);
//	ST_SprFnt* pSprFnt = SprFnt_Load_Parse(d, pEnmKey, _RegKey_SprNo, FontName);
//	yaml_document_free(d);
//	return pSprFnt;
//↓2016/09/24変更:スプライトフォントを定義したYamlファイルが、DXアーカイブ内に有っても読み込めるようにしました。
	char* s = FullyLoadText(fname);
	if(!s) { DIE(); }
	ST_SprFnt* pSprFnt = SprFnt_Parse(s, pEnmKey, _RegKey_SprNo, FontName);
	free(s);
	return pSprFnt;
//}}2016/09/24変更:スプライトフォントを定義したYamlファイルが、DXアーカイブ内に有っても読み込めるようにしました。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
ST_SprFnt* SprFnt_Parse(const char* s, const void* pEnmKey, int _RegKey_SprNo, const char* FontName/* = "Font"*/) {
	yaml_document_t* d = yaml_document_parse(s);
	ST_SprFnt* pSprFnt = SprFnt_Load_Parse(d, pEnmKey, _RegKey_SprNo, FontName);
	yaml_document_free(d);
	return pSprFnt;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static ST_SprFnt* SprFnt_Load_Parse(yaml_document_t* d, const void* pEnmKey, int _RegKey_SprNo, const char* FontName) {
	ST_SprFnt* pSprFnt = (ST_SprFnt*)calloc(sizeof(ST_SprFnt), 1);
	if(!pSprFnt) { DIE(); }
	pSprFnt->ht_Char = ght_create(0);
	ght_set_rehash(pSprFnt->ht_Char, 1);
	{
		//スプライト名の比較関数を取得しておく。
		int (*compar)(const char*, const char*) = EnmDef_GetCanonicalNameCompar(pEnmKey, _RegKey_SprNo);
		if(!compar) { DIE(); }
		{
			//指定されたフォント名のノードを取得する。
			yaml_mapping_t* mFont = yaml_path_get_mapping(d, NULL, FontName);
			//フォントの既定値を取得する。
			int Font_DrawX       = 0, Font_DrawY       = 0, Font_NextX       = 0, Font_NextY       = 0;	//フォントの既定値が定義されていなければ0.0のままとする。
			int Font_DrawX_Valid = 0, Font_DrawY_Valid = 0, Font_NextX_Valid = 0, Font_NextY_Valid = 0;	//フォントの既定値が定義されていなければ  0のままとする。
			SEH_try { Font_DrawX = yaml_mapping_get_number(d, mFont, "DrawX"); Font_DrawX_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//描画オフセットX
			SEH_try { Font_DrawY = yaml_mapping_get_number(d, mFont, "DrawY"); Font_DrawY_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//描画オフセットY
			SEH_try { Font_NextX = yaml_mapping_get_number(d, mFont, "NextX"); Font_NextX_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//列ストライド
			SEH_try { Font_NextY = yaml_mapping_get_number(d, mFont, "NextY"); Font_NextY_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//行ストライド
			{
				//文字のルートノードを取得する。
				yaml_mapping_t* mCharRoot = yaml_mapping_get_mapping(d, mFont, "Char");
				//各文字について…
				int i, n = yaml_mapping_get_count(d, mCharRoot);
				for(i = 0; i < n; i++) {
					const char* CharName = yaml_mapping_get_key(d, mCharRoot, i);
					//文字コードを取得する。
					int CharCode = mbsnextc(CharName);
					//空文字ならば…
					if(!CharCode) {
						//フォールバック用の描画設定と見なして、処理を継続する。
						/** no job **/
					//空文字でなければ…
					} else {
						//文字のキーは、一文字でなければならない。
						if(*mbsinc(CharName)) { DIE(); }				//もしここで停止した場合は、おそらくアプリケーション定義のYamlファイルの間違いです。
						//1バイト文字ならば、表示可能な文字でなければならない。
						if((CharCode <= UCHAR_MAX) && !isprint(CharCode)) { DIE(); }	//もしここで停止した場合は、おそらくアプリケーション定義のYamlファイルの間違いです。
					}
					{
						//文字のノードを取得する。
						yaml_mapping_t* mChar = yaml_mapping_get_mapping(d, mCharRoot, CharName);
						//文字の設定値を取得する。
						const char* SprNo_Name;		//スプライト名(列挙値の名前)	必須。半角スペース,又は,全角スペースの場合のみ空文字可。
						int Char_DrawX       = 0, Char_DrawY       = 0, Char_NextX       = 0, Char_NextY       = 0;	//文字の設定値が定義されていなければ0.0のままとする。
						int Char_DrawX_Valid = 0, Char_DrawY_Valid = 0, Char_NextX_Valid = 0, Char_NextY_Valid = 0;	//文字の設定値が定義されていなければ  0のままとする。
						          SprNo_Name = yaml_mapping_get_string(d, mChar, "SprNo");								//スプライト名(列挙値の名前)	必須。半角スペース,又は,全角スペースの場合のみ空文字可。
						SEH_try { Char_DrawX = yaml_mapping_get_number(d, mChar, "DrawX"); Char_DrawX_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//描画オフセットX
						SEH_try { Char_DrawY = yaml_mapping_get_number(d, mChar, "DrawY"); Char_DrawY_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//描画オフセットY
						SEH_try { Char_NextX = yaml_mapping_get_number(d, mChar, "NextX"); Char_NextX_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//列ストライド
						SEH_try { Char_NextY = yaml_mapping_get_number(d, mChar, "NextY"); Char_NextY_Valid = 1; } SEH_catch(YamlException) { } SEH_end	//行ストライド
						{
							int SprNo;		//スプライト番号(列挙値)	スプライト名が空文字列ならば-1とする。
							ST_SprDef stSprDef;	//スプライト定義
							//スプライト名が空文字でなければ…
							if(*SprNo_Name) {
								//スプライト名から、スプライト番号を取得する。
								SprNo = EnmDef_GetVal_UseCompar(pEnmKey, _RegKey_SprNo, SprNo_Name, compar);
								if(SprNo == -1) { DIE(); }	//SprNoキーで指定されたスプライト名に一致するスプライトが存在しなければ、エラー停止する。おそらくアプリケーション定義のYamlファイルの間違いです。
								//スプライト定義を取得する。
								get_sprDef(SprNo, &stSprDef);
							//スプライト名が空文字ならば…
							} else {
								//スプライト名が定義されていなくても良いのは、半角スペース,又は,全角スペースの場合のみである。
								if((CharCode != 0x20) && (CharCode != 0x8140)) { DIE(); }	//もしここで停止した場合は、おそらくアプリケーション定義のYamlファイルの間違いです。
								SprNo = -1;
								//スプライト定義をクリアする。
								memset(&stSprDef, 0, sizeof stSprDef);
							}
							//文字の設定値に、描画オフセットXが定義されていなければ…
							if(!Char_DrawX_Valid) {
								//フォントの既定値に、描画オフセットXが定義されていたら…
								if(Font_DrawX_Valid) {
									//フォントの既定値から、描画オフセットXを取得する。
									Char_DrawX = Font_DrawX;
								}
							}
							//文字の設定値に、描画オフセットYが定義されていなければ…
							if(!Char_DrawY_Valid) {
								//フォントの既定値に、描画オフセットYが定義されていたら…
								if(Font_DrawY_Valid) {
									//フォントの既定値から、描画オフセットYを取得する。
									Char_DrawY = Font_DrawX;
								}
							}
							//文字の設定値に、列ストライドが定義されていなければ…
							if(!Char_NextX_Valid) {
								//フォントの既定値に、列ストライドが定義されていたら…
								if(Font_NextX_Valid) {
									//フォントの既定値から、列ストライドを取得する。
									Char_NextX = Font_NextX;
								//フォントの既定値にも、列ストライドが定義されていなければ…
								} else {
									//スプライトの幅を、列ストライドとする。
									Char_NextX = stSprDef.w;
								}
							}
							//文字の設定値に、行ストライドが定義されていなければ…
							if(!Char_NextY_Valid) {
								//フォントの既定値に、行ストライドが定義されていたら…
								if(Font_NextY_Valid) {
									//フォントの既定値から、行ストライドを取得する。
									Char_NextY = Font_NextY;
								//フォントの既定値にも、行ストライドが定義されていなければ…
								} else {
									//スプライトの高さを、行ストライドとする。
									Char_NextY = stSprDef.h;
								}
							}
							{
								//文字を登録する。
								ST_SprFnt_Char* pChar = (ST_SprFnt_Char*)calloc(sizeof(ST_SprFnt_Char), 1);
								if(!pChar) { DIE(); }
								pChar->SprNo = SprNo;		//スプライト番号
								pChar->DrawX = Char_DrawX;	//描画オフセットX
								pChar->DrawY = Char_DrawY;	//描画オフセットY
								pChar->NextX = Char_NextX;	//列ストライド
								pChar->NextY = Char_NextY;	//行ストライド
								if(ght_insert(pSprFnt->ht_Char, pChar, sizeof CharCode, &CharCode)) { DIE(); }	//文字が重複している。おそらくアプリケーション定義のYamlファイルの間違いです。
#ifdef  _DEBUG
								syslog(LOG_DEBUG, "%s(%s): SprNo='%s' DrawX=%d DrawY=%d NextX=%d NextY=%d", FontName, CharName, SprNo_Name, Char_DrawX, Char_DrawY, Char_NextX, Char_NextY);
#endif//_DEBUG
							}
						}
					}
				}
			}
		}
	}
	return pSprFnt;
}
/*--------------------------------------------------------------------------*/
void SprFnt_Free(ST_SprFnt* pSprFnt) {
	ght_iterator_t it;
	ST_SprFnt_Char* pChar;
	for(pChar = (ST_SprFnt_Char*)ght_first(pSprFnt->ht_Char, &it, NULL);
	    pChar;
	    pChar = (ST_SprFnt_Char*)ght_next( pSprFnt->ht_Char, &it, NULL)) { free(pChar); }
	ght_finalize(pSprFnt->ht_Char);
	free(pSprFnt);
}
/*--------------------------------------------------------------------------*/
void SprFnt_Draw(ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* s) {
	int size_x, size_y;
	SprFnt_Draw_subr1(pSprFnt, &size_x, &size_y, s);
	x -= (size_x * scale_x * ax);
	y -= (size_y * scale_y * ay);
	SprFnt_Draw_subr2(pSprFnt, x, y, scale_x, scale_y, rgba, mode, s);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void SprFnt_DrawV(ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* fmt, va_list ap) {
	char* s = strdup_vprintf(fmt, ap);
	SprFnt_Draw(pSprFnt, x, y, scale_x, scale_y, ax, ay, rgba, mode, s);
	free(s);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void SprFnt_DrawF(ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	SprFnt_DrawV(pSprFnt, x, y, scale_x, scale_y, ax, ay, rgba, mode, fmt, ap);
	va_end(ap);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void SprFnt_Draw_subr1(ST_SprFnt* pSprFnt, int* p_size_x, int* p_size_y, const char* s) {
	//アルゴリズムは概ねclippce.cのrender_string()と同じです。
	int x = 0, y = 0, size_x = 0, size_y = 0;
	int c, h = 0;
	while((c = mbsnextc(s))) {
		if(c == '\n') {
			x = 0;
			y += h;
			h = 0;
		} else {
			//この文字の描画設定を取得する。
			ST_SprFnt_Char* pChar = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c, &c);
			//この文字の描画設定が無ければ…
			if(!pChar) {
				//フォールバック用の描画設定を取得する。
				static const int c0;	//フォールバック用の描画設定の文字コード(0x0000)
				pChar = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c0, &c0);
				if(!pChar) { DIE(); }	//フォールバック用の描画設定も無ければ、エラー停止する。
			}
			//列ストライドの分、次の描画位置を右へ進める。
			x += pChar->NextX;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			h = max(h, pChar->NextY);
			//サイズを更新する。
			if(x > size_x) { size_x = x; }
			if((y + h) > size_y) { size_y = (y + h); }
		}
		s = mbsinc(s);
	}
	*p_size_x = size_x;
	*p_size_y = size_y;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void SprFnt_Draw_subr2(ST_SprFnt* pSprFnt, double x, double y, double scale_x, double scale_y, int rgba, int mode, const char* s) {
	const double x0 = x;
	int c, h = 0;
	while((c = mbsnextc(s))) {
		if(c == '\n') {
			x = x0;
			y += h * scale_y;
			h = 0;
		} else {
			//この文字の描画設定を取得する。
			ST_SprFnt_Char* pChar = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c, &c);
			//この文字の描画設定が無ければ…
			if(!pChar) {
				//フォールバック用の描画設定を取得する。
				static const int c0;	//フォールバック用の描画設定の文字コード(0x0000)
				pChar = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c0, &c0);
				if(!pChar) { DIE(); }	//フォールバック用の描画設定も無ければ、エラー停止する。
			}
			//スプライト番号が有れば…
			if(pChar->SprNo != -1) {
				//スプライトを描画する。
				draw_spr2(
					x + (pChar->DrawX * scale_x),
					y + (pChar->DrawY * scale_y),
					pChar->SprNo,
					scale_x,
					scale_y,
					0.0,
					rgba,
					mode);
			}
			//列ストライドの分、次の描画位置を右へ進める。
			x += pChar->NextX * scale_x;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			h = max(h, pChar->NextY);	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
		s = mbsinc(s);
	}
}
/*--------------------------------------------------------------------------*/
//文字列s1とs2を、混合率(1.0-transition):transitionで、各文字の位置を補間しながら、クロスフェードして描く。
// - 文字列s1とs2の文字数は異なっていても構いませんが、お互いの一文字目から混合して行くので、同じ文字数である事が望ましいです。
//   文字数が違っていても、左揃え(ax=0.0)ならば自然に表示されますが、右揃え(ax=1.0)や中揃え(ax=0.5)だと、意図と違う描画結果になる恐れが有ります(動作はしますが)。
void SprFnt_DrawXF(ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* s1, const char* s2, double transition/*0.0〜1.0*/) {
	int size_x, size_y;
	//文字列1の描画位置を求める。
	SprFnt_Draw_subr1(pSprFnt, &size_x, &size_y, s1);
	int x1 = x - (size_x * scale_x * ax);
	int y1 = y - (size_y * scale_y * ay);
	//文字列2の描画位置を求める。
	SprFnt_Draw_subr1(pSprFnt, &size_x, &size_y, s2);
	int x2 = x - (size_x * scale_x * ax);
	int y2 = y - (size_y * scale_y * ay);
	//文字1と文字2をクロスフェードして描く。
	SprFnt_DrawXF_subr2(pSprFnt, x1, y1, x2, y2, scale_x, scale_y, rgba, mode, s1, s2, transition);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void SprFnt_DrawXF_subr2(ST_SprFnt* pSprFnt, int x1, int y1, int x2, int y2, double scale_x, double scale_y, int rgba, int mode, const char* s1, const char* s2, double transition/*0.0〜1.0*/) {
	static const int c0;	//フォールバック用の描画設定の文字コード(0x0000)
	const double x10 = x1, x20 = x2, t1 = (1.0 - transition), t2 = transition;
	const int rgba1 = RGBA_MAKE(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba), RGBA_GETALPHA(rgba) * t1);
	const int rgba2 = RGBA_MAKE(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba), RGBA_GETALPHA(rgba) * t2);
	int c, h1 = 0, h2 = 0;
	for(;;) {
		ST_SprFnt_Char *pChar1 = NULL, *pChar2 = NULL;
		//文字列1について…
		for(;;) {
			//文字1を取得する。
			if(!(c = mbsnextc(s1))) { break; }	//文字列1が終端ならばループを抜ける。
			s1 = mbsinc(s1);
			//文字1が改行文字でなければ…
			if(c != '\n') {
				//文字1の描画設定を取得する。
				pChar1 = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c, &c);
				//文字1の描画設定が無ければ…
				if(!pChar1) {
					//フォールバック用の描画設定を取得する。
					pChar1 = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c0, &c0);
					if(!pChar1) { DIE(); }	//フォールバック用の描画設定も無ければ、エラー停止する。
				}
				break;	//ループを抜ける。
			}
			//文字1が改行文字ならば…
			x1 = x10;
			y1 += h1 * scale_y;
			h1 = 0;
		}
		//文字列2について…
		for(;;) {
			//文字2を取得する。
			if(!(c = mbsnextc(s2))) { break; }	//文字列2が終端ならばループを抜ける。
			s2 = mbsinc(s2);
			//文字2が改行文字でなければ…
			if(c != '\n') {
				//文字2の描画設定を取得する。
				pChar2 = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c, &c);
				//文字2の描画設定が無ければ…
				if(!pChar2) {
					//フォールバック用の描画設定を取得する。
					pChar2 = (ST_SprFnt_Char*)ght_get(pSprFnt->ht_Char, sizeof c0, &c0);
					if(!pChar2) { DIE(); }	//フォールバック用の描画設定も無ければ、エラー停止する。
				}
				break;	//ループを抜ける。
			}
			//文字2が改行文字ならば…
			x2 = x20;
			y2 += h2 * scale_y;
			h2 = 0;
		}
		//文字列1も文字列2も終端ならば、終了する。
		if(!pChar1 && !pChar2) { break; }	//ここまで
		//文字列1が終端でなく,かつ,文字1のスプライト番号が有れば…
		if(pChar1 && (pChar1->SprNo != -1)) {
			//文字列2が終端でなく,かつ,文字2のスプライト番号が有れば…
			if(pChar2 && (pChar2->SprNo != -1)) {
				double x = (x1 * t1) + (x2 * t2);
				double y = (y1 * t1) + (y2 * t2);
				//文字1と文字2のスプライト番号が同じならば…
				if(pChar1->SprNo == pChar2->SprNo) {
					//文字1(文字2でも同じ)を、補間した位置に描く。
					draw_spr2(x  + (pChar1->DrawX * scale_x), y  + (pChar1->DrawY * scale_y), pChar1->SprNo, scale_x, scale_y, 0.0, rgba , mode);
				//文字1と文字2のスプライト番号が同じでなければ…
				} else {
					//文字1と文字2を、補間した位置に、クロスフェードして描く。
					draw_spr2(x  + (pChar1->DrawX * scale_x), y  + (pChar1->DrawY * scale_y), pChar1->SprNo, scale_x, scale_y, 0.0, rgba1, mode);
					draw_spr2(x  + (pChar2->DrawX * scale_x), y  + (pChar2->DrawY * scale_y), pChar2->SprNo, scale_x, scale_y, 0.0, rgba2, mode);
				}
			//文字列2が終端か,又は,文字2のスプライト番号が無ければ…
			} else {
					//文字1を、文字1の位置に、フェードアウトして描く。
					draw_spr2(x1 + (pChar1->DrawX * scale_x), y1 + (pChar1->DrawY * scale_y), pChar1->SprNo, scale_x, scale_y, 0.0, rgba1, mode);
			}
		//文字1のスプライト番号が無ければ…
		} else {
			//文字列2が終端でなく,かつ,文字2のスプライト番号が有れば…
			if(pChar2 && (pChar2->SprNo != -1)) {
					//文字2を、文字2の位置に、フェードインして描く。
					draw_spr2(x2 + (pChar2->DrawX * scale_x), y2 + (pChar2->DrawY * scale_y), pChar2->SprNo, scale_x, scale_y, 0.0, rgba2, mode);
			//文字列2が終端か,又は,文字2のスプライト番号が無ければ…
			} else {
					/** no job **/
			}
		}
		//文字列1が終端でなければ…
		if(pChar1) {
			//列ストライドの分、次の描画位置を右へ進める。
			x1 += pChar1->NextX * scale_x;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			h1 = max(h1, pChar1->NextY);	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
		//文字列2が終端でなければ…
		if(pChar2) {
			//列ストライドの分、次の描画位置を右へ進める。
			x2 += pChar2->NextX * scale_x;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			h2 = max(h2, pChar2->NextY);	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
	}
}
/****************************************************************************
 *	優先度付き描画
 ****************************************************************************/
#ifdef  __CLIP_CBKQ_H__
struct ST_CbkQue_SprFnt_DrawF {
	ST_SprFnt*	pSprFnt;
	int		x;
	int		y;
	double		scale_x;
	double		scale_y;
	double		ax;
	double		ay;
	int		rgba;
	int		mode;
	char		str[0];
};
static void fn_CbkQue_SprFnt_DrawF(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_SprFnt_DrawF* p = (struct ST_CbkQue_SprFnt_DrawF*)param;
	SprFnt_Draw(p->pSprFnt, p->x, p->y, p->scale_x, p->scale_y, p->ax, p->ay, p->rgba, p->mode, p->str);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□SprFnt_DrawF()の優先度付きバージョン
void CbkQue_SprFnt_DrawF(ST_CbkQue* pCbkQue, int pri, struct _ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* fmt, ...) {
	struct ST_CbkQue_SprFnt_DrawF* p;
	va_list ap;
	va_start(ap, fmt);
	p = (struct ST_CbkQue_SprFnt_DrawF*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_SprFnt_DrawF, sizeof(struct ST_CbkQue_SprFnt_DrawF) + vscprintf(fmt, ap) + 1/*nul*/);
	va_end(ap);
	p->pSprFnt	= pSprFnt;
	p->x		= x;
	p->y		= y;
	p->scale_x	= scale_x;
	p->scale_y	= scale_y;
	p->ax		= ax;
	p->ay		= ay;
	p->rgba		= rgba;
	p->mode		= mode;
	va_start(ap, fmt);
	vsprintf(p->str, fmt, ap);
	va_end(ap);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_SprFnt_DrawXF {
	ST_SprFnt*	pSprFnt;
	int		x;
	int		y;
	double		scale_x;
	double		scale_y;
	double		ax;
	double		ay;
	int		rgba;
	int		mode;
	double		transition;
	char		s1_s2[0];	//s1,nul,s2,nulの順で格納する。
};
static void fn_CbkQue_SprFnt_DrawXF(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_SprFnt_DrawXF* p = (struct ST_CbkQue_SprFnt_DrawXF*)param;
	SprFnt_DrawXF(p->pSprFnt, p->x, p->y, p->scale_x, p->scale_y, p->ax, p->ay, p->rgba, p->mode, p->s1_s2/*=s1*/, (strchr(p->s1_s2, '\0') + 1/*nul*/)/*=s2*/, p->transition);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□SprFnt_DrawXF()の優先度付きバージョン
void CbkQue_SprFnt_DrawXF(ST_CbkQue* pCbkQue, int pri, struct _ST_SprFnt* pSprFnt, int x, int y, double scale_x, double scale_y, double ax, double ay, int rgba, int mode, const char* s1, const char* s2, double transition/*0.0〜1.0*/) {
	struct ST_CbkQue_SprFnt_DrawXF* p;
	p = (struct ST_CbkQue_SprFnt_DrawXF*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_SprFnt_DrawXF, sizeof(struct ST_CbkQue_SprFnt_DrawXF) + strlen(s1) + 1/*nul*/ + strlen(s2) + 1/*nul*/);
	p->pSprFnt	= pSprFnt;
	p->x		= x;
	p->y		= y;
	p->scale_x	= scale_x;
	p->scale_y	= scale_y;
	p->ax		= ax;
	p->ay		= ay;
	p->rgba		= rgba;
	p->mode		= mode;
	p->transition	= transition;
	stpcpy(stpcpy(p->s1_s2, s1) + 1/*nul*/, s2);	//s1,nul,s2,nulの順で格納する。
}
#endif//__CLIP_CBKQ_H__
/****************************************************************************
 *	CbkQue_SprFnt_DrawF()の使用例
 ****************************************************************************/
#if 0
/*--------------------------------------------------------------------------*/
#//SprFnt.yml
Font:
		NextX:						8
		NextY:						16
		Char:
				"":
						SprNo:		DUMMY		#//フォールバック用の描画設定
				" ":
						SprNo:				#//スプライト無し
				"　":
						SprNo:				#//スプライト無し
						NextX:		16
				"0":
						SprNo:		NUM_0
				"1":
						SprNo:		NUM_1
				"2":
						SprNo:		NUM_2
				"3":
						SprNo:		NUM_3
				"4":
						SprNo:		NUM_4
				"5":
						SprNo:		NUM_5
				"6":
						SprNo:		NUM_6
				"7":
						SprNo:		NUM_7
				"8":
						SprNo:		NUM_8
				"9":
						SprNo:		NUM_9
				".":
						SprNo:		DOT
						DrawX:		-2
						DrawY:		12
						NextX:		4
/*--------------------------------------------------------------------------*/
//cpp.cpp
int cpp_main(int argc, char* argv[]) {
	if(!setjmp(env_exit)) {
		ST_SprFnt* pSprFnt = SprFnt_Load("xxxxxxxxxxxxx.txt", pEnmKey, RegKey_SprNo);
		for(;;) {
			schedule();
			CbkQue_draw_line(pCbkQue, 0, DISP_X / 2, 0, DISP_X / 2, DISP_Y, 0, D3DRGBA(1.0, 0.0, 0.0, 1.0));
			CbkQue_draw_line(pCbkQue, 0, 0, DISP_Y / 2, DISP_X, DISP_Y / 2, 0, D3DRGBA(1.0, 0.0, 0.0, 1.0));
			CbkQue_SprFnt_DrawF(pCbkQue, 0, pSprFnt, DISP_X / 2, DISP_Y / 2, 2.0, 2.0, 1.0, 0.5, -1, DX_BLENDMODE_ALPHA, "0123 456.789\n0123.456 789");
		}
	}
	return EXIT_SUCCESS;
}
/*--------------------------------------------------------------------------*/
#endif
/****************************************************************************
 *	CbkQue_SprFnt_DrawXF()の使用例
 ****************************************************************************/
#if 0
/*--------------------------------------------------------------------------*/
//cpp.cpp
int cpp_main(int argc, char* argv[]) {
	if(!setjmp(env_exit)) {
		for(;;) {
			schedule();
			char s1[32], s2[32];
			//表示
			int iVal = CntMgr_GetVal(pCntMgr, CntCh_1P);
			double dErrTerm = CntMgr_GetErrTerm(pCntMgr, CntCh_1P);
			snprintf(s1, sizeof s1, "%'12.2f", iVal / 100.0);	//右揃えの場合はs1とs2の文字数を合わせる方が自然に表示されるので、このように先頭部分の空白埋めを行うのが望ましいです。
			if(dErrTerm > 0.0) { iVal++; }				//┐
			if(dErrTerm < 0.0) { iVal--; dErrTerm = -dErrTerm; }	//┴この処理が肝です。
			snprintf(s2, sizeof s2, "%'12.2f", iVal / 100.0);
			CbkQue_SprFnt_DrawXF(pCbkQue, 0, pSprFntMedium, DISP_X-16, DISP_Y-16, 5.0, 5.0, 1.0, 1.0, D3DRGBA(1,0,0,1), DX_BLENDMODE_ALPHA, s1, s2, dErrTerm);
			//操作
			if(keyTrig[KEY_INPUT_1]) { CntMgr_SetVal(pCntMgr, CntCh_1P,       0,   10, SEC(3.0)); }
			if(keyTrig[KEY_INPUT_2]) { CntMgr_SetVal(pCntMgr, CntCh_1P,      10,   10, SEC(3.0)); }
			if(keyTrig[KEY_INPUT_3]) { CntMgr_SetVal(pCntMgr, CntCh_1P,    1000, 1000, SEC(3.0)); }
			if(keyTrig[KEY_INPUT_4]) { CntMgr_SetVal(pCntMgr, CntCh_1P, 1000000, 1000, SEC(3.0)); }
		}
	}
	return EXIT_SUCCESS;
}
/*--------------------------------------------------------------------------*/
#endif
