ページ 11

フォント異常

Posted: 2010年4月03日(土) 23:10
by シノニム
いつもお世話になっております。今回はプログラム実行中に起きるバグについて質問させていただきます

内部の変数を表示する関数が学校のライブラリについていたため、それを使用したんですが、フォントのサイズを変更する機能をつけたかったので、少し自分なりにアレンジしてみたんですが
//------------------------------------------------------------------------
//    ■出力フォーマット指定可能な文字列の描画    // 2009.06.07 追加
//     ※表示位置,文字色の指定あり.変数の値を表示することができる.
//        下記は文字色が入力されなかった場合
//
//        宣 言)    bool DrawPrintf(char font, int x, int y, int size, D3DCOLOR color, char *szFormat, ...);
//
//        引 数)    x, y        : 表示位置
//                size        : 文字のサイズ
//                color        : 文字列の色
//                szFormat    : 出力書式
//                ...            : 変数の並び
//
//        戻り値)    生成された文字数(終端の null 文字は含まれません)
//                (エラー時:負の値)
//------------------------------------------------------------------------
int    DrawPrintf(int x, int y, int size, D3DCOLOR color, char *szFormat, ...)
{
    char    szText[1024];    // 描画文字列
    RECT    rect;            // 矩形    
    int        nLength;        // 文字数

    // 文字列の生成
    va_list        va;

    va_start( va, szFormat );
    nLength = vsprintf( szText, szFormat, va);
    va_end( va );

    // フォントの初期化
    D3DXFONT_DESC fd;

    fd.Height = size;
    fd.Width = 0;
    fd.Weight = 0;
    fd.MipLevels = 0;
    fd.Italic = FALSE;
    fd.CharSet = SHIFTJIS_CHARSET;
    fd.OutputPrecision = 0;
    fd.Quality = 0;
    fd.PitchAndFamily = FIXED_PITCH;
    strcpy(fd.FaceName, "MS ゴシック");
    
    D3DXCreateFontIndirect( g_pd3dDevice, &fd, &g_pD3DXFont);

    // 文字列の描画
    SetRect( &rect, x, y, WINDOW_WIDTH, WINDOW_HEIGHT );
    g_pD3DXFont->DrawText( NULL, szText, -1, &rect, DT_LEFT, color );
    
    // 戻すためにまた初期化(メモリ関係で毎回解放する)
    D3ReleaseFont();
    D3InitFont();    

    return nLength;
}
1、変数の定義
2、文字列の生成
3、フォントのサイズを変更(フォント情報の初期化。多分いらない初期化もある)
4、文字列の描写
5、メモリの解放
6、フォントまた定義(?)

という手順を関数内で踏んでいます。
5が無くても実行はできたんですが、メモリ消費量がちょっとシャレにならなかったので追加してみたところ、重くなる問題はクリアできたのでそれ以降追加してあります

そして今回の質問の本題なんですが、何回かループを繰り返すとフォントがおかしくなってしまいます
具体的には、フォントの大きさが戻ってしまうんですが、初めの5秒間くらいは要望通りに表示してくれているため、対処に困っています

OSはXP、言語はC++です
どなたかお力添えのほど、よろしくお願いします

Re:フォント異常

Posted: 2010年4月04日(日) 02:30
by Justy
 なんとなく解放忘れな気がしてならないのですが、それも D3ReleaseFontと D3InitFontの
中身次第ですね。
 g_pD3DXFontの変数は新規に作成したものですか? 既存のもので D3InitFontなどで初期化されている
ものですか?

 とりあえず、D3DXCreateFontIndirectの戻り値を確認して、その数秒たってフォントの大きさが
戻ったときこの戻り値がどうなっているか確認してみて下さい。

Re:フォント異常

Posted: 2010年4月04日(日) 21:29
by シノニム
-> Justyさん

返答ありがとうございます
g_pD3DXFontはライブラリでグローバル変数として定義されているポインタです

/// 定義 ///
LPD3DXFONT g_pD3DXFont; // D3DXフォント

-> D3ReleaseFontとD3InitFontの中身次第
下記にあるのが中身です

//-----------------------------------------------------------------------------
// フォントの初期化
//-----------------------------------------------------------------------------
void D3InitFont(void)
{
    // D3DXフォント作成
    D3DXFONT_DESC fd;

    fd.Height = 16;
    fd.Width = 0;
    fd.Weight = 0;
    fd.MipLevels = 0;
    fd.Italic = FALSE;
    fd.CharSet = SHIFTJIS_CHARSET;
    fd.OutputPrecision = 0;
    fd.Quality = 0;
    fd.PitchAndFamily = FIXED_PITCH;
    strcpy(fd.FaceName, "MS ゴシック");
    
    D3DXCreateFontIndirect( g_pd3dDevice, &fd, &g_pD3DXFont);

}
    
//-----------------------------------------------------------------------------
// フォントの解放
//-----------------------------------------------------------------------------
void D3ReleaseFont(void)
{
    if( g_pD3DXFont != NULL )  g_pD3DXFont->Release();
    //if( g_pd3dDevice != NULL ) g_pd3dDevide->
}
-> とりあえず、D3DXCreateFontIndirectの戻り値を確認して
お恥ずかしながら調べ方が分かりません・・・

int test = D3DXCreateFontIndirect( g_pd3dDevice, &fd, &g_pD3DXFont);
char test[/url] = D3DXCreateFontIndirect( g_pd3dDevice, &fd, &g_pD3DXFont);

上記の2つでダメだったんですが、どこが間違ってるんですかね?

Re:フォント異常

Posted: 2010年4月04日(日) 22:38
by Justy
>お恥ずかしながら調べ方が分かりません・・・
 そういう時は検索しましょう。

D3DXCreateFontIndirect
http://msdn.microsoft.com/ja-jp/library ... S.85).aspx

 戻り値は HRESULT型です。


 さて、コードを拝見したところ概ね原因はわかりました。
 やはり解放ミスです。

 D3InitFont()で D3DXCreateFontIndirect関数を使って g_pD3DXFontに対して
フォントオブジェクトを作成しています。
 本来はこのまま g_pD3DXFontを使用し、アプリが終了する段階で D3ReleaseFont関数で解放する、
という流れ(作成と解放は1対1)になっているところを DrawPrintf関数が間に入ることで

1  アプリ起動時 D3InitFont関数内で g_pD3DXFont作成 フォントオブジェクト数 +1
2a DrawPrintf関数内 - D3DXCreateFontIndirect > g_pD3DXFont作成 フォントオブジェクト数 +1
2b DrawPrintf関数内 - DrawText > g_pD3DXFontを使って文字列表示
2c DrawPrintf関数内 - D3ReleaseFont > g_pD3DXFont解放 フォントオブジェクト数 -1
2d DrawPrintf関数内 - D3InitFont > g_pD3DXFont作成 フォントオブジェクト数 +1
3 アプリ終了時 D3ReleaseFont関数内で _pD3DXFont解放 フォントオブジェクト数 -1

という流れになり、2a~2dは DrawPrintfが呼ばれる度に繰り返されます。

 どこがおかしいのか(解放が1つ足らない)は一目瞭然ですね。
 結果、フォントオブジェクトがどんどん作成され、数秒後限界にきた為、
D3DXCreateFontIndirect関数が失敗し、前に2dで初期化された時のフォントオブジェクトが
そのまま使用された為文字が元の大きさに戻ってしまったのでしょう。
 
 
 で、解決策ですが(足らない分の解放を)作成する前にする、というのも手ではありますが
DrawPrintf関数で g_pD3DXFontを使わない、という方法が良さそうです。


int DrawPrintf(int x, int y, int size, D3DCOLOR color, char *szFormat, ...)
{
..(略)...

LPD3DXFONT hfont;
HRESULT hr = D3DXCreateFontIndirectA( g_pD3DDevice, &fd, &hfont);
if(hr == D3D_OK)
{
SetRect( &rect, x, y, 640, 480 );
hfont->DrawTextA( NULL, szText, -1, &rect, DT_LEFT, color );
hfont->Release();
return nLength;
}
return -1;
}

 g_pD3DXFontを使ってしまうと、諸々初期化と解放を2回ずつ行わなければならなくなりますが、
この方法なら1回ずつで済みます。

 ただ、この方法にしても DrawPrintf関数が呼ばれる度にフォントオブジェクトを作成していたのでは
あまりにも効率が悪すぎます。

 文字サイズの種類がある程度絞られるなら、事前に(g_pD3DXFontのように)必要なサイズ分の
フォントオブジェクトを作成しておき、サイズに応じて使い分けるなど工夫した方がいいでしょう。

 以下はコードのイメージです。

[color=#d0d0ff" face="monospace"><hr width="60%" align="left" color="#101010]enum UserFontSize
{
UserFontSize_9,
UserFontSize_12,
UserFontSize_14,
UserFontSize_18,
UserFontSize_22
UserFontSize_Max
};

LPD3DXFONT g_pD3DXUserFont[UserFontSize_Max]; // どこかで D3DXCreateFontIndirectを使って全て初期化しておく

int DrawPrintf(int x, int y, UserFontSize size, D3DCOLOR color, const char *szFormat, ...)
{
...
g_pD3DXUserFont[size]->DrawTextA( NULL, szText, -1, &rect, DT_LEFT, color );
...
}
[/color]

Re:フォント異常

Posted: 2010年4月05日(月) 00:42
by シノニム
-> Justyさん

非常に理解しやすいご返答感謝します
なるほどJustyさんがおっしゃってた通り解放処理が足りなかったわけですか
msdn見たんですけど、あまり利用しない方なので見方が分からなく、ご迷惑おかけしました

>>ただ、この方法にしても DrawPrintf関数が呼ばれる度にフォントオブジェクトを作成していたのでは
あまりにも効率が悪すぎます。

 文字サイズの種類がある程度絞られるなら、事前に(g_pD3DXFontのように)必要なサイズ分の
フォントオブジェクトを作成しておき、サイズに応じて使い分けるなど工夫した方がいいでしょう。 >>

たしかにおっしゃることは理解できるんですが、実際リリース版には反映されないし、なるべく自由度の高い関数が好みなので、深刻な問題にならない限りこのスタイルで行こうと思ってます(実はフォントも弄れるようにするつもりでした)。生意気ですいません


一応反映された結果として、
int    DrawPrintf(int x, int y, int size, D3DCOLOR color, char *szFormat, ...)
{
    char    szText[1024];    // 描画文字列
    RECT    rect;            // 矩形    
    int        nLength;        // 文字数

    // 文字列の生成
    va_list        va;

    va_start( va, szFormat );
    nLength = vsprintf( szText, szFormat, va);
    va_end( va );

    // フォントの初期化
    LPD3DXFONT hfont; 
    HRESULT hr = D3DXCreateFontIndirectA( g_pd3dDevice, &fd, &hfont); 
    if(hr == D3D_OK) 
    { 
        SetRect( &rect, x, y, 640, 480 ); 
        hfont->DrawTextA( NULL, szText, -1, &rect, DT_LEFT, color ); 
        hfont->Release(); 
        return nLength; 
    } 
    else{
        MessageBox(NULL, "D3D_OK以外の値が返されました","エラー",MB_OK);
        PostQuitMessage( 0 );}
    return 0;
}
このような状態になっているんですが、これどこにサイズ変更の命令をいれればいいんですかね・・・?
それとまた別の問題なんですが、どうも描写されたりされなかったりする部分があるみたいで
フォント関係に触れるのが初めてなのでかなり単純なことを聞いている感が否めないのですが、なにとぞよろしくお願いします

Re:フォント異常

Posted: 2010年4月05日(月) 01:31
by Justy
 元の DrawPrintfと比較すると判りますが、D3DXFONT_DESCの宣言から fd.FaceNameへの
フォント名の設定までがそっくり消えていますよね。
 
 g_pD3DXFontを使わないで新規にフォントオブジェクトを作って描画した後破棄する、
というところ以外は元のままでいいですよ(SetRectのところとか)

Re:フォント異常

Posted: 2010年4月05日(月) 10:17
by シノニム
やっと解決できました!
ありがとうございました