強火で進め

このブログではプログラム関連の記事を中心に書いてます。

テクスチャ上にビットマップフォントを準備して文字列を描画


どうやらiPhoneOpenGL ESでは文字列の描画処理用のコマンドが見当たらなかったので自前でテクスチャ上にビットマップフォントを準備し、文字列を描画することにしました。

フォント選定

フォントはフリーのものでもビットマップデータ化を禁止しているものも多いので今回使用したのはこちらの「みかちゃん」フォントを使用しました。

オリジナルフォント【みかちゃん
http://mikachan-font.com/

こちらのフォントはフォント自身の販売以外は制限の無い、使用者にとても優しいライセンスとなっています。作者さんに感謝、感謝です。

フォントには「みかちゃん」(等幅フォント)と「みかちゃん-P」(プロポーショナルフォント)があるのですが今回は処理が楽なので等幅フォントの「みかちゃん」を使用しました。

今回はデバック情報の表示に使用する予定なので「等幅フォント」の方を選択しましたが長文を表示する場合などは「プロポーショナル」の方を選択した方が良いでしょう。

対応する文字コードの範囲

さてとりあえずビットマップ化する文字の対象の選出です。取りあえず日本語も使える様にしちゃうとデータサイズが大きくなりすぎるので今回はASCII文字列のみです。

こちらの情報を確認したところ

[psl]ASCIIコード一覧表
http://www.psl.ne.jp/perl/pdojo00b.html

文字コード 0x20 のスペースから 0x7E の ~(チルダ)まで良さそうです。

こちらの様にカタカナまで入れても良いかな?とも思いましたが取りあえず今回は英数字、記号までとしました。

アスキー
http://www.tuat.ac.jp/~yamada/cp1/ascii.html

ビットマップ化

次にビットマップ化です。普通ならビットマップデータを準備して内部的に文字列を描画したりするんでしょうがまだMacのプログラムにはあまり慣れてないし楽したいなぁと思ってHTMLファイルを出力するC言語プログラムを作成するというちょっとトリッキーなことしてますw

プログラムはこの様になっています。ソースコードこちらからDLして下さい。

スタイルシートで整形したテーブルに文字を1行、16文字づつ表示するHTMLファイルを生成しています。

#include <stdio.h>

int main (int argc, const char * argv[]) {
	FILE *fp;
	int i, count;
	fp = fopen("../../ascii.html", "w");
	if (fp) {
		fprintf(fp, "<html>\n<body>\n");
		fprintf(fp, "<style type='text/css'><!--\n.ffamily { font-family: 'みかちゃん'; border: 1px solid #000; width: 140px; padding: 0px; margin: 0px; line-height: 1em; }\n--></style>\n");
		fprintf(fp, "<style type='text/css'><!--\ntable { border: 1px solid #000; border-collapse: collapse; }\n--></style>\n");
		fprintf(fp, "<style type='text/css'><!--\ntd { border: 1px solid #000; }\n--></style>\n");
		
		count = 0;
		fprintf(fp, "<table class='ffamily'>\n<tr>");
		for (i=0x20; i<0x7F; i++) {
			count++;
			fprintf(fp, "<td>%c</td>", (char)i);
			if (count >= 16) {
				fprintf(fp, "</tr>\n<tr>\n");
				count = 0;
			}
		}
		fprintf(fp, "</tr>\n</table>\n</body>\n</html>\n");
		fclose(fp);
	}
    return 0;
}

このプログラムを実行するとプロジェクトと同じ位置に ascii.html というHTMLファイルが生成されますのでFireFoxなどのブラウザで表示し、その画面をキャプチャして下さい。

表示結果この様になります。

こちらを加工してこの様にします。

加工した点は以下

  • 枠線は必要ないので幅と高さを測定した後に白で塗りつぶす
  • iPhoneではテクスチャは2の乗数(128,256,512など)しか対応していないので画像のサイズを128x128とする
  • CGエディタとテクスチャ座標系ではY座標の向きが逆なので画像を反転
  • 描画ミスした場合にすぐに分かる様に角を緑と青でマークを付ける
  • 自分が使用しているCGエディタのLeeshoreはアルファチャンネルを追加しても全てのピクセルのアルファ値が100%の時は自動的にRGBの画像として保存される様なので実際には使用しない部分にアルファ値100%以外のピクセルを作成

文字列描画のプログラム

文字列の描画については以前紹介した glDrawTexiOES() を使用して行っています。

glReadPixels()、glDrawTexiOES()コマンドを使ってレンダリング後の画像を利用する - 強火で進め
http://d.hatena.ne.jp/nakamura001/20081208/1228729579

ビットマップフォントをロードしたテクスチャをバインドし、 DrawTextureFont()関数(自作関数) を呼ぶと描画できるように作成しました。

DrawTextureFont()関数の引数は先頭から順に以下の様になります。

  • X座標
  • Y座標
  • 描画する文字列を char * 型で指定(ASCII文字列の0x20〜0x7Eの範囲のみ。それ以外を指定した場合にはスペースに置き換わります)
    glBindTexture(GL_TEXTURE_2D, asciiTexName);
    sprintf(fps, "%3.1ffps", frame_rate);
    DrawTextureFont(0, 0, fps);
    DrawTextureFont(0, kTexFontHeight, "Hello World!");

具体的な実装方法はこちらのサンプルコードを参照下さい。

問題点

実装してみて現在分かっている問題点として以下のものがあります。もしかしたらプログラム次第で対応可能なのかもしれませんが今回調べた範囲では対応策が見つかりませんでした(もし、対応策をご存知の方がいましたらコメント欄にて情報お願いします<(_ _)>)。

問題点1. RGBAのテクスチャでないといけない

透過しないし、RGBのテクスチャで良いかな思いRGBのものでテクスチャを作ったところエラーとなりました。iPhone(OpenGL ES)はRGBAしか対応してない?

問題点2. 文字に透過、半透明が使えない

※コメントにて id:mswar さんに「使えますよ」と教えて頂きました。自分でもテストしたところ透過、半透明ともに可能でした(うーん、なんか勘違いしてたみたいです)。
背景の白の部分のアルファ値を50%に設定してテストしたところ灰色になったので成功したのかな?と思ったのですが文字の後ろにはちゅねが来てもまったく描画がされませんでした。

特に問題点2については現在はデバック用途を考えているので大きな問題ではないのですが実際にユーザが読む部分で使用する場合などはちょっと問題になるかなと思っています。

問題点3. シミュレータでは glDrawTexiOES() が反映されない

シミュレータでは glDrawTexiOES() での描画に対応していないようでシミュレータで動作確認ができません。

取りあえずこの辺りに対応するには glDrawTexiOES() コマンドを使っての描画でなくポリゴンを準備して、そこに文字のテクスチャを貼付ける形での実装をしないといけないかなぁと思っています。

glDrawTexiOES() だとカメラが移動したときのことを考慮しなくて良いので楽かなぁと思ってたんですが意外な弱点がありましたw 必要になったらポリゴン版も作らないといけないなぁ。

あと、こちらのサイトの方がやられてるみたいな動的な生成もしたいところ。

文字列のイメージを作成する - Objective-Audio
http://objective-audio.jp/2008/12/post-11.html

(2009/01/27 追記)
問題点3を追記。