2012年02月09日

PC6001V移植計画(4)フォントビットマップを作る

さて、PC6001VをLinuxに移植するにあたって、もうひとつクリアしなければならない課題があります。

フォントファイルの生成です。
PC6001Vは、モニタモードやステータスバーをビットマップで描画しており、そのためのフォントビットマップを実行時にMSゴシックから生成しています。
fontz12_orig.png
↑こんなの


しかしLinux環境の場合、特定のフォントがインストールされている事を前提にできないので、フォントビットマップはアプリにバンドルしておく方が無難です。

ただし、これを実現するための要件は結構シビアです。
1.フォントファイルからビットマップの生成、再配布が可能なライセンスのフォントを使うこと。
2.半角6x12,全角12x12でアンチエイリアスを使わずに読めること。

1.が最大の壁でした。
フォントビットマップの生成はゲーム開発の分野では多くの方が直面する課題なので、ここにとても有用なまとめがありました。
ここでの利用用途は、上記のページで言うところの「活字img」に相当するので、フォント全体をビットマップ化して、アプリに同梱して大丈夫なゴシック体というのは、事実上M+と梅フォントの二択です。

2.に関しては、M+BITMAPが12ドットフォントを提供しており、これが使えればベストだったのですが、これはBDFフォーマットで提供されており、どうやらデスクトップ版のQtでは扱えないようです。
こちらでいろいろ試した限りでは、小夏フォントを使うとTTF内蔵のビットマップを使って描画出来ているっぽいのですが、Fontforgeとかを使ってM+BITMAPをTTFに埋め込んでみてもうまくいきませんでした。

小夏フォントが一番きれいな出力を得られたのですが、ライセンスがクリエイティブコモンズで、FSFによるとLGPLであるPC6001Vには同梱できないらしいので諦めました。

今回はM+OUTLINEベースで手を打つことにします。

フォントの選定が出来たところでビットマップを生成するジェネレータのプログラムを書きます。
PC6001Vのフォントビットマップは、JIS漢字コード順で94x94文字の正方形になっています。
これとそっくり同じ配置で文字を描画すればいい事になります。

描画自体は、QPainterにQFontをセットしてQImageに描画してやれば、PNGファイルとして保存できます。楽チンです。


int id = QFontDatabase::addApplicationFont(":/mplus-1mn-regular.ttf");
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont font(family);
font.setPointSizeF(9);
font.setStyleHint(QFont::AnyStyle,
QFont::StyleStrategy(QFont::PreferBitmap | QFont::NoAntialias));

// フォント描画
const int size = 6;
// 半角
char hbuf[2] = { 0x00, 0x00 };
const int hShift = 3; //余白調整のため描画を上にずらす
QImage hImage(1152, 24, QImage::Format_RGB32);
hImage.fill(0);
QPainter hPainter(&hImage);
hPainter.setPen(Qt::white);
hPainter.setFont(font);
for( int y=0; y<2; y++ ){
for( int x=32; x<128; x++ ){
hbuf[0] = x + y * 128;
hPainter.drawText(QRect(x*size, y*size*2-hShift, size, size*2+hShift), Qt::AlignLeft, hbuf);
}
}
hImage.convertToFormat(QImage::Format_Indexed8).save("fonth12.png");

フォントのサイズやベースラインの調整のためのオフセットは、PC60001V用のフォントファイルという用途に特化しているので、ハードコードのパラメータで何度も試行錯誤しながら調整しています。

元となるTrueTypeフォントは、Qtのリソースファイルとして、ジェネレータのバイナリに埋め込むという暴挙に出ています。
    int id = QFontDatabase::addApplicationFont(":/mplus-1mn-regular.ttf");


しかしこうしておくと、
・フォントをシステムにインストールしなくてよい
・ジェネレータの実行ファイルとフォントの相対パスを気にしなくて良い
という利点があります。

イメージの生成自体は簡単ですが、面倒なのが文字コードです。
文字配置はJISコード順ですが、Qtの内部文字コードはUnicodeなので、JIS→Unicode変換をしてやる必要があります。

QtにはQTextCodecという文字コード変換クラスが用意されています。
ISO-2022-JPというのが俗に言うJISコードです。
    char zbuf[6] = { 0x1B, 0x24, 0x42, 0x00, 0x00, 0x00 };  //前半3バイトは漢字を使うための制御コード
QTextCodec *jis = QTextCodec::codecForName("ISO-2022-JP");
for( int y=1; y<95; y++ ){
for( int x=1; x<95; x++ ){
zbuf[3] = y + 0x20;
zbuf[4] = x + 0x20;
QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
QString letter(jis->toUnicode(zbuf));
zPainter.drawText(QRect(x*size*2, y*size*2-zShift, size*2+1, size*2+zShift), Qt::AlignLeft, letter);
}
}


こうしてやれば、JISコードのバイト列からUnicode文字列を得ることができ、グラフィックとして描画することができます。
漢字1文字を書くのになぜ6バイトも必要かというと、JISコードには制御コードが必要で、漢字を使う前には0x1B, 0x24, 0x42の3バイトの制御コードを挟まなければなりません。
制御コードを入れないと、上のコードで最初に生成される"2121"というコードは"!!"という半角文字列として描画されてしまいます。

このへんの仕組みは全く知らなかったので随分はまりました。
今時はSJISかUnicodeが主流で、JISコードを触ることなんてないし、
(官公庁案件では今でもあるのかもですが)
P6は第1水準漢字までしかないし、制御コード何それ美味しいのという漢字でした、もとい感じでした。

こうして何とか読める程度のフォントビットマップが出来ました。
Screenshot-2012-02-08 20:09:42.png

M+BITMAPが使えなかったのが心残りですが、たぶんFreeTypeを直接叩くなどする必要があるでしょう。
こちらは又の機会という事にします。

こうしたフォントビットマップの生成は、エロ(ゲフンゲフン)もといノベルゲームを作る人などにもしかしたら需要があるかもなので、そのうちP6Vと一緒にフォントジェネレータも公開しようと思います。

posted by eighttails at 22:56| Comment(0) | PC6001VX | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: