ゲーム開発の備忘録

趣味のゲーム開発でのノウハウや、技術的に嵌ったポイントを忘れないように書き記しておくブログです。

DirectWriteでフォントファイルからフォントデータを読込む

はじめに

今回は、DirectWriteでttf等のフォントファイルを読みこんで、フォントデータを抽出する方法を説明します。
ゲーム内でDirectWriteで文字列を描画する際、ユーザの環境にその文字列のフォントがインストールされていない場合があります。そのため、datファイルの中にttfファイルを含め、そこからフォントデータを抽出できるようにしなければなりません。

DirectWriteでフォントファイルからフォントデータを読込む手段は登場当初から提供されていたものの、手間のかかる内容となっていました。しかし、Windows 10 Creators Update (プレビュー ビルド 15021 以降) でより簡単にフォントファイルを読込む手段が提供されています。今回は、新しい手段でのフォントファイルの読み込みについて解説します。

FontSetBuilderの準備

はじめに、フォントセットの手動生成に必要となるIDWriteFontSetBuilder1クラスのインスタンスを生成します。このインスタンスを生成するには、dwrite_3.hに含まれるIDWriteFactory5クラスのインスタンスが必要になりますので、IDWriteFactoryを使っていた方はdwrite.hをdwrite_3.hに差し替えて、IDWriteFactoryインスタンスを取得している箇所を、IDWriteFactory5インスタンスを取得するように変更しましょう。
なお、dwrite.libのリンクはそのままで問題なく、また、IDWriteFactory5インスタンスはIDWriteFactoryインスタンスと同様、DWriteCreateFactory()関数で生成できます。

IDWriteFactory5インスタンスが生成できたら、IDWriteFactory5#CreateFontSetBuilder()関数でIDWriteFontSetBuilder1インスタンスを生成します。引数にはIDWriteFontSetBuilder1インスタンスのポインタのポインタを指定します。ここまではデバイスロストの影響を受けないため、ゲーム起動後に一回だけ実施できればよいでしょう。

フォントファイルの読込み

必要な前準備が完了したらフォントファイルを読み込みます。まずはdatファイル内ではなく、フォルダ内にttf等のファイルが存在する場合から説明します。
DirectWriteでは、フォントファイルはIDWriteFontFileクラスのインスタンスとして扱います。

フォルダ内のフォントファイルを読み込むにはIDWriteFactory5#CreateFontFileReference()関数を利用します。第1引数にはフォントファイルのパスを、第3引数にはIDWriteFontFileインスタンスのポインタのポインタを指定します。第2引数は通常はnullptrで問題ありません。

datファイル内のフォントファイルの読込み

datファイル内に存在するフォントファイル等、メモリ上にバイナリデータとして存在するフォントファイルを読み込むには一手間が必要で、フォントファイルを読込む前にInMemoryFontFileLoaderを生成してFontFileLoaderとしてシステムに登録しなければなりません。

まずは、IDWriteInMemoryFontFileLoaderクラスのインスタンスを、IDWriteFactory5#CreateInMemoryFileLoader()関数を用いて生成します。引数にはIDWriteInMemoryFontFileLoaderインスタンスのポインタのポインタを指定します。
次に、IDWriteFactory5#RegisterFontFileLoader()関数を、先程生成したIDWriteInMemoryFontFileLoaderインスタンスのポインタを引数に渡して呼出し、システムが使用するFontFileLoaderとして登録します。
ここまでの作業はデバイスロストの影響を受けないため、ゲーム起動後に一回だけ実施できればよいでしょう。なお、登録したFontFileLoaderはアプリケーションが終了する前に登録解除する必要があります。IDWriteFactory5#UnregisterFontFileLoader()関数で登録解除を行えます。引数には、登録したIDWriteInMemoryFontFileLoaderインスタンスのポインタを指定してください。

IDWriteInMemoryFontFileLoaderインスタンスが準備できたら、IDWriteInMemoryFontFileLoader#CreateInMemoryFontFileReference()関数を利用して、IDWriteFontFileインスタンスを生成します。第1引数にはIDWriteFactory5インスタンスのポインタを、第2引数にはメモリ上のフォントファイルの先頭を指すポインタを、第3引数にはメモリ上のフォントファイルのサイズを、第5引数にはIDWriteFontFileインスタンスのポインタのポインタを指定します。第4引数は通常はnullptrで問題ありません。事前に必要となる、datファイル等からフォントファイルの先頭のアドレスを取得する処理は読者各々で実装しておいてください。

フォントコレクションの生成

IDWriteFontFileインスタンスの準備ができたら、それをもとにフォントコレクションを生成します。
まずは、IDWriteFontSetBuilder1#AddFontFile()関数で、FontSetBuilderにフォントファイルを登録します。引数にはIDWriteFontFileインスタンスのポインタを指定します。
次に、IDWriteFontSetBuilder1#CreateFontSet()関数で、フォントファイルからフォントセットを生成します。引数にはIDWriteFointSetクラスのインスタンスのポインタのポインタを指定します、
最後に、IDWriteFactory5#CreateFontCollectionFromFontSet()関数で、フォントセットからフォントコレクションを生成します。第1引数にはIDWriteFointSetインスタンスのポインタを、第2引数にはIDWriteFontCollection1クラスのインスタンスのポインタのポインタを指定します。

なお、最終的に必要になるのはIDWriteFontCollectionクラスのインスタンスですが、IDWriteFontCollection1からIDWriteFontCollectionへはキャストできますので問題ありません。

フォントコレクションを指定したTextFormat生成

フォントコレクションを生成したら、いよいよTextFormatの生成を行います。
IDWriteFactory#CreateTextFormat()関数の第2引数に、生成したIDWriteFontCollectionインスタンスを指定することでフォントコレクションを用いたTextFormatの生成ができます。この時、第1引数のフォントファミリ名は、フォントコレクションに含まれるフォントのフォントファミリ名を指定してください。
あとは通常の描画時と同様に、そのTextFormatを用いて文字列描画をすることができます。

おわりに

お疲れ様でした!DirectWriteも情報源が少なく、MSDNでも難解な言い回しがされていることもあり、いまいち何ができるのかが分かりにくく感じることが多々ありましたので、同じように感じている方に情報共有をすることができれば嬉しく思います。
今回解説した内容は、DirectX10以上を使用しているPCゲームであればほぼすべての場合に必要になると思いますので、ぜひ導入してみてください!