/*	
 *	clipsprf.c
 *
 *	スプライトフォント
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2017 Naoyuki Sawa
 *
 *	* Sat Mar 18 22:13:04 JST 2017 Naoyuki Sawa
 *	- 作成開始。
 *	  既存のDxlibHelper_SprFnt.cppを元にして作成しました。
 *	  当モジュールを作成する事にした理由は以下の通りです。
 *	- 既存のDxlibHelper_SprFnt.cppの関数はなかなか使い勝手が良く、実際のアプリケーション上でも2016/08～2017/03の間に十分な動作検証が行えました。
 *	  そこで、DxlibHelper_SprFnt.cppを元にして、各環境でも同様の機能を使用できるようにしようと思いました。
 *	  具体的には、既存のDxlibHelper_SprFnt.cppの関数はC++言語とDxlib専用でしたが、C言語とP/ECEや、C#言語とUnity等でも使いたいと思いました。
 *	  そのために、DxlibHelper_SprFnt.cppがDxlibに依存している部分をアプリケーション定義のコールバック関数に分離して、共通化出来る部分をC言語で実装したのが当モジュールです。
 *	  当モジュールのC言語版を元にして、C#言語版版も実装して、libclip.netフォルダ内のライブラリに追加しました。
 *	- 既存のDxlibHelper_SprFnt.cppとの特に大きな違いとして、これまではスプライトフォント定義をYamlで記述し実行時にパースしていましたが、今後は専用のスクリプトで定義して予めdSprFntC.exeでコンパイルするようにしました。
 *	  そうした理由は、現在の所、C#言語(.NET Framework)でYamlをパースする標準的な方法が無いようだからです。
 *	  元々の理由は上記の通りではありますが、予めコンパイルするようになった事によって、実行時のパースエラーが発生しなくなったので、結果的に使い易さも改善したと思います。
 *	  (元々、既存のDxlibHelper_SprFnt.cppでYamlを使用して実行時にパースしていたのは、Yaml自体の勉強のためという理由が大きく、Yamlでなければいけない理由は特に無かったので…)
 *	* Sun Mar 19 22:19:43 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	* Mon Mar 20 22:44:17 JST 2017 Naoyuki Sawa
 *	- SprFnt_PrintXF_subr()にて、演算誤差で位置がぶれる問題を修正しました。
 *	  理論上は、常に補間処理を行っても同じ結果になるはずなのですが、実際には、演算誤差のために微妙にぶれが発生します。
 *	  特に、同じ座標同士で補間処理を行った場合に、文字の位置は変わらないはずなのに微妙にぶれるので目立ってしまいます。
 *	  実際の所、クロスフェードする文字同士の位置は同じ座標である事の方が圧倒的に多いので、この問題はかなり目立ちます。
 *	  そこで、この問題を回避するために、同じ座標である場合は、補間処理を行わずに、そのままの値を使う事にしました。
 *	  尚、異なる座標同士で補間処理を行う場合の演算誤差は対策していませんが、その場合はクロスフェード中に文字が移動するのに紛れて目立たないので問題無いと思います。
 *	* Tue Apr 04 23:33:32 JST 2017 Naoyuki Sawa
 *	- スプライトフォント描画に回転機能を追加しました。
 *	* Wed Apr 19 22:11:14 JST 2017 Naoyuki Sawa
 *	- SprFnt_GetSize()をローカル関数⇒グローバル関数に変更しました。
 */
#include "clip.h"
/*****************************************************************************
 *	構造体
 *****************************************************************************/
//文字の描画設定
typedef struct _ST_SprFnt_Info {
	int		SprNo;		//0(blank),又は,スプライト番号(1～0xFFFF)						┐
	int		NextX;		//列ストライド。この文字を描いた後、この値の分、右へ進みます。				│
	int		NextY;		//行ストライド。一行に描いた文字の、この値の内、最大の値の分、改行時に下へ進みます。	├詳細は、/clip/tool/dSprFntC/winapp.hの、ST_SprFntHdr,及び,ST_SprFntRowのコメントを参照して下さい。
	int		DrawX;		//描画オフセットX。スプライト原点Xを、この位置に描画します。				│
	int		DrawY;		//描画オフセットY。スプライト原点Yを、この位置に描画します。				┘
} ST_SprFnt_Info;
/*****************************************************************************
 *	ローカル関数
 *****************************************************************************/
//文字の描画設定の値を取得する。
static int SprFnt_GetInfo_subr(const void* pSprFnt, const void* pChrKey, int iValueName/*SprFnt_NextX or SprFnt_NextY or SprFnt_DrawX or SprFnt_DrawY*/) {
	int iValue = REG_get_value(pChrKey, iValueName);	//この文字のキーから、値を取得する。
	if(iValue == -1) {					//この文字のキーから、値を取得出来なければ…
		iValue = REG_get_value(pSprFnt, iValueName);	//スプライトフォントのキーから、デフォルト値を取得する。
		if(iValue == -1) { iValue = 0; }		//デフォルト値も無ければ、0とする。
	}
	return iValue << 8 >> 8;				//取得した値のbit23を符号拡張して元の値に戻す。
}
/*---------------------------------------------------------------------------*/
//文字の描画設定を取得する。
static void SprFnt_GetInfo(const void* pSprFnt, int c, /*[out]*/ ST_SprFnt_Info* pInfo) {
	//この文字のキーを取得する。
	const void* pChrKey = REG_open_key(pSprFnt, c);
	//この文字のキーが無ければ…
	if(!pChrKey) {
		//フォールバック用のキーを取得する。
		pChrKey = REG_open_key(pSprFnt, 0/*fallback*/);
		if(!pChrKey) { DIE(); }	//この文字のキーもフォールバック用のキーも取得出来なければエラー停止する。ここでエラー停止した場合は恐らく呼び出し側のバグでこのスプライトフォントに登録されていない文字を含む文字列を描画しようとしており,かつ,このスプライトフォントにフォールバック用の描画設定も設定されていない事が原因です。
	}
	//この文字の(又はフォールバック用の)描画設定を取得する。
	pInfo->SprNo = REG_get_value(pChrKey, SprFnt_SprNo);			//この文字のキーから、0(blank),又は,スプライト番号(1～0xFFFF)を取得する。
	if(pInfo->SprNo == -1) { DIE(); }					//この文字のキーから、0(blank),又は,スプライト番号(1～0xFFFF)が取得出来なければエラー停止する。dSprFntC.exeはどの文字のキーにも少なくともSprNoの値を出力しているはずである。
	pInfo->NextX = SprFnt_GetInfo_subr(pSprFnt, pChrKey, SprFnt_NextX);	//列ストライド,又は,デフォルト値,又は,0を取得する。
	pInfo->NextY = SprFnt_GetInfo_subr(pSprFnt, pChrKey, SprFnt_NextY);	//行ストライド,又は,デフォルト値,又は,0を取得する。
	pInfo->DrawX = SprFnt_GetInfo_subr(pSprFnt, pChrKey, SprFnt_DrawX);	//描画オフセットX,又は,デフォルト値,又は,0を取得する。
	pInfo->DrawY = SprFnt_GetInfo_subr(pSprFnt, pChrKey, SprFnt_DrawY);	//描画オフセットY,又は,デフォルト値,又は,0を取得する。
}
/*---------------------------------------------------------------------------*/
//{{2017/04/19移動:SprFnt_GetSize()をローカル関数⇒グローバル関数に変更しました。
////文字列のサイズを取得する。
//static void SprFnt_GetSize(const void* pSprFnt, const char* s, /*[out]*/ int* p_size_x, /*[out]*/ int* p_size_y) {
//	//アルゴリズムは概ね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_Info stInfo;
//			//この文字の描画設定を取得する。
//			SprFnt_GetInfo(pSprFnt, c, &stInfo);
//			//列ストライドの分、次の描画位置を右へ進める。
//			x += stInfo.NextX;
//			//現在の行に描いた文字の、行ストライドの最大値を更新する。
//			if(stInfo.NextY > h) { h = stInfo.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;
//}
//}}2017/04/19移動:SprFnt_GetSize()をローカル関数⇒グローバル関数に変更しました。
/*---------------------------------------------------------------------------*/
//SprFnt_Print()の描画処理の本体です。
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//static void SprFnt_Print_subr(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, double x, double y, double scale_x, double scale_y, int color, 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_Info stInfo;
//			//この文字の描画設定を取得する。
//			SprFnt_GetInfo(pSprFnt, c, &stInfo);
//			//この文字のスプライトがblankでなければ…
//			if(stInfo.SprNo) {
//				//スプライトを描画する。
//				(*fn)(user_data, x + (stInfo.DrawX * scale_x), y + (stInfo.DrawY * scale_y), scale_x, scale_y, color, stInfo.SprNo);
//			}
//			//列ストライドの分、次の描画位置を右へ進める。
//			x += stInfo.NextX * scale_x;
//			//現在の行に描いた文字の、行ストライドの最大値を更新する。
//			if(stInfo.NextY > h) { h = stInfo.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
//		}
//		s = mbsinc(s);
//	}
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
static void SprFnt_Print_subr(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x0, int y0, double scale_x, double scale_y, double rot, double cx, double cy, int color, const char* s) {
	int c, h = 0, x = 0, y = 0;
	//変換行列を作成する。					m=mat2f_1			┐
	//↑⑤ピボットが(x0,y0)に位置するように平行移動する。	mat2f_xlate(&m,x0,y0)		│
	//│④ピボットを中心に回転する。			mat2f_rot(&m,rot)		│この下で求める係数a00～a12は、左記の計算結果と同じ値になります。
	//│③ピボットを中心に拡大する。			mat2f_scale(&m,scale_x,scale_y)	│C#へ移植するために、clipflt1.cモジュールへの依存を避けるために、当関数内で展開しました。(C#版のclipflt1.cは未作成だから…)
	//│②ピボットが(0,0)に位置するように平行移動する。	mat2f_xlate(&m,-cx,-cy)		│
	//│①文字の位置に掛け合わせる。			vec2f_xform(&m,&v)		┘
	const double dCos = cos(rot), dSin = sin(rot);
	const double a00 = dCos * scale_x, a01 = -dSin * scale_y, a02 = x0 - (a00 * cx) - (a01 * cy);
	const double a10 = dSin * scale_x, a11 =  dCos * scale_y, a12 = y0 - (a10 * cx) - (a11 * cy);
	while((c = mbsnextc(s))) {
		if(c == '\n') {
			x = 0;
			y += h;
			h = 0;
		} else {
			ST_SprFnt_Info stInfo;
			//この文字の描画設定を取得する。
			SprFnt_GetInfo(pSprFnt, c, &stInfo);
			//この文字のスプライトがblankでなければ…
			if(stInfo.SprNo) {
				//スプライトを描画する。
				double dx = x + stInfo.DrawX, dy = y + stInfo.DrawY;
				double ex = (dx * a00) + (dy * a01) + a02;
				double ey = (dx * a10) + (dy * a11) + a12;
				(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color, stInfo.SprNo);
			}
			//列ストライドの分、次の描画位置を右へ進める。
			x += stInfo.NextX;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			if(stInfo.NextY > h) { h = stInfo.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
		s = mbsinc(s);
	}
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*---------------------------------------------------------------------------*/
//SprFnt_PrintXF()の描画処理の本体です。
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//static void SprFnt_PrintXF_subr(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, double x1, double y1, double x2, double y2, double scale_x, double scale_y, int color, const char* s1, const char* s2, double transition/*0.0～1.0*/) {
//	const double x10 = x1, x20 = x2, t1 = (1.0 - transition), t2 = transition;
//	const int color1 = RGBA_MAKE(RGBA_GETRED(color), RGBA_GETGREEN(color), RGBA_GETBLUE(color), RGBA_GETALPHA(color) * t1);
//	const int color2 = RGBA_MAKE(RGBA_GETRED(color), RGBA_GETGREEN(color), RGBA_GETBLUE(color), RGBA_GETALPHA(color) * t2);
//	int c1, c2, h1 = 0, h2 = 0;
//	for(;;) {
//		ST_SprFnt_Info stInfo1, stInfo2;
//		//文字列1について…
//		for(;;) {
//			//文字1を取得する。
//			if(!(c1 = mbsnextc(s1))) { break; }	//文字列1が終端ならばループを抜ける。
//			s1 = mbsinc(s1);
//			//文字1が改行文字でなければ…
//			if(c1 != '\n') {
//				//文字1の描画設定を取得する。
//				SprFnt_GetInfo(pSprFnt, c1, &stInfo1);
//				break;	//ループを抜ける。
//			}
//			//文字1が改行文字ならば…
//			x1 = x10;
//			y1 += h1 * scale_y;
//			h1 = 0;
//		}
//		//文字列2について…
//		for(;;) {
//			//文字2を取得する。
//			if(!(c2 = mbsnextc(s2))) { break; }	//文字列2が終端ならばループを抜ける。
//			s2 = mbsinc(s2);
//			//文字2が改行文字でなければ…
//			if(c2 != '\n') {
//				//文字2の描画設定を取得する。
//				SprFnt_GetInfo(pSprFnt, c2, &stInfo2);
//				break;	//ループを抜ける。
//			}
//			//文字2が改行文字ならば…
//			x2 = x20;
//			y2 += h2 * scale_y;
//			h2 = 0;
//		}
//		//文字列1も文字列2も終端ならば、終了する。
//		if(!c1 && !c2) { break; }	//ここまで
//		//文字列1が終端でなく,かつ,文字1のスプライトがblankでなければ…
//		if(c1 && stInfo1.SprNo) {
//			//文字列2が終端でなく,かつ,文字2のスプライトがblankでなければ…
//			if(c2 && stInfo2.SprNo) {
////{{2017/03/20変更:SprFnt_PrintXF_subr()において、補間処理の演算誤差で位置がぶれる問題を修正しました。
////				double x = (x1 * t1) + (x2 * t2);
////				double y = (y1 * t1) + (y2 * t2);
////↓2017/03/20変更:SprFnt_PrintXF_subr()において、補間処理の演算誤差で位置がぶれる問題を修正しました。
//				// * Mon Mar 20 22:44:17 JST 2017 Naoyuki Sawa
//				// - SprFnt_PrintXF_subr()にて、演算誤差で位置がぶれる問題を修正しました。
//				//   理論上は、常に補間処理を行っても同じ結果になるはずなのですが、実際には、演算誤差のために微妙にぶれが発生します。
//				//   特に、同じ座標同士で補間処理を行った場合に、文字の位置は変わらないはずなのに微妙にぶれるので目立ってしまいます。
//				//   実際の所、クロスフェードする文字同士の位置は同じ座標である事の方が圧倒的に多いので、この問題はかなり目立ちます。
//				//   そこで、この問題を回避するために、同じ座標である場合は、補間処理を行わずに、そのままの値を使う事にしました。
//				//   尚、異なる座標同士で補間処理を行う場合の演算誤差は対策していませんが、その場合はクロスフェード中に文字が移動するのに紛れて目立たないので問題無いと思います。
//				double x = (x1 == x2) ? x1 : ((x1 * t1) + (x2 * t2));
//				double y = (y1 == y2) ? y1 : ((y1 * t1) + (y2 * t2));
////}}2017/03/20変更:SprFnt_PrintXF_subr()において、補間処理の演算誤差で位置がぶれる問題を修正しました。
//				//文字1と文字2のスプライト番号が同じならば…
//				if(stInfo1.SprNo == stInfo2.SprNo) {
//					//文字1(文字2でも同じ)を、補間した位置に描く。
//					(*fn)(user_data, x  + (stInfo1.DrawX * scale_x), y  + (stInfo1.DrawY * scale_y), scale_x, scale_y, color , stInfo1.SprNo);
//				//文字1と文字2のスプライト番号が同じでなければ…
//				} else {
//					//文字1と文字2を、補間した位置に、クロスフェードして描く。
//					(*fn)(user_data, x  + (stInfo1.DrawX * scale_x), y  + (stInfo1.DrawY * scale_y), scale_x, scale_y, color1, stInfo1.SprNo);
//					(*fn)(user_data, x  + (stInfo2.DrawX * scale_x), y  + (stInfo2.DrawY * scale_y), scale_x, scale_y, color2, stInfo2.SprNo);
//				}
//			//文字列2が終端か,又は,文字2のスプライトがblankならば…
//			} else {
//					//文字1を、文字1の位置に、フェードアウトして描く。
//					(*fn)(user_data, x1 + (stInfo1.DrawX * scale_x), y1 + (stInfo1.DrawY * scale_y), scale_x, scale_y, color1, stInfo1.SprNo);
//			}
//		//文字列1が終端か,又は,文字1のスプライトがblankならば…
//		} else {
//			//文字列2が終端でなく,かつ,文字2のスプライトがblankでなければ…
//			if(c2 && stInfo2.SprNo) {
//					//文字2を、文字2の位置に、フェードインして描く。
//					(*fn)(user_data, x2 + (stInfo2.DrawX * scale_x), y2 + (stInfo2.DrawY * scale_y), scale_x, scale_y, color2, stInfo2.SprNo);
//			//文字列2が終端か,又は,文字2のスプライトがblankならば…
//			} else {
//					/** no job **/
//			}
//		}
//		//文字列1が終端でなければ…
//		if(c1) {
//			//列ストライドの分、次の描画位置を右へ進める。
//			x1 += stInfo1.NextX * scale_x;
//			//現在の行に描いた文字の、行ストライドの最大値を更新する。
//			if(stInfo1.NextY > h1) { h1 = stInfo1.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
//		}
//		//文字列2が終端でなければ…
//		if(c2) {
//			//列ストライドの分、次の描画位置を右へ進める。
//			x2 += stInfo2.NextX * scale_x;
//			//現在の行に描いた文字の、行ストライドの最大値を更新する。
//			if(stInfo2.NextY > h2) { h2 = stInfo2.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
//		}
//	}
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
static void SprFnt_PrintXF_subr(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x0, int y0, double scale_x, double scale_y, double rot, double cx1, double cy1, double cx2, double cy2, int color, const char* s1, const char* s2, double transition/*0.0～1.0*/) {
	const double t1 = (1.0 - transition), t2 = transition;
	const int color1 = RGBA_MAKE(RGBA_GETRED(color), RGBA_GETGREEN(color), RGBA_GETBLUE(color), RGBA_GETALPHA(color) * t1);
	const int color2 = RGBA_MAKE(RGBA_GETRED(color), RGBA_GETGREEN(color), RGBA_GETBLUE(color), RGBA_GETALPHA(color) * t2);
	int c1, c2, h1 = 0, x1 = 0, y1 = 0, h2 = 0, x2 = 0, y2 = 0;
	//変換行列を作成する。					m=mat2f_1			┐
	//↑⑤ピボットが(x0,y0)に位置するように平行移動する。	mat2f_xlate(&m,x0,y0)		│
	//│④ピボットを中心に回転する。			mat2f_rot(&m,rot)		│この下で求める係数a00～a12は、左記の計算結果と同じ値になります。
	//│③ピボットを中心に拡大する。			mat2f_scale(&m,scale_x,scale_y)	│C#へ移植するために、clipflt1.cモジュールへの依存を避けるために、当関数内で展開しました。(C#版のclipflt1.cは未作成だから…)
	//│②ピボットが(0,0)に位置するように平行移動する。	mat2f_xlate(&m,-cx,-cy)		│
	//│①文字の位置に掛け合わせる。			vec2f_xform(&m,&v)		┘
	const double dCos = cos(rot), dSin = sin(rot);
	const double cx = (cx1 == cx2) ? cx1 : ((cx1 * t1) + (cx2 * t2));
	const double cy = (cy1 == cy2) ? cy1 : ((cy1 * t1) + (cy2 * t2));
	const double a00 = dCos * scale_x, a01 = -dSin * scale_y, a02 = x0 - (a00 * cx) - (a01 * cy);
	const double a10 = dSin * scale_x, a11 =  dCos * scale_y, a12 = y0 - (a10 * cx) - (a11 * cy);
	for(;;) {
		ST_SprFnt_Info stInfo1, stInfo2;
		double dx, dy, ex, ey;
		//文字列1について…
		for(;;) {
			//文字1を取得する。
			if(!(c1 = mbsnextc(s1))) { break; }	//文字列1が終端ならばループを抜ける。
			s1 = mbsinc(s1);
			//文字1が改行文字でなければ…
			if(c1 != '\n') {
				//文字1の描画設定を取得する。
				SprFnt_GetInfo(pSprFnt, c1, &stInfo1);
				break;	//ループを抜ける。
			}
			//文字1が改行文字ならば…
			x1 = 0;
			y1 += h1;
			h1 = 0;
		}
		//文字列2について…
		for(;;) {
			//文字2を取得する。
			if(!(c2 = mbsnextc(s2))) { break; }	//文字列2が終端ならばループを抜ける。
			s2 = mbsinc(s2);
			//文字2が改行文字でなければ…
			if(c2 != '\n') {
				//文字2の描画設定を取得する。
				SprFnt_GetInfo(pSprFnt, c2, &stInfo2);
				break;	//ループを抜ける。
			}
			//文字2が改行文字ならば…
			x2 = 0;
			y2 += h2;
			h2 = 0;
		}
		//文字列1も文字列2も終端ならば、終了する。
		if(!c1 && !c2) { break; }	//ここまで
		//文字列1が終端でなく,かつ,文字1のスプライトがblankでなければ…
		if(c1 && stInfo1.SprNo) {
			//文字列2が終端でなく,かつ,文字2のスプライトがblankでなければ…
			if(c2 && stInfo2.SprNo) {
				// * Mon Mar 20 22:44:17 JST 2017 Naoyuki Sawa
				// - SprFnt_PrintXF_subr()にて、演算誤差で位置がぶれる問題を修正しました。
				//   理論上は、常に補間処理を行っても同じ結果になるはずなのですが、実際には、演算誤差のために微妙にぶれが発生します。
				//   特に、同じ座標同士で補間処理を行った場合に、文字の位置は変わらないはずなのに微妙にぶれるので目立ってしまいます。
				//   実際の所、クロスフェードする文字同士の位置は同じ座標である事の方が圧倒的に多いので、この問題はかなり目立ちます。
				//   そこで、この問題を回避するために、同じ座標である場合は、補間処理を行わずに、そのままの値を使う事にしました。
				//   尚、異なる座標同士で補間処理を行う場合の演算誤差は対策していませんが、その場合はクロスフェード中に文字が移動するのに紛れて目立たないので問題無いと思います。
				double x = (x1 == x2) ? x1 : ((x1 * t1) + (x2 * t2));
				double y = (y1 == y2) ? y1 : ((y1 * t1) + (y2 * t2));
				//文字1と文字2のスプライト番号が同じならば…
				if(stInfo1.SprNo == stInfo2.SprNo) {
					//文字1(文字2でも同じ)を、補間した位置に描く。
					dx = x  + stInfo1.DrawX, dy = y  + stInfo1.DrawY;
					ex = (dx * a00) + (dy * a01) + a02;
					ey = (dx * a10) + (dy * a11) + a12;
					(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color , stInfo1.SprNo);
				//文字1と文字2のスプライト番号が同じでなければ…
				} else {
					//文字1と文字2を、補間した位置に、クロスフェードして描く。
					dx = x  + stInfo1.DrawX, dy = y  + stInfo1.DrawY;
					ex = (dx * a00) + (dy * a01) + a02;
					ey = (dx * a10) + (dy * a11) + a12;
					(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color1, stInfo1.SprNo);
					dx = x  + stInfo2.DrawX, dy = y  + stInfo2.DrawY;
					ex = (dx * a00) + (dy * a01) + a02;
					ey = (dx * a10) + (dy * a11) + a12;
					(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color2, stInfo2.SprNo);
				}
			//文字列2が終端か,又は,文字2のスプライトがblankならば…
			} else {
					//文字1を、文字1の位置に、フェードアウトして描く。
					dx = x1 + stInfo1.DrawX, dy = y1 + stInfo1.DrawY;
					ex = (dx * a00) + (dy * a01) + a02;
					ey = (dx * a10) + (dy * a11) + a12;
					(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color1, stInfo1.SprNo);
			}
		//文字列1が終端か,又は,文字1のスプライトがblankならば…
		} else {
			//文字列2が終端でなく,かつ,文字2のスプライトがblankでなければ…
			if(c2 && stInfo2.SprNo) {
					//文字2を、文字2の位置に、フェードインして描く。
					dx = x2 + stInfo2.DrawX, dy = y2 + stInfo2.DrawY;
					ex = (dx * a00) + (dy * a01) + a02;
					ey = (dx * a10) + (dy * a11) + a12;
					(*fn)(user_data, ex, ey, scale_x, scale_y, rot, color2, stInfo2.SprNo);
			//文字列2が終端か,又は,文字2のスプライトがblankならば…
			} else {
					/** no job **/
			}
		}
		//文字列1が終端でなければ…
		if(c1) {
			//列ストライドの分、次の描画位置を右へ進める。
			x1 += stInfo1.NextX;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			if(stInfo1.NextY > h1) { h1 = stInfo1.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
		//文字列2が終端でなければ…
		if(c2) {
			//列ストライドの分、次の描画位置を右へ進める。
			x2 += stInfo2.NextX;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			if(stInfo2.NextY > h2) { h2 = stInfo2.NextY; }	//scale_yを掛ける処理は改行時に行うのでここでは行わない。
		}
	}
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*****************************************************************************
 *	グローバル関数
 *****************************************************************************/
//スプライトフォントを使用して、文字列を描画する。
//[in]
//	pSprFnt			スプライトフォントのレジストリキー		REG_open_key_l(TBL_RegTbl,RegKey_SprFnt,RegKey_スプライトフォントラベル,-1)
//	fn			アプリケーション定義のスプライト描画関数
//	user_data		アプリケーション定義のスプライト描画関数に引き渡す、アプリケーション定義の任意のデータ
//	x,y			基準座標
//	scale_x,scale_y		拡大率
//	ax,ay			アライメント					0.0=左揃え/上揃え,0.5=中揃え,1.0=右揃え/下揃え。左記以外の値も指定可能です。
//	color			カラー						SprFnt_Print()/SprFnt_PrintV()/SprFnt_PrintF()は、カラーの値を解釈せずに、そのままアプリケーション定義のスプライト描画関数に引き渡します。
//										SprFnt_PrintXF()は、カラーの値がDirect3D互換のD3DCOLOR形式であると想定して、アルファ値を補間します。
//										従って、SprFnt_PrintXF()を使用しない場合はカラーの値は任意の形式で構いませんが、SprFnt_PrintXF()を使用する場合はDirect3D互換のD3DCOLOR形式として下さい。
//	s			文字列
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//void SprFnt_Print(  const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double ax, double ay, int color, const char* s) {
//	int size_x, size_y;
//	SprFnt_GetSize(pSprFnt, s, &size_x, &size_y);
//	x -= (size_x * scale_x * ax);
//	y -= (size_y * scale_y * ay);
//	SprFnt_Print_subr(pSprFnt, fn, user_data, x, y, scale_x, scale_y, color, s);
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
void SprFnt_Print(  const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double rot, double ax, double ay, int color, const char* s) {
	int size_x, size_y;
	double cx, cy;
	//文字列のサイズを求める。
	SprFnt_GetSize(pSprFnt, s, &size_x, &size_y);
	//文字列のピボットを求める。
	cx = size_x * ax;
	cy = size_y * ay;
	//文字列を描画する。
	SprFnt_Print_subr(pSprFnt, fn, user_data, x, y, scale_x, scale_y, rot, cx, cy, color, s);
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*---------------------------------------------------------------------------*/
//スプライトフォントを使用して、書式付き文字列を描画する。
//[in]
//	- SprFnt_Print()の説明を参照して下さい。
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//void SprFnt_PrintV( const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double ax, double ay, int color, const char* fmt, va_list ap) {
//	char* s = strdup_vprintf(fmt, ap);
//	SprFnt_Print(pSprFnt, fn, user_data, x, y, scale_x, scale_y, ax, ay, color, s);
//	free(s);
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
void SprFnt_PrintV( const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double rot, double ax, double ay, int color, const char* fmt, va_list ap) {
	char* s = strdup_vprintf(fmt, ap);
	SprFnt_Print(pSprFnt, fn, user_data, x, y, scale_x, scale_y, rot, ax, ay, color, s);
	free(s);
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*---------------------------------------------------------------------------*/
//スプライトフォントを使用して、書式付き文字列を描画する。
//[in]
//	- SprFnt_Print()の説明を参照して下さい。
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//void SprFnt_PrintF( const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double ax, double ay, int color, const char* fmt, ...) {
//	va_list ap;
//	va_start(ap, fmt);
//	SprFnt_PrintV(pSprFnt, fn, user_data, x, y, scale_x, scale_y, ax, ay, color, fmt, ap);
//	va_end(ap);
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
void SprFnt_PrintF( const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double rot, double ax, double ay, int color, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	SprFnt_PrintV(pSprFnt, fn, user_data, x, y, scale_x, scale_y, rot, ax, ay, color, fmt, ap);
	va_end(ap);
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*---------------------------------------------------------------------------*/
//文字列s1とs2を、混合率(1.0-transition):transitionで、各文字の位置を補間しながら、クロスフェードして描く。
//[in]
//	- SprFnt_Print()の説明を参照して下さい。
//[note]
//	- 文字列s1とs2の文字数は異なっていても構いませんが、お互いの一文字目から混合して行くので、同じ文字数である事が望ましいです。
//	  文字数が違っていても、左揃え(ax=0.0)ならば自然に表示されますが、右揃え(ax=1.0)や中揃え(ax=0.5)だと、意図と違う描画結果になる恐れが有ります(動作はしますが)。
//{{2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
//void SprFnt_PrintXF(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double ax, double ay, int color, const char* s1, const char* s2, double transition/*0.0～1.0*/) {
//	int size_x, size_y, x1, y1, x2, y2;
//	//文字列1の描画位置を求める。
//	SprFnt_GetSize(pSprFnt, s1, &size_x, &size_y);
//	x1 = x - (size_x * scale_x * ax);
//	y1 = y - (size_y * scale_y * ay);
//	//文字列2の描画位置を求める。
//	SprFnt_GetSize(pSprFnt, s2, &size_x, &size_y);
//	x2 = x - (size_x * scale_x * ax);
//	y2 = y - (size_y * scale_y * ay);
//	//文字列1と文字列2をクロスフェードして描く。
//	SprFnt_PrintXF_subr(pSprFnt, fn, user_data, x1, y1, x2, y2, scale_x, scale_y, color, s1, s2, transition);
//}
//↓2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
void SprFnt_PrintXF(const void* pSprFnt, void (*fn)(void* user_data, int x, int y, double scale_x, double scale_y, double rot, int color, int iSprNo), void* user_data, int x, int y, double scale_x, double scale_y, double rot, double ax, double ay, int color, const char* s1, const char* s2, double transition/*0.0～1.0*/) {
	int size_x1, size_y1, size_x2, size_y2;
	double cx1, cy1, cx2, cy2;
	//文字列1のサイズを求める。
	SprFnt_GetSize(pSprFnt, s1, &size_x1, &size_y1);
	//文字列2のサイズを求める。
	SprFnt_GetSize(pSprFnt, s2, &size_x2, &size_y2);
	//片方のサイズが空ならば、もう一方のサイズに合わせる。(両方とも空ならば空のままで構いません。)
	// - 片方の文字列が空文字列だった場合に、もう片方の文字列だけをフェードインして描くためです。
	if(!(size_x1 | size_y1)) { size_x1 = size_x2, size_y1 = size_y2; }
	if(!(size_x2 | size_y2)) { size_x2 = size_x1, size_y2 = size_y1; }
	//文字列1のピボットを求める。
	cx1 = size_x1 * ax;
	cy1 = size_y1 * ay;
	//文字列2のピボットを求める。
	cx2 = size_x2 * ax;
	cy2 = size_y2 * ay;
	//文字列1と文字列2をクロスフェードして描く。
	SprFnt_PrintXF_subr(pSprFnt, fn, user_data, x, y, scale_x, scale_y, rot, cx1, cy1, cx2, cy2, color, s1, s2, transition);
}
//}}2017/04/04変更:スプライトフォント描画に回転機能を追加しました。
/*---------------------------------------------------------------------------*/
//{{2017/04/19移動:SprFnt_GetSize()をローカル関数⇒グローバル関数に変更しました。
//文字列のサイズを取得する。
//[in]
//	pSprFnt			スプライトフォントのレジストリキー		REG_open_key_l(TBL_RegTbl,RegKey_SprFnt,RegKey_スプライトフォントラベル,-1)
//	s			文字列
//	p_size_x,p_size_y	サイズを格納する変数へのポインタ
//[note]
//	- 当関数には、拡大率や回転のパラメータは有りません。
//	  当関数を使用して文字列のサイズを取得し、サイズを考慮して実際の描画位置を決定する場合は、呼び出し側にて拡大率や回転を考慮して下さい。
//	  具体例は、SprFnt_Print_subr()の実装を参考にして下さい。
//	  実際の所、ほとんどのケースではSprFnt_Print()やSprFnt_PrintF()で足りると思うので、アプリケーションが当関数を呼び出して明示的に文字列のサイズを取得する必要は少ないと思いますが、
//	  もし、SprFnt_Print()やSprFnt_PrintF()では想定していない方法で文字列のサイズを描画位置に反映したい場合のために、当関数もグローバル関数としました。
void SprFnt_GetSize(const void* pSprFnt, const char* s, /*[out]*/ int* p_size_x, /*[out]*/ int* p_size_y) {
	//アルゴリズムは概ね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_Info stInfo;
			//この文字の描画設定を取得する。
			SprFnt_GetInfo(pSprFnt, c, &stInfo);
			//列ストライドの分、次の描画位置を右へ進める。
			x += stInfo.NextX;
			//現在の行に描いた文字の、行ストライドの最大値を更新する。
			if(stInfo.NextY > h) { h = stInfo.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;
}
//}}2017/04/19移動:SprFnt_GetSize()をローカル関数⇒グローバル関数に変更しました。
