Skip to content

Instantly share code, notes, and snippets.

@k-takata
Created January 16, 2013 12:36
Show Gist options
  • Save k-takata/4546842 to your computer and use it in GitHub Desktop.
Save k-takata/4546842 to your computer and use it in GitHub Desktop.
additional patch No.5 for vim-kaoriya-vim-mq-ex / patch-direct_write.diff (Use GDI compatible layouts.)
diff --git a/src/gui_dwrite.cpp b/src/gui_dwrite.cpp
--- a/src/gui_dwrite.cpp
+++ b/src/gui_dwrite.cpp
@@ -14,6 +14,10 @@
#include "gui_dwrite.h"
+extern "C" {
+#include "vim.h"
+}
+
#ifdef __MINGW32__
# define __maybenull SAL__maybenull
# define __in SAL__in
@@ -39,10 +43,13 @@
public:
GdiTextRenderer(
IDWriteBitmapRenderTarget* bitmapRenderTarget,
- IDWriteRenderingParams* renderingParams) :
+ IDWriteRenderingParams* renderingParams,
+ FLOAT dpiScaleX) :
cRefCount_(0),
pRenderTarget_(bitmapRenderTarget),
- pRenderingParams_(renderingParams)
+ pRenderingParams_(renderingParams),
+ dpiScaleX_(dpiScaleX),
+ offset_(0.0)
{
pRenderTarget_->AddRef();
pRenderingParams_->AddRef();
@@ -97,14 +104,31 @@
// Pass on the drawing call to the render target to do the real work.
RECT dirtyRect = {0};
+ // Adjust glyphAdvances and baselineOriginX to fit with the layout of
+ // GDI. Using CreateGdiCompatibleTextLayout() is not enough.
+ DWRITE_GLYPH_RUN glyphRunAdj = *glyphRun;
+ FLOAT* glyphAdvances = new FLOAT[glyphRun->glyphCount];
+ glyphRunAdj.glyphAdvances = glyphAdvances;
+ FLOAT delta = 0.0;
+ for (int i = 0; i < glyphRun->glyphCount; i++) {
+ if (glyphRun->glyphAdvances[i] > gui.char_width * 1.5 / dpiScaleX_)
+ glyphAdvances[i] = gui.char_width * 2 / dpiScaleX_;
+ else
+ glyphAdvances[i] = gui.char_width / dpiScaleX_;
+ delta += glyphAdvances[i] - glyphRun->glyphAdvances[i];
+ }
+
hr = pRenderTarget_->DrawGlyphRun(
- baselineOriginX,
+ baselineOriginX + offset_,
baselineOriginY,
measuringMode,
- glyphRun,
+ &glyphRunAdj,
pRenderingParams_,
context->color,
&dirtyRect);
+ offset_ += delta;
+
+ delete [] glyphAdvances;
return hr;
}
@@ -188,6 +212,8 @@
unsigned long cRefCount_;
IDWriteBitmapRenderTarget* pRenderTarget_;
IDWriteRenderingParams* pRenderingParams_;
+ FLOAT dpiScaleX_;
+ FLOAT offset_;
};
struct DWriteContext {
@@ -477,9 +503,15 @@
HDC memdc = bmpRT->GetMemoryDC();
BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY);
+#if 1
+ hr = mDWriteFactory->CreateTextLayout(
+ text, len, mTextFormat, static_cast<FLOAT>(w),
+ static_cast<FLOAT>(h), &textLayout);
+#else
hr = mDWriteFactory->CreateGdiCompatibleTextLayout(
text, len, mTextFormat, static_cast<FLOAT>(w),
static_cast<FLOAT>(h), mDpiScaleX, NULL, TRUE, &textLayout);
+#endif
if (SUCCEEDED(hr))
{
@@ -493,7 +525,7 @@
if (SUCCEEDED(hr))
{
GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT,
- mRenderingParams);
+ mRenderingParams, mDpiScaleX);
GdiTextRendererContext data = { color };
textLayout->Draw(&data, renderer, 0, 0);
SafeRelease(&renderer);
@koron
Copy link

koron commented Jan 16, 2013

このコードを書き起こすに当たって、参考にした資料(URLとか)ってありますか?
GdiTextRenderer はMSが出していたサンプルの丸コピーなので、
この修正がRendererの書き方の仕様や作法に対して妥当であるかどうかの検証がしたいのです。

直感的には glyphRun.glyphAdvances を補正するのは
DrawGlyphRun ではない別の場所になりそうな気がしてます。
本来は Layout のほうをカスタムしてやるんじゃないかなぁ、というのが直感。

あと offset_ に差 (delta) が蓄積してんだけど、
Yが異なる=不要な offset_ が適用されることは無いのかしら?

@koron
Copy link

koron commented Jan 16, 2013

gui.char_width は GdiTextRenderer から直接参照するんじゃなくて
GdiTextRendererContext を経由して参照したほうが良い。
今のままでは結合が強くなりすぎてる。

あと DWriteContext::DrawText に gui.char_width を引数として渡すようにしたほうが良いだろう。
名前は singleCellWidth とか。
描画に必要なパラメータは、すべて DrawText の引数経由で渡していることを尊重すべし。

@koron
Copy link

koron commented Jan 16, 2013

あと offset_ に差 (delta) が蓄積してんだけど、
Yが異なる=不要な offset_ が適用されることは無いのかしら?

検証の結果、これは実質無さそう。
というのは DrawText が行単位でしか呼ばれておらず、
またDrawText の呼び出し1回に付き1つRendererを作っているから。

@koron
Copy link

koron commented Jan 16, 2013

ちょいと私の考え&要求を以下にまとめます。

  1. このコードを書き起こすに当たって、参考にした資料(URLとか)ってあったら教えてください。
  2. CreateGdiCompatibleTextLayout を使わない理由を教えてください。(glyphAdvances に入ってくる値が使い物にならなくなるから、と推測しています)

@k-takata
Copy link
Author

このコードを書き起こすに当たって、参考にした資料(URLとか)ってあったら教えてください。

ないです。

CreateGdiCompatibleTextLayout を使わない理由を教えてください。

CreateGdiCompatibleTextLayout を使っても CreateTextLayout を使っても、glyphAdvances の値を上書きしてしまっているので、同じ結果になっているはずです。(少なくとも私には判別できませんでした。)
measuringMode の値が違っているかもしれませんので、サブピクセルレベルでは配置が異なっている可能性がありますが、未確認です。

本来は Layout のほうをカスタムしてやるんじゃないかなぁ、というのが直感。

そういうやり方を探してはいるのですが、見つかってないです。

あと offset_ に差 (delta) が蓄積してんだけど、
Yが異なる=不要な offset_ が適用されることは無いのかしら?

行が異なる場合はoffset_が0に初期化されるので問題ないです。
一回の textLayout->Draw() の呼び出しで、DrawGlyphRun() が複数回呼ばれるので、それを補正しているのが offset_ です。

@k-takata
Copy link
Author

GdiTextRendererContext を経由して参照したほうが良い。
今のままでは結合が強くなりすぎてる。

それはそうですね。

@koron
Copy link

koron commented Jan 16, 2013

Thanks! んでは、続きは私の方で取り込んでみます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment