ここの情報は2006年10月27日現在の情報に基づいております(FreeType 2.2.1)
文中・ソースコードにひっきりなしにGDIとの比較対比を書いています。これは手近に比較するものがあると安心ということで深く考えないようにしたほうがいいかもしれません。
FreeTypeはフリーで、かつ軽量、プラットフォームに依存しないフォントエンジンであり、方々で使われております。ただしこのライブラリを直接使うことは少ないのか(pango、cairo等を通して使うことが多いような気がする)情報があまり出てきません。ただし本家のTutorialをよく読んでやれば表示までは割合簡単にでます。また本家のドキュメントにはフォントについての情報も載せられていますので熟読するといっそう理解が深まることと思います。
FreeType2のキャッシュを使ってみる(WindowsのTextOutもどき)2006/10/29
FreeTypeで一文字一文字真面目にFT_Load_Glyphを呼んでいると非常に時間がかかります。そこでFreeType自体にキャッシュシステムが搭載されております。ただこのキャッシュについてはチュートリアル的なものが見当たりません(よくみてないだけかも)。サンプルコードを読んでも今ひとつまともな使いかたとは思えない状況です(FreeTypeを使っているライブラリ等のコードを読めばわかることでしょうが)。
キャッシュシステムの使い方は難しくありません。
FT_Library freetype_library;
FTC_Manager cache_man; // キャッシュマネージャ
FTC_CMapCache cmap_cache; // CharMapのキャッシュ
FTC_ImageCache image_cache; // グリフイメージのキャッシュ
// キャッシュマネージャの初期化
FTC_Manager_New(freetype_library, 0, 0, 0,
face_requester, /* face_requester に渡すもの */,
&cache_man);
FTC_CMapCache_New(cache_man, &cmap_cache);
FTC_ImageCache_New(cache_man, &image_cache);
のように初期化をし、
FT_Face freetype_face; FTC_Manager_LookupFace(cache_man, face_id, &freetype_face);
としてfaceを取得し、
FTC_ScalerRec scaler; scaler.face_id = face_id; // 設定するfaceのID scaler.width = 0; // フォントの幅 scaler.height = FONT_HEIGHT << 6; // フォントの高さ scaler.pixel = 0; // Pixel指定かどうか scaler.x_res = RESOLUTION_X; // 横方向解像度 scaler.y_res = RESOLUTION_Y; // 縦方向解像度 FT_Size font_size; FTC_Manager_LookupSize(cache_man, &scaler, &font_size);
のようにサイズを指定し、
FT_Int cmap_index = FT_Get_Charmap_Index(freetype_face->charmap);
FT_UInt glyph_index = FTC_CMapCache_Lookup(
cmap_cache,
face_id,
cmap_index,
/* 文字コード */);
としてグリフインデックスを取得、
FTC_ImageTypeRec font_type;
font_type.face_id = face_id; // 取得するfaceのID
font_type.width = 0; // 幅
font_type.height = FONT_HEIGHT; // 高さ
font_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
FTC_ImageCache_Lookup(
image_cache,
&font_type,
glyph_index,
&glyph,
NULL);
でグリフインデックスをグリフイメージにし、表示するだけです。
FT_Set_Char_Sizeの替わりにFTC_Manager_LookupSizeを、FT_Get_Char_IndexのかわりにFTC_CMapCache_Lookupを、FT_Load_GlyphのかわりにFTC_ImageCache_Lookupを使用します。
face_requesterは自分で作成するコールバック関数で、キャッシュシステムでfaceが要求されたときにキャッシュされていないFaceIDだと呼ばれます。この中でFaceIDにしたがってフォントを作成し、返却します。FaceIDも自分で勝手に使い方を決めてかまいません(好きなデータへのポインタを渡してよい)。ただしNULLではいけません。
FTC_Manager_LookupFaceをして取得したfaceはサイズオブジェクトがありません。グリフイメージを取得するだけなら(FTC_ImageCache_Lookupを呼び出すだけならば)font_typeにサイズを指定しますが、FTC_Manager_LookupSizeを呼び出してサイズを指定しておく必要があると思います(手元ではFT_Set_Char_Sizeを指定すると落ちる)。
愚痴としてひとつ。FTC_Manager_LookupFaceや何かに渡すFaceIDですが、これはポインタです。FreeTypeキャッシュシステムでは同一値かどうかはFaceIDのアドレスで判定しています。ということは別アドレスにある同一値のものを指定しても同一とは判定されないためface_requesterが呼ばれ、実質的に同じfaceが複数キャッシュされてしまうことになりそうです。ですからこれは単にID値として整数をキャストして渡すか、メモリアドレスが固定的なFaceIDを自前で用意し、事前にそれを取得、LookupFaceに渡すというような処理をする必要がでてきそうです。しかしそこまでするならばLookupをするためにFreeTypeのキャッシュシステムに頼る必要はなくなってきます。この場合はFacdIDが同一か判定するための関数のポインタをキャッシュシステムに渡して、メモリアドレスにかかわらない判定ができるようにするのがいいと思います(多分なってないと思う)。ただこういった判定は頻繁に呼び出されますからインラインにすると五割も早いぜ!
とかやってると関数ポインタを使って呼び出すというのはすこしためらわれるかもしれません。
はじめに断っておきますがこのベースラインを求めるという手順を踏みたいのはGDIのTextOut APIのようなインターフェイスにしたいがためです。
ベースラインがずれると無茶苦茶に表示されます。WindowsGDIと比較しつつ試したところ
baseline = (freetype_face->height + freetype_face->descender) *
freetype_face->size->metrics.y_ppem / freetype_face->units_per_EM;
とすると大体合致するようです(2006-10-29 Fix 以前はy_ppemではなくmetrics.height >> 6としていた)。ただこれでも1ピクセルから2、3ピクセル程度ずれるものもあるようです。
(2006-11-01追記)
ソースに修正は入れませんがfreetype_face->height + freetype_face->descenderとせずにfreetype_face->ascenderをそのまま使ったほうが良い結果になることが多いようです(heightがAscenderとDescenderの和になっていないので良くみたところ、このHeightは両者にLineGapを加算したものになっていました)。とは言うもののこの計算でかなり変になるものがあります。そういったものはface->ascenderの値(を上記のとおり変換したもの)とTEXTMETRIC.tmAscenderの値が大きく異なります(だからずれるのですが)。
FreeTypeのAscenderはOUTLINETEXTMETRIC.otmMacAscentと同じになるようです。ただこれだけ分かってもどうしてもTEXTMETRIC.tmAscenderをはじめとした値の算出がわかりません。まあ別に分からなくてもFreeTypeを使うのにこんな怪しい作業をする必要はありませんからかまわないのですがどんな構造なのか気になります。。
そもそもFreeTypeはベースラインを基準に描画するようにできているので本来であればベースラインを算出する処理をしないで描画するべきです。Windows APIではTEXTMETRIC.tmAscenderがそのままベースラインの位置であって、Yから減算するとぴったりそろうことになります。ところがFreeTypeではYをいじらない状態でぴったり描画するのです。
Kerningのようなものも含め、FreeTypeでは小数部6ビット、あるいは16ビットの固定小数点数を多用しています。これによってズレの蓄積を防ぎ、精度の高い座標計算を可能にしています。実際はそういった形で使うのが望ましいでしょう。ただしサンプルではそんなことはまったくしていません。
Kerningについても一応少し手をつけてあります(2006-10-28 Fix)。ただ文字幅をガガっと取得するためにグリフを一度ロードしています(2度手間になる)。まあキャッシュを使っていますからキャッシュされるでしょう。
セル高さ文字高さ2006/10/29
ワープロのようなもので表示するときにセル高さを指定すると英字と邦字で見た目の大きさが異なる事が起きてきます。そのためかFreeTypeは普通(FT_Set_Char_Size・FT_Set_Pixel_Sizes)は文字高さを使用しています。しかし、行の高さを決定しておいて、それに当てはまるサイズが必要になることもあるはずで、その場合にはセル高さを指定します。
FT_Set_Char_Size・FT_Set_Pixel_SizesのかわりにFT_Request_Sizeを使用します(FT_Set_Char_Size・FT_Set_Pixel_Sizesは内部でFT_Request_Sizeを呼び出している)。その際FT_SIZE_REQUEST_TYPE_CELLを指定するとセル高さでサイズを指定できます。
FT_Size_RequestRec size_request; size_request.type = FT_SIZE_REQUEST_TYPE_CELL; size_request.width = 0; size_request.height = FONT_HEIGHT << 6; size_request.horiResolution = 0; size_request.vertResolution = 0; FT_Request_Size(freetype_face, &size_request);
あとは普通に使うだけです。
Windows GDIではCreateFontのnHeightに正の値を指定するとセルの高さ、負の値を指定すると文字の高さになります。ただGDIでは必ず指定した数値以下の大きさになるのですがFreeTypeではそうはいかないようです。
太字にする例2006/10/29
こういった文章中で太字を使うというのはよくあることです。書籍等の印刷物では普通は明朝で太字部分はゴシック体のような形にしますが、WEBブラウザのようなものでHTMLを表示するとき等は同じ書体で太字にしたものを使うのが多いようです。太字体が用意されている書体ならそれを使えばいいのですがいつもそういうわけにはいきません。そういった場合にWindows GDIではCreateFontのfnWeight引数にFW_BOLD等を渡すことで実現できます。ではFreeTypeではどうするのでしょうか。
まずはグリフのアウトラインを取得します。フォントはアウトラインフォントでないといけません。
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP);
次にFT_Outline_Emboldenを呼び出し、アウトラインを補正します
if(face->glyph->format != FT_GLYPH_FORMAT_OUTLINE){
// エラー! アウトラインでなければならない
}
int strength = 1 << 6; // 適当な太さ
FT_Outline_Embolden(&face->glyph->outline, strength);
最後に通常どおりFT_Render_Glyphでビットマップに変換します
FT_Render_Glyph(freetype_face->glyph, FT_RENDER_MODE_NORMAL);
これで終了です。FT_Outline_Emboldenを使用するためにはFT_OUTLINE_Hをincludeする必要があります。
斜体にする例2006/10/29
西欧のほうでは斜体も強調(というか変化?)としてよく使うようなの。普通斜体と呼ばれるのはItalicで、これはただ文字を斜めにしただけとは少し違います。欧文ではこれを斜体と呼ぶのが普通だと思います。一方斜体を用意するのが大変だとか、そもそも邦字でそんなことをやるのか必要なのかという場合もあるでしょう。写真植字では変形レンズを使ってぐにゃりと文字を変形させているということですがデジタル書体でも同じです。
Windows GDIではItalic体があるときはそちらを優先して使用するようになっているようです。それは運用上の問題なので触れませんが、Italic体がない場合に斜体指定されるとべしゃっとひしゃげて斜体にします。obliqueといい、CSSでもitalicとは別にobliqueも定義されております。
FreeTypeのマニュアルをざっと眺めてみましたがObliqueについては触れられていないようです。将来的には実装されるでしょうが(実はアルファとしてソース中にはあるようだがマニュアルに載せないぐらいので使うのは控えておいたほうがいいと思う)、それまで暫定的にそれっぽくみえるものをこしらえて使っておくのもいいかもしれないと思いました。
FT_Outline_Transformでアウトラインを変形するだけです。
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP);
if(face->glyph->format != FT_GLYPH_FORMAT_OUTLINE){
// エラー! アウトラインでなければならない
}
FT_Matrix matrix;
matrix.xx = 1 << 16;
matrix.xy = 0x5800;
matrix.yx = 0;
matrix.yy = 1 << 16;
FT_Outline_Transform(&freetype_face->glyph->outline, &matrix);
0x5800という値は適当です。適宜変更するとよろしいかと。
アンチエイリアス表示調整手法の一例2006/10/26