この記事は Qt Blog の "Hint, hint, nudge, nudge, say no more!"を翻訳したものです。
執筆: Eskil Abrahamsen Blomfeldt 2011年3月14日
この記事の執筆時点で、Qt のバグトラッカー で最も多くの投票を得ているのは “Allow better font rendering for projects like Koffice” (Koffice のようなプロジェクトのためのより良いフォントレンダリング) という若干漠然としたタイトルのタスクです。
このタスクの背景として、Qt がフォントのデザインメトリックスで行うテキストのレイアウトにおけるクロスプラットフォームサポートの欠如があります。Qt のフォントのバックエンドはオペレーティングシステムに依存しているため、テキストの描画にヒントを使用する場合があります。ヒントの利用とは、特定のサイズと解像度での描画用に最適化されたグリフ(字体)を、さらに画面に描画される前にわずかに変更することを意味しています。プリンタのような高解像度のデバイスではグリフのヒントは必要ありません。しかし、デスクトップモニタのように低解像度で表示する際には、ヒントを使用してピクセルグリッドに沿った配置にする事で、よりくっきりとした字体ではっきりと読めるように出来るでしょう。
代表的な例を挙げると、水平および垂直方向のグリフのヒント(フルヒント)を活用するものとして Windows の GDI、一切のヒントを使用しないものとして Mac OS X の Core Text があります。Linux でテキストのラスタライズに用いられる FreeType ではフルヒントから垂直方向のみ、ヒントなしまでカスタマイズする事が出来ます。Windows と Mac のテキストラスタライズの違いを下記のスクリーンショットでご確認ください。
例えば、このスクリーンショットの "Lorem" の ’e' に注目してください。GDI 版の方がよりはっきりと見えるのが分かると思います。これは中央の横線の外形が2ピクセルに渡りぼけた描画になるのを避けるために、ピクセルグリッドに沿った形状へとわずかに変更したことによるものです。
この二つの例を拡大したときに次に気が付くのは、GDI 版はある文字の描画がどこであっても全て同じであるのに対して、Core Text ではその文字の文字列中の位置によって違いが見られる事でしょう。これは Core Text ではグリフをサブピクセル単位で配置しているためです。その位置によるわずかなピクセル(およびサブピクセル)の変化に対応してグリフが変化しています(ラスターペイントエンジンのサブピクセル位置に関するブログ を参照してください)。グリフがピクセルグリッドに従って配置されている場合、すなわち GDI では各グリフはピクセルベースで位置を決めているため、各文字は全て同じ形で描画されます。
三番目に気が付く事は [qtbug QTBUG-10615] のやりとりから判明した事の一つで、テキストの各行の長さが GDI 版と Core Text 版とで異なっています。上記の例ではその差異は小さいものの、はっきりと分かります。また、テキストの折り返し位置のようなレイアウトのプロパティに影響する場合もあるでしょう。ユーザインターフェースのテキストではこれは多くの場合問題ないでしょう。UI はテキストサイズの些細な変化に対応できますし、多くの場合、そのアプリケーションが動くプラットフォームの見た目と同様にするのがメインだからです。そのため、Qt も過去にはそこに注力してきました。しかし、より印刷が重要なケースではそれは問題になるかもしれません。プリンタのように解像度の違うデバイスを扱う際に、テキストのレイアウトやサイズをあらかじめ計算しておく事が出来なくなるからです。Microsoft Word のような印刷向けアプリケーションではこれは問題となります。そういったアプリケーションではまずデザインメトリックスを計算し、レンダリングしたグリフとレイアウトのグリフとの誤差を計算し、この誤差を単語間や文字間に伝搬させより精度を確保させようとしています(スペースを一切含まないテキストでは各行に奇妙な影響が生じる事になりますが)。Qt ではこれまでこの問題は無視していました。
この数週間、Jiang と共にこの問題に取り組んできました。そして、この問題に対応するために Qt 4.8 で [qt QFont] に新しいプロパティ QFont::hintingPreference() を追加する事にしました(ヒントという名称が指し示すように、その挙動については保証されませんが、フォントのバックエンドで可能な機能を活用できるようにベストを尽くします。この API はまだ確定していないので、なにかしらの意見があれば是非お知らせください)。
プラットフォームでのサポートには限りがあります。Core Text ではテキストは既に正確に描画されており、ネイティブのユーザインターフェースもヒントを使わずにレンダリングされています。そのため、このプラットフォームでは "None" 以外のヒントスタイルはサポートしません。GDI においては逆にヒントをオフにする方法がありません。そのため古い Windows プラットフォームではこの API のメリットは享受できません。
注力したのは下記の二つのプラットフォームです。
(注: DirectWrite バックエンドサポートを有効にするには、DirectWrite をターゲットのプラットフォームで有効にするために "-directwrite" オプションをつけて configure、ビルドした Qt が必要です。)
前述のスクリーンショットに比較用に DirectWrite の結果を加えたものを見てください。
このスクリーンショットでは、DirectWrite が Core Text のテキストと同じメトリックス(フォントのデザインメトリックス)で描画されています。すなわち行の幅が同じになっています。さらに拡大してみると、DirectWrite エンジンではグリフの位置がサブピクセルで決定されている事、すなわち文字列中の各文字の位置によって若干異なるラスタライズ結果が得られている事が分かります。最後に、DirectWrite のラスタライズ結果は Core Text の結果よりもはっきりしている事も分かります。これは DirectWrite は依然として垂直方向のヒントを使用しているためです(文字 'e' の横棒をよく見てください。Core Text では2ピクセルにわたって描画しているのが分かります)。ヒントはテキストの水平方向のレイアウトには影響していませんが、全くヒントを使用しない場合に比べてよりはっきりとラスタライズされ、読みやすくなっています。
全てを比較するため、FreeType エンジンで描画された二つの例を追加しましょう(訳注: 最終的に三つの例が追加されました)。
ここでは "no hinting" が Core Text のラスタライズ結果に対応し、"vertical hinting" (FreeType では "light" と呼びます)が DirectWrite のラスタライズ結果に、"full hinting" が GDI のラスタライズ結果に対応します。
これらの変更が mainline に達するまでにはもうしばらくかかります。FreeType の変更はまだ仕上げをしているところです。DirectWrite バックエンドは外部に公開する前に、テストやインテグレーションといったステップを踏む必要があります。近いうちにはこれらの三つのどのバックエンドでも、アプリケーションがテキストを正しく描画する印刷をサポートできるようになるでしょう。