ページ 11

文字幅について

Posted: 2013年9月26日(木) 08:09
by Rittai_3D
お久しぶりです。3Dです。今回は文字幅についての質問です。

現在、C++が以前より使えるようになったので、また性懲りもなく東方ProjectのMusicRoom の再現をしています。その事で質問があります。

それは、全角文字と半角文字の入り交じった文を表示する時、DrawExtendFormatStringToHandleを使用して表示すると拡大率が固定なので、たとえば、半角なら拡大率を0.86f、漢字やひらがな、カタカナの場合は拡大率を1.0fにしたい時にどうすれば良いのかわかしません。自分で関数を作ってみたものの、これだと一文字目が半角だと拡大率が0.86f固定に、逆に一文字目が全角なら拡大率が1.0f固定になってしまいます。

長ったらしく書きましたが、要するに文字列の中の全角文字と半角文字の拡大率を変て表示する方法です。

もし、Dxライブラリでは不可能と言うのならばDirectXやWin32APIなどでの方法を教えて頂けると幸いです。

自作関数

コード:

bool IsJapaneseChar( unsigned char code )
{
	if( ( code >= 0x81 && code <= 0x9F ) ||
		( code >= 0xE0 && code <= 0xFC ) ) {
			return true;
	}
	return false;
}

void DrawSubStringToHandle( int x, int y, int color, int FontHdl,char* String, ... )
{
	va_list args;
	va_start( args, String );

	char str[1024];
	int i;
	double ExRate_X = 0.0f;	// 文字の拡大率

	for( i=0 ; String[i] != '\0' ; i++ ) {
		if( IsJapaneseChar( String[i] ) == true ) {
			ExRate_X = 1.0f;
		} else {
			ExRate_X = 0.86f;
		}
	}

	vsnprintf_s( str, sizeof( str ), _TRUNCATE, String, args );

	DrawExtendFormatStringToHandle( x, y, ExRate_X, 1.0f, color, FontHdl, str );
}

サンプルコードです

コード:

// サンプルコードには上の自作関数は使用しておりません。
#include "DxLib.h"

bool ProcessLoop()
{
	if( ClearDrawScreen() != 0 ) return false;
	if( ProcessMessage()  != 0 ) return false;
	if( CheckHitKey( KEY_INPUT_ESCAPE ) ) return false;
	return true;
}

typedef struct _TAG_TALK
{
	char Aisatu[3][256];
}talk_t;

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );

	int Font = CreateFontToHandle( "MSPゴシック", 16, 0, DX_FONTTYPE_ANTIALIASING_EDGE );

	talk_t talk = { "No.1 お早うございます", "No.2 今日は", "No.3 今晩は" }; // 挨拶を登録

	while( ProcessLoop() ) {

		for( int i=0 ; i<3 ; i++ ) {
			SetDrawMode( DX_DRAWMODE_BILINEAR );
			DrawExtendFormatStringToHandle( 63, 95+20*i, 0.86f, 1.0f, GetColor( 255,255,255 ), Font,"%s", talk.Aisatu[i] );// ここ!
			SetDrawMode( DX_DRAWMODE_NEAREST );
		}

		ScreenFlip();
	}

	DxLib_End();
	return 0;
}




【開発環境】

 Windows XP
 Visual C++ Express 2008
 Dxライブラリ

Re: 文字幅について

Posted: 2013年9月26日(木) 08:51
by みけCAT
遅いかもしれないですが、単純な方法としては全角文字と半角文字を分けて描画することが考えられます。

コード:

#include "DxLib.h"

char Key[256];

void drawMazattaString(int x,int y,int Color,int FontHandle,const TCHAR *String,
		double ExRateX_han,double ExRateX_zen,
		double ExRateY_han=1.0,double ExRateY_zen=1.0) {
	TCHAR* buffer=new TCHAR[lstrlen(String)+1]; // 描画用バッファ
	int bufferLen=0; // 描画用バッファの長さ
	bool hankakuNow=false; // 今描画用バッファに入っているのが半角文字か
	int nowLength=0; // 今見ている文字が使う配列の要素数
	for(int i=0;String[i];i+=nowLength) {
		bool thisIsHankaku; // 今見ている文字が半角か
#ifdef UNICODE
		// 半角カタカナ?カタカナの一種だろ
		thisIsHankaku=(String[i]<0x7F);
		nowLength=1;
#else
		// どうせShift_JISなんだろ?
		unsigned char check[2]={String[i],String[i+1]};
		if(check[1]!=0 && (
					(check[0]>=0x81 && check[0]<=0x9F) ||
					(check[0]>=0xE0 && check[0]<=0xFE)
				) && (check[1]>=0x40 && check[1]<=0xFC && check[1]!=0x7F)) {
			thisIsHankaku=false;
			nowLength=2;
		} else {
			thisIsHankaku=true;
			nowLength=1;
		}
#endif
		if(hankakuNow!=thisIsHankaku) {
			if(bufferLen>0) {
				buffer[bufferLen]=0;
				DrawExtendStringToHandle(x,y,
					hankakuNow?ExRateX_han:ExRateX_zen,
					hankakuNow?ExRateY_han:ExRateY_zen,
					buffer,Color,FontHandle);
				x+=GetDrawExtendStringWidthToHandle(
					hankakuNow?ExRateX_han:ExRateX_zen,
					buffer,lstrlen(buffer),FontHandle);
			}
			bufferLen=0;
		}
		for(int j=0;j<nowLength;j++)buffer[bufferLen+j]=String[i+j];
		bufferLen+=nowLength;
		hankakuNow=thisIsHankaku;
	}
	if(bufferLen>0) {
		buffer[bufferLen]=0;
		DrawExtendStringToHandle(x,y,
			hankakuNow?ExRateX_han:ExRateX_zen,
			hankakuNow?ExRateY_han:ExRateY_zen,
			buffer,Color,FontHandle);
		x+=GetDrawExtendStringWidthToHandle(
			hankakuNow?ExRateX_han:ExRateX_zen,
			buffer,lstrlen(buffer),FontHandle);
	}
	delete[] buffer;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理
	SetDrawScreen( DX_SCREEN_BACK );        //裏画面に設定

	int aFont=CreateFontToHandle(TEXT("MS 明朝"),20,-1,-1);
	int clWhite=GetColor(255,255,255);
	const TCHAR* theText=TEXT("伊集院メイ(CV:田村ゆかり)");

	while(!ProcessMessage() && !ClearDrawScreen() && !GetHitKeyStateAll( Key ) && !Key[KEY_INPUT_ESCAPE]){
		//↑メッセージ処理         ↑画面をクリア          ↑キーボード入力状態取得       ↑ESCが押されていない

		//ココに処理を書いていく
		drawMazattaString(30,30,clWhite,aFont,theText,0.86,1.0);
		drawMazattaString(30,70,clWhite,aFont,theText,1.0,0.86);
		drawMazattaString(30,110,clWhite,aFont,theText,2.0,1.0);
		drawMazattaString(30,150,clWhite,aFont,theText,1.0,2.0);

		ScreenFlip();//裏画面を表画面に反映
	}

	DxLib_End();
	return 0;
}
※UNICODE版はDXライブラリ(3.10a)のヘッダでエラーが出たのでテストしていません

書式は事前にwsprintfやsprintf(実数を使いたい場合)などで変換してからdrawMazattaString関数に渡してください。

Re: 文字幅について

Posted: 2013年9月26日(木) 11:04
by softya(ソフト屋)
DirectXでもDXライブラリでもWin32APIでも特に変わりないです。
みけCATさんの書かれたような感じで一文字づつ判断して半角/全角を分けて処理するしかないでしょう。
世の中にある気の利いた表示なんてのはAPIにあるんじゃなくて大抵はアプリ側が処理しています。
と言うことでがんばりましょう。

Re: 文字幅について

Posted: 2013年9月26日(木) 15:59
by Rittai_3D
返信ありがとうございます。

>みけCAT様

サンプルコードありがとうございます。

早速コンパイルして動きを見させて頂きます。

>softya様


やはりそのような都合のいい関数やAPIはないですか…

一文字ずつ判定は思いつきましたが、実装が出来ずに諦めてしまいました。

Re: 文字幅について

Posted: 2013年9月26日(木) 16:11
by usao
判定の実装が{つらい/面倒/そこまでしたくない/わからん}→「人間があらかじめ判定しておく」という妥協は……ダメ?

"aaaあああiiいいい9"
 ↓ 手動でこう書いちゃう
{ "aaa", "あああ", "ii", "いいい", "9" } (+最初の文字列が半角か全角かを示すboolか何か)

Re: 文字幅について

Posted: 2013年9月27日(金) 18:54
by Rittai_3D
>>usao様

返信ありがとうございます。
usao さんが書きました:判定の実装が{つらい/面倒/そこまでしたくない/わからん}→「人間があらかじめ判定しておく」という妥協は……ダメ?

"aaaあああiiいいい9"
 ↓ 手動でこう書いちゃう
{ "aaa", "あああ", "ii", "いいい", "9" } (+最初の文字列が半角か全角かを示すboolか何か)
出来れば妥協はしたくないのですが、どうしてもというならば諦めて妥協します。
オフトピック
こっからは独り言です。
► スポイラーを表示

Re: 文字幅について

Posted: 2013年9月27日(金) 18:58
by Rittai_3D
みけCAT様のサンプルを動かしてみて自分の考えていた動作をしていたのでそれを元に色々改造してみたいと思います。

dxライブラリにはまだまだ知らない関数がいっぱいあるのですね。驚きました。

というわけで、自分の疑問は解消したのでこれにて解決とさせていただきます。

回答して下さった皆様、本当にありがとうございました。