EMF+で文字をレンダリングしているのは?
EMF+のレコードをデバッガで追っていて、面白いことを発見した。GDI+では、文字の描画は、Graphics::DrawStringメソッドを使うのだが、実は、その内部では、ExtTextOutが使われているのだ。ということは、逆に言えば、フォントのアンチエイリアスは、CreateFont/CreateFontIndirectしているところで、LOGFONT構造体のlfQualityをいじって、強制的にアンチエイリアスをOFFにしてしまえば良いわけだ。そして、どうやら、EmfRecordTypeExtCreateFontIndirect (EMR_EXTCREATEFONTINDIRECTW/構造体だと、EMREXTCREATEFONTINDIRECTW)を処理すればこれができるらしい。
ということで書いてみたコードは次のような感じ。
Graphics g(...); Metafile mf(...); Rect rc(...); // レンダリングしたいサイズで初期化 // 事前にアンチエイリアスをOFFにしておく g.SetSmoothingMode(Gdiplus::SmoothingModeNone); g.SetTextRenderingHint(Gdiplus::TextRenderingHintSingleBitPerPixelGridFit); // 描画 g.EnumerateMetafile(mf, rc, emfProc, &mf, NULL);
そして、本体から呼び出されるコールバックは次のような感じ。
static BOOL CALLBACK emfProc( Gdiplus::EmfPlusRecordType recordType, unsigned int flags, unsigned int dataSize, const unsigned char* pStr, void* callbackData) { BYTE* buf = NULL; Metafile* mf = (Metafile*)callbackData; if(recordType == Gdiplus::EmfPlusRecordTypeSetAntiAliasMode) { // 無視する return TRUE; } else if(recordType == EmfRecordTypeExtCreateFontIndirect) { // pStrを直接書き換えるのはリスクが高いので、 // コピーしてから書き換え、それをPlayRecordに渡す。 buf = new BYTE[dataSize + 8]; pStr = buf; CopyMemory(buf, pStr - 8, dataSize + 8); EMREXTCREATEFONTINDIRECTW& eecfi = *(EMREXTCREATEFONTINDIRECTW*)buf; // アンチエイリアスを強制的にOFFにする eecfi.elfw.elfLogFont.lfQuality = NONANTIALIASED_QUALITY; } BOOL ret = mf->PlayRecord(recordType, flags, dataSize, pStr); if(buf) delete[] buf; // バッファ解放 return ret; }