えふのへや - 等々力随想

できればもうひとつのブログ http://ameblo.jp/bewise-kwsk/ もご訪問ください

Win32で遊びはじめました (4)

2021-02-14 20:02:55 | Weblog
GetGlyphOutlineはデバイスコンテキストに設定されたフォントの、特定文字のアウトライン、またはビットマップを取得する機能です。

形式は以下の通り。
BOOL GetGlyphOutlineW(
HDC hdc,
UINT ch,
UINT fuFormat,
LPGRYPHMETRICS lpgm,
DWORD cjBuffer,
LPVOID pvBuffer,
const MAT2 *lpmat2)

hdc引数は描画対象のデバイスコンテキストで、使用するフォントを先に指定しておく必要があります。

ch引数はアウトラインを取得したい文字、GetGlyphOutlineWの場合はUnicode文字です。GetGlyphOutlineAの場合にはマルチバイト文字を指定するのだろうと思いますが、GetGlyphOutlineAの方は確認していません。

fuFormat引数にGGO_NATIVEを指定するとフォントがTrueTypeまたはOpenTypeであり、フォントがアウトラインの取得を許可していればフォントのアウトラインデータを取得することができます。
# アウトラインの取得を許可していないフォントを持っていないので、確認はできていませんが。

lpgm引数には文字データに関連する情報が記録されます。文字の送り量 (gmCellIncX) を取得するのに使えます。

pvBuffer引数とcjBuffer引数には取得したアウトラインデータを保存するメモリへのポインタとメモリサイズを設定します。
と言っても、アウトラインデータの場合は必要なメモリサイズを見積もることはできないので、cjBuffer引数に0、pvBuffer引数にNULLを設定してGetGlyphOutlineを呼ぶことで必要なメモリサイズを戻り値として取得できます。

lpmat2にはあらかじめ作成した行列へのポインタを渡します。アウトラインデータを取得する場合、なぜか文字の上下が反転しているので、{{0, 1}, {0, 0}, {0, 0}, {0, -1}}を設定する必要があるようです。

戻り値はアウトラインデータのデータサイズ、エラーの場合は負の値です。空白文字の場合、アウトラインデータがないので0が返りますが、エラーではないのでlpgm引数に適切なデータがセットされます。

なお、デフォルトでデバイスコンテキストに設定されているシステムフォントはアウトラインが取得できないようで、エラーになります。

取得できるアウトラインデータはベースラインが基準になっているようです。また、1文字ずつアウトラインを取得するのでカーニングは自分で調整しなければなりません。なのでTextOutと合わせて使用するのは面倒な気がします。
# カーニング関連はそのうち調べます。

アウトラインデータの形式はTTPOLYGONHEADERの後に必要な数のTTPOLYCURVEが続くという形で一つのパスを表現し、櫃ヨナパスが連続してアウトラインデータを構成するという仕組みのようです。

TTPOLYGONHEADERの形式は以下の通り。
DWORD cb
DWORD dwType
POINTFX pfxStart

cbメンバーはTTPOLYGONHEADERと引き続くTTPOLYCURVEの列を含むパスデータのバイト長を示します。

dwTypeメンバーはパスの形式を示すデータで、現在はTT_POLYGON_TYPEしか定義されていません。将来との互換性を考慮するのであれば、このデータの値をチェックして知らないデータ形式であればエラーを出すなどの処理が必要かもしれません。多分、その必要はないと思いますが。

pfxStartメンバーはパスの最初の点になります。POINTFXは整数部 (value) 16ビット+小数部 (fract) 16ビットの固定小数点数の組で構成された座標値です。
# デバイスコンテキストにより解像度調整されており、かつLineToやPolyBezierToは整数引数しか取らないので、GDIの中で使用する際に小数部をどう扱うかが実はよくわかっていません。

TTPOLYGONHEADERに続いてTTPOLYCURVEがパス要素の数だけ連続します。

TTPOLYCURVEの形式は以下のとおり。
WORD wType
WORD cpfx
POINTFX apfx[1]

wTypeメンバーはパス要素の形式を示します。TT_PRIM_LINEは直線、TT_PRIM_QSPLINEは二次ベジエ曲線、TT_PRIM_CSPLINEは三次ベジエ曲線ということになっていますが、三次ベジエ曲線にTT_PRIM_QSPLINEが設定されている場合があるようです。なので、TT_PRIM_QSPLINEが指定されている場合は次のcpfxが2の倍数の場合は二次スプライン、3の倍数の場合は三次スプライン、6の倍数の時はあきらめてサイコロを振るといった対応が必要です。

cpfxメンバーはパス要素を構成する点の数を示します。TTPOLYCURVE構造体はapfxの要素数1で定義されていますが、実際にはcpfxで示される数のapfx要素があることに注意してください。直線要素 (TT_PRIM_LINE)の場合は1以上、二次ベジエ曲線 (TT_PRIM_QSPLINE)の場合は2以上の2の倍数、三次ベジエ曲線 (TT_PRIM_CSPLINE)の場合には3以上の3の倍数となります。
# ただ、TT_PRIM_QSPLINEとcpfxが3という組み合わせがあったので、ちょっと注意が必要です。

apfxメンバーはパス要素を構成する点の列で、POINTFX形式です。PolyBezierToは三次ベジエ曲線しか受け付けないので、二次ベジエ曲線を出力する際は端点と制御点から三次ベジエ曲線の制御点を計算する必要があります。
# 二次ベジエ曲線の端点をP1, P2,制御点をCとすると、C1=(P1+2*C)/3, C2=(P2+2*C)/3 だそうです。

フォントのアウトラインパスは必ず閉じているので、パスを一つ描画したらCloseFigureでパスを閉じておく必要があります。

といったところが基本的な話です。

で、今回試していたのはTextOutで出力した文字列とGetGlyphOutlineで出力したアウトラインが一致するかというテスト。

しかし、それ以前で困ったことが起きているので、テストには至っていません。

一つの謎はCloseFigureでパスを閉じているにもかかわらず、パスが閉じないこと。アウトラインをとっているので、アウトラインの一部が欠けた状態となっています。このため、アウトラインの一致が完全には確認できません。

ほかにも微妙に形状がおかしいところがあり、小数部をきちんと処理していないことが影響しているような気もします。

とりあえず、CloseFigureが閉じないことをどうにかしないと。
コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Win32で遊びはじめてみました... | トップ | Win32で遊びはじめてみました... »
最新の画像もっと見る

コメントを投稿

Weblog」カテゴリの最新記事