DirectXTKでフォント表示、日本語対応

投稿日:

デバッグ用の情報を表示するためにフォント表示を実装します。DirectX10まではフォント表示機能がありましたが、DirectX11にはないため、DirectXTKのフォント表示を使ってみます。それと日本語に対応していないようなので(バグかも)、DirectXTKを改造して無理やり日本語に対応させます。かなりいい加減な日本語対応だけどデバッグ用なので気にしない。
ss_dx11DirectXTKFont


DirectXTK フォント表示(アスキー文字)

DirectX11用の便利機能を提供するライブラリです。ここから入手(DirectXTK)
フォント表示にはSpriteFontクラスを使用します。
使用バージョン:February 22, 2013

コンパイル

コンパイルするとフォント画像を作成するコマンドラインツールも同時に作成されます。このツールで作成した画像をテクスチャとして読み込みフォント表示を行います。

フォント画像作成

ツールの場所は、DirectXTK\MakeSpriteFont\bin\Release\MakeSpriteFont.exe
コマンドプロンプトで引数に出力ファイルパスや作成する文字などを指定、実行します。

MakeSpriteFont "MS ゴシック" myfile.spritefont /FontSize:12 /CharacterRegion:32-126

readme.txtの使用例では”Comic Sans”フォントを指定していますが、ないので代わりに”MS ゴシック”を使用しています。

SpriteFontクラスの作成

#include "SpriteFont.h"//DirectXTK
// グローバル変数など
SpriteBatch* pSpriteBatch;
SpriteFont* pSpriteFont;

//初期化
pSpriteBatch = new SpriteBatch(deviceContext);//スプライト表示
pSpriteFont = new SpriteFont(device, L"myfile.spritefont");//フォント表示
// myfile.spritefont ツール(MakeSpriteFont)で作成したファイル

フォントの表示

pSpriteBatch->Begin();
pSpriteFont->DrawString(pSpriteBatch, L"Hello, world!", XMFLOAT2(x, y));
pSpriteBatch->End();

DirectXTK 日本語フォント対応

最初に

デバッグ用の仮対応、DirectXTKのソースコードを変更します。
利用は自己責任で!

DirectXTKでの問題

unicode(UTF16)を使用しているため日本語にも対応していそうですが、いくつか問題がありそのままでは使用できませんでした。

MakeSpriteFontが応答しない

漢字ひらがななど約2万文字を指定してツールを起動すると、数時間たっても終了しない。文字数が増えると指数関数的に処理時間が増加するようです。いつか終了するかもしれませんが、これでは使えません。

文字幅がおかしい

フォント画像は正常に作成されるようですが、フォントの文字幅設定がおかしく、全角文字の表示が重なってしまいます。

問題の解決

ツール(MakeSpriteFont)を修正して問題を解決します。

文字幅

文字コードから文字幅を取得するためにGetCharABCWidthsFloatという関数を使用していますが、でたらめな値を返しているようです。正常に動作させる方法がわからないので、半角か全角か調べ固定の文字幅を設定するように変更しました。

//TrueTypeImporter.cs 147行目
    // Query its ABC spacing.
#if false   //変更前
    float? abc = GetCharacterWidth(character, font, graphics);
#else   //変更後
    float? abc;
    // GetCharacterWidthがいい加減な値を返すので
    // 固定の幅に
    if (character > 0xff)//むりやり全角文字判定
    {
        abc = (float)Math.Ceiling(font.Size);
    }
    else
    {
        abc = (float)Math.Ceiling(font.Size)/2.0f;
    }
#endif

全角の判定、文字幅もとりあえず”MS ゴシック”fontSize=12のときに合うようにしているだけです。違うサイズ、フォントで正常に表示されるかわかりません。

ツールの処理時間

時間がかかり過ぎるので調べてみると、文字を追加する時の空き領域を検索する処理が原因でした。検索方法を見てみると、毎回左上から1ドット単位で空きを総当たりで検索……2万文字だと終わるのに何日かかるのか。効率よく画像を詰め込むための処理でしょうが、日本語の固定幅表示では不要なので順次、左上から詰めていくように改造しました。

//GlyphPacker.cs 22行目
    public static Bitmap ArrangeGlyphs(Glyph[] sourceGlyphs)
    {
        // Build up a list of all the glyphs needing to be arranged.
        List<ArrangedGlyph> glyphs = new List<ArrangedGlyph>();

        int font_h = 0;//フォント最大高さ
        for (int i = 0; i < sourceGlyphs.Length; i++)
        {
            ArrangedGlyph glyph = new ArrangedGlyph();

            glyph.Source = sourceGlyphs[i];

            // Leave a one pixel border around every glyph in the output bitmap.
            glyph.Width = sourceGlyphs[i].Subrect.Width + 2;
            glyph.Height = sourceGlyphs[i].Subrect.Height + 2;

            font_h = Math.Max(font_h, glyph.Height);//フォント最大高さ

            glyphs.Add(glyph);
        }

        // Sort so the largest glyphs get arranged first.
        glyphs.Sort(CompareGlyphSizes);

        // Work out how big the output bitmap should be.
        int outputWidth = GuessOutputWidth(sourceGlyphs);
        int outputHeight = 0;

        // Choose positions for each glyph, one at a time.
        int last_x = 0, last_y = 0;
        for (int i = 0; i < glyphs.Count; i++)
        {
            //毎回左上から隙間を1ドット単位で検索 遅すぎる!!
            PositionGlyph(glyphs, i, outputWidth, font_h, ref last_x, ref last_y);
            // 漢字は矩形内に収まるので順番に詰めていく 詰めた場所をlast_xyで保存しておく
                
            outputHeight = Math.Max(outputHeight, glyphs[i].Y + glyphs[i].Height);
        }

        // Create the merged output bitmap.
        outputHeight = MakeValidTextureSize(outputHeight, false);

        return CopyGlyphsToOutput(glyphs, outputWidth, outputHeight);
    }
// GlyphPacker.cs 99行目
    // Works out where to position a single glyph.
    static void PositionGlyph(List<ArrangedGlyph> glyphs, int index, int outputWidth, int font_h, ref int last_x, ref int last_y)
    {
        int x = last_x;
        int y = last_y;

        while (true)
        {
            // Is this position free for us to use?
            int intersects = FindIntersectingGlyph(glyphs, index, x, y);

            if (intersects < 0)
            {
                glyphs[index].X = x;
                glyphs[index].Y = y;
                last_x = x;
                last_y = y;
                return;
            }

            // Skip past the existing glyph that we collided with.
            x = glyphs[intersects].X + glyphs[intersects].Width;

            // If we ran out of room to move to the right, try the next line down instead.
            if (x + glyphs[index].Width > outputWidth)
            {
                x = 0;
                //y++;
                y += font_h;//すべて同じ高さに固定 qgなどで隙間ができるけど気にしない 速度重視
            }
        }
    }

日本語フォント画像作成

MakeSpriteFont "MS ゴシック" myfile.spritefont /FontSize:12 /CharacterRegion:32-126 /CharacterRegion:0x3000-0x30ff  /CharacterRegion:0xff01-0xffe5 /CharacterRegion:0x4e00-0x9fff
//  引数の説明
アスキー文字:/CharacterRegion:32-126
ひらがなカタカナ:/CharacterRegion:0x3000-0x30ff
記号:/CharacterRegion:0xff01-0xffe5 
漢字:/CharacterRegion:0x4e00-0x9fff
他に必要な文字があれば追加

サンプルプログラム

“DirectXTK MakeSpriteFont日本語対応” をダウンロード MakeSpriteFontModify.zip – 909 回のダウンロード – 12 KB

注意

変更したソースファイルのみ
デバッグ目的の仮対応

「DirectXTKでフォント表示、日本語対応」への2件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.