Skip to content

[Win32] Remove obsolete fallback for drawing text without GDI+#3100

Draft
HeikoKlare wants to merge 1 commit into
eclipse-platform:masterfrom
vi-eclipse:issue-3091
Draft

[Win32] Remove obsolete fallback for drawing text without GDI+#3100
HeikoKlare wants to merge 1 commit into
eclipse-platform:masterfrom
vi-eclipse:issue-3091

Conversation

@HeikoKlare

@HeikoKlare HeikoKlare commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

❗This is an experimental PR to test if the current "fallback" logic is actually obsolete.

A check in GC.drawText() make the implementation fall back to the text rendering based on glyphs and their positions calculated by GDI even when GDI+ is used in all cases except when it contains specific characters that GDI cannot handle. This was necessary more than a decade ago because specific Chinese characters were not properly rendered by GDI+. But this is quite non-intuitive as in case GDI+/advance mode is enabled the consumer will usually expected GDI+ to be used for rendering. In addition, the problematic scenario from back then works fine now.

This change thus adapts the GC.drawText() implementation to use plain GDI+ whenever it is enabled and removes all existing fallback code. It also adds a case for not drawing line delimiters when an according style flag is set, which was not considered by the GDI+ implementation yet.

May fix #3091

@github-actions

github-actions Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Test Results (win32)

   33 files  ±0     33 suites  ±0   4m 10s ⏱️ -59s
4 717 tests +1  4 641 ✅ +1  76 💤 ±0  0 ❌ ±0 
1 246 runs  +1  1 222 ✅ +1  24 💤 ±0  0 ❌ ±0 

Results for commit 7269ed0. ± Comparison against base commit 450ece0.

♻️ This comment has been updated with latest results.

@HeikoKlare

Copy link
Copy Markdown
Contributor Author

Interestingly, this change would also have an impact on some issues reported to GEF (@ptziegler fyi).

It seems like the following fix would be come obsolete as I see the behavior also fixed with this PR:

Without this change on 150% monitor:
image

With this change on 150% monitor:
image

For the underlying issue and the snippet posted there, I rather see a change that I would consider a regression caused by this PR:

This is how it looks without this change on 100%:
image

And this is how it looks with this change on 100%:
image

But comparing the position of the text on 100% monitor with its position on 150% monitor, the behavior after this change seems to be even more "correct".

Anyway, it seems like the Win32 GC.drawText() implementation has some severe flaws when using advanced mode / GDI+. I am just a bit afraid that simply removing all the code for falling back to GDI and completely relying on GDI+ for all text rendering when advanced mode is enabled may have unintended side effects. But checking some of the bugs for which this implementation has been made (such as https://bugs.eclipse.org/bugs/show_bug.cgi?id=289244 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=305815) and running the snippets used back then without any issues after this change, hopefully GDI+ was improved so much throughout the years that all these workarounds are not necessary anymore.

A check in GC.drawText() makes the implementation fall back to the text
rendering based on glyphs and their positions calculated by GDI even
when GDI+ is used in all cases except when it contains specific
characters that GDI cannot handle. This was necessary more than a decade
ago because specific Chinese characters were not properly rendered by
GDI+. But this is quite non-intuitive as in case GDI+/advance mode is
enabled the consumer will usually expected GDI+ to be used for
rendering. In addition, the problematic scenario from back then works
fine now.

This change thus adapts the GC.drawText() implementation to use plain
GDI+ whenever it is enabled. The fallback code is preserved but only
executed if a newly introduced system property is enabled. This allows
to switch back to previous behavior in case unexpected regressions are
found. The property is to be removed in a future release in case no
regressions are found.

While adding regression tests for this change it was found that the
GDI+ based drawText() path computed the DRAW_TAB tab stop width from
the width of a single space glyph as measured by GDI+, while the legacy
GDI-based path and plain (non-advanced) GDI both use the GDI text
metric's average character width. These two metrics can differ
substantially, which made tab stops shrink visibly (roughly by half)
compared to plain GDI and the legacy fallback whenever GDI+ rendering
was used. The GDI+ path is changed to reuse the same GDI text metric
already computed by its caller, so tab stops are now sized consistently
across all three rendering paths (plain GDI, default GDI+, and legacy
GDI+ fallback).

Extend GCWin32Tests with additional regression tests covering not only
the previously fixed underline/strikeout invisibility issue, but also
style-decoration combinations, mnemonic underlining, mirrored/RTL text,
complex scripts and charsets (Arabic, Hebrew, CJK, Cyrillic, Greek,
combining diacritics), kerning-sensitive text width, and tab stop
expansion; all comparing the default and legacy (system property
enabled) rendering paths end-to-end.

Add Snippet395 as a manual/visual companion to the automated tests: it
renders the same set of text properties side-by-side and offers two
checkboxes to toggle at runtime, without restarting, between plain GDI
and GDI+ (advanced) rendering, and - while GDI+ is active - between the
default and legacy text rendering path, to visually compare the
resulting output.

May fix eclipse-platform#3091

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@HeikoKlare

HeikoKlare commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Testing approach and additional findings

To validate this change more thoroughly and guard against regressions, I (with assistance from GitHub Copilot) added:

  • 19 additional automated regression tests in GCWin32Tests, comparing rendered output between the default (fixed) GDI+ text rendering path and the legacy fallback restored via the org.eclipse.swt.internal.win32.useGDITextRenderingWithGDIP system property. Beyond the underline/strikeout scenario from the reported issue, coverage now includes:

    • Style-decoration combinations (bold/italic + underline/strikeout)
    • Mnemonic/accelerator underlining
    • Mirrored/RTL text
    • Complex scripts and charsets (Arabic, Hebrew, CJK, Cyrillic, Greek, combining diacritics)
    • Kerning-sensitive text width
    • Tab stop expansion
  • Snippet395, a manual/visual companion snippet that renders the same set of text properties side-by-side, with two checkboxes to toggle at runtime (no restart needed) between plain GDI and GDI+ (advanced) rendering, and - while GDI+ is active - between the default and legacy text rendering path, to visually compare the output.

Finding: tab stop size regression

While building out this coverage, we found that the tab stop width computed by the GDI+ drawText path (drawTextGDIP) differs from both plain GDI and the legacy GDI+ fallback path: it used 8x the width of a single space glyph as measured by GDI+, whereas the other two paths use 8x GDI's average character width (TEXTMETRIC.tmAveCharWidth), matching the long-standing Win32 DrawText()/TabbedTextOut() convention. Since a space glyph is typically narrower than the average character width, this made tab stops shrink noticeably (roughly by half) whenever the default GDI+ path was used, compared to plain GDI and the legacy fallback.

We fixed this by having drawTextGDIP reuse the same TEXTMETRIC its caller already computes, so tab stops are now sized consistently across all three rendering paths.

Note: This tab-stop discrepancy appears to be a pre-existing bug in the GDI+ text rendering path (unrelated to the underline/strikeout issue this PR addresses) that this PR's change to always use that path simply made visible/impactful by default. It's probably worth splitting the tab-stop fix out into its own separate PR, since it fixes an independent, pre-existing defect rather than something introduced by this change - happy to do that if preferred.

snippet395

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

  200 files  ± 0    200 suites  ±0   30m 37s ⏱️ + 4m 10s
4 760 tests +16  4 736 ✅ +16   24 💤 ±0  0 ❌ ±0 
6 908 runs  +16  6 740 ✅ +16  168 💤 ±0  0 ❌ ±0 

Results for commit 7701b89. ± Comparison against base commit 28ddc85.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] Text is not displayed if the font has Underline or Strikeout and GC.setAdvanced(true)

1 participant