From: Yasuhiro Matsumoto Date: Sat, 31 Jan 2026 16:23:28 +0000 (+0000) Subject: patch 9.1.2125: MS-Windows: DirectX rendering can be improved X-Git-Tag: v9.1.2125^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b39b065d6fde5f214feebab882b8ebba78945035;p=thirdparty%2Fvim.git patch 9.1.2125: MS-Windows: DirectX rendering can be improved Problem: MS-Windows: DirectX rendering can be improved Solution: Use lpDx advance, fix vertical text shift and correct font-size calculations (Yasuhiro Matsumoto). This changes DirectWrite rendering to: - Respect lpDx-provided advance widths instead of always adjusting to cell width. - Use a fixed baseline derived from cell geometry to prevent vertical shifts when font fallback occurs (e.g. with CJK characters). - Correctly convert LOGFONT lfHeight to DirectWrite font size using font metrics for closer GDI compatibility. closes: #19254 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- diff --git a/src/gui_dwrite.cpp b/src/gui_dwrite.cpp index 4f24390775..b228d28f55 100644 --- a/src/gui_dwrite.cpp +++ b/src/gui_dwrite.cpp @@ -307,6 +307,8 @@ struct DWriteContext { IDWriteTextFormat *mTextFormat; DWRITE_FONT_WEIGHT mFontWeight; DWRITE_FONT_STYLE mFontStyle; + FLOAT mFontSize; // Font size in pixels (em height) + FLOAT mFontAscent; // Font ascent in pixels D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode; @@ -367,7 +369,10 @@ public: AdjustedGlyphRun( const DWRITE_GLYPH_RUN *glyphRun, FLOAT cellWidth, - FLOAT &accum) : + FLOAT &accum, + const INT *lpDx, + int *glyphIndex, + int cchText) : DWRITE_GLYPH_RUN(*glyphRun), mAccum(accum), mDelta(0.0f), @@ -377,7 +382,18 @@ public: for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) { FLOAT orig = glyphRun->glyphAdvances[i]; - FLOAT adjusted = adjustToCell(orig, cellWidth); + FLOAT adjusted; + + if (*glyphIndex < cchText) + { + adjusted = FLOAT(lpDx[*glyphIndex]); + (*glyphIndex)++; + } + else + { + adjusted = adjustToCell(orig, cellWidth); + } + mAdjustedAdvances[i] = adjusted; mDelta += adjusted - orig; } @@ -403,9 +419,13 @@ struct TextRendererContext { // const fields. COLORREF color; FLOAT cellWidth; + const INT *lpDx; + int cchText; + FLOAT baselineY; // Fixed baseline Y coordinate // working fields. FLOAT offsetX; + int glyphIndex; }; class TextRenderer FINAL : public IDWriteTextRenderer @@ -496,8 +516,14 @@ public: TextRendererContext *context = reinterpret_cast(clientDrawingContext); + // Use fixed baseline Y from context instead of DirectWrite's + // baselineOriginY to prevent vertical shifts when font fallback + // occurs (e.g., for CJK characters). + FLOAT fixedBaselineY = context->baselineY; + AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth, - context->offsetX); + context->offsetX, context->lpDx, &context->glyphIndex, + context->cchText); #ifdef FEAT_DIRECTX_COLOR_EMOJI if (pDWC_->mDWriteFactory2 != NULL) @@ -526,7 +552,7 @@ public: pDWC_->mRT->DrawGlyphRun( D2D1::Point2F( colorGlyphRun->baselineOriginX, - colorGlyphRun->baselineOriginY), + fixedBaselineY), &colorGlyphRun->glyphRun, pDWC_->mBrush, DWRITE_MEASURING_MODE_NATURAL); @@ -542,7 +568,7 @@ public: pDWC_->mRT->DrawGlyphRun( D2D1::Point2F( baselineOriginX + context->offsetX, - baselineOriginY), + fixedBaselineY), &adjustedGlyphRun, pDWC_->SolidBrush(context->color), DWRITE_MEASURING_MODE_NATURAL); @@ -619,6 +645,8 @@ DWriteContext::DWriteContext() : mTextFormat(NULL), mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), mFontStyle(DWRITE_FONT_STYLE_NORMAL), + mFontSize(0.0f), + mFontAscent(0.0f), mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) { HRESULT hr; @@ -776,28 +804,25 @@ DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, // Use lfHeight of the LOGFONT as font size. fontSize = float(logFont.lfHeight); + DWRITE_FONT_METRICS fontMetrics; + font->GetMetrics(&fontMetrics); + + // Convert lfHeight to DirectWrite font size if (fontSize < 0) { - // Negative lfHeight represents the size of the em unit. + // Negative lfHeight represents the font's em height in pixels fontSize = -fontSize; } - else + else if (fontSize > 0) { - // Positive lfHeight represents the cell height (ascent + - // descent). - DWRITE_FONT_METRICS fontMetrics; - font->GetMetrics(&fontMetrics); - - // Convert the cell height (ascent + descent) from design units - // to ems. - float cellHeight = static_cast( - fontMetrics.ascent + fontMetrics.descent) - / fontMetrics.designUnitsPerEm; - - // Divide the font size by the cell height to get the font em - // size. - fontSize /= cellHeight; + // Positive lfHeight represents the font's cell height (ascent + descent) + // Convert to em height + fontSize = fontSize * float(fontMetrics.designUnitsPerEm) + / float(fontMetrics.ascent + fontMetrics.descent); } + + // Scale by ascent ratio to match GDI rendering size + fontSize = ceil(fontSize * float(fontMetrics.ascent) / float(fontMetrics.designUnitsPerEm)); } // The text format includes a locale name. Ideally, this would be the @@ -1039,8 +1064,18 @@ DWriteContext::DrawText(const WCHAR *text, int len, textLayout->SetFontWeight(mFontWeight, textRange); textLayout->SetFontStyle(mFontStyle, textRange); + // Calculate baseline using a fixed formula based on cell geometry. + // Do NOT use GetLineMetrics() because it returns different values + // depending on text content (e.g., when CJK characters trigger + // font fallback, the metrics change). + // Use the same baseline calculation for all text to prevent + // vertical shifts during redraw. + FLOAT baselineY = floorf(FLOAT(y) + FLOAT(h) * 0.83f + 0.5f); + TextRenderer renderer(this); - TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; + TextRendererContext context = { color, FLOAT(cellWidth), lpDx, len, + baselineY, 0.0f, 0 }; + textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y)); } diff --git a/src/version.c b/src/version.c index 011a4d1e6d..e18b2c8c3f 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2125, /**/ 2124, /**/