环境
- Core UI:
v1.7.0
- Windows 11
- Direct2D / DirectWrite
- 小字号 UI 文本(约 12–13 DIP)
问题描述
启用 UI_TEXT_RENDER_SHARP 后,小字号文字虽然笔画更锐利,但同一行内会出现局部字距忽宽忽窄的问题。UI_TEXT_RENDER_GRAY_SHARP 也使用相同的 GDI_CLASSIC rendering mode,因此存在同类风险。
问题不是 GDI_CLASSIC 本身,而是 Core UI 只切换了字形的 rendering mode,却仍然使用默认的 natural metrics 创建、测量和布局文字,导致渲染模式与度量模式不匹配。
最小复现
ui_theme_set_text_render_mode(UI_TEXT_RENDER_SHARP);
随后用 12–13 DIP 字号显示一行中英文混排或连续中文,例如:
与 UI_TEXT_RENDER_CLEARTYPE 对比,SHARP 模式下局部字符间距会显得不均匀。
根因分析
Renderer::ApplyTextRenderMode() 把 SHARP 和 GRAY_SHARP 设置为 DWRITE_RENDERING_MODE_GDI_CLASSIC:
https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1034-L1070
但核心绘制路径仍统一调用 IDWriteFactory::CreateTextLayout():
https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1231-L1283
文字宽度、高度和公开 layout helper 同样使用 CreateTextLayout():
https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1286-L1346
微软对 DWRITE_RENDERING_MODE_GDI_CLASSIC 的说明明确要求使用 DWRITE_MEASURING_MODE_GDI_CLASSIC 获取 glyph advances:
https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_rendering_mode
而 CreateGdiCompatibleTextLayout(..., useGdiNatural = FALSE, ...) 才会让 text layout 使用对应的 GDI Classic metrics:
https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefactory-creategdicompatibletextlayout
当前实现只通过 SetTextRenderingParams() 改变 raster rendering,没有同步改变 text layout 的 metrics。
期望行为
选择 UI_TEXT_RENDER_SHARP 时,文字的绘制、宽度测量、换行、ellipsis、caret 和 hit-test 应使用一致的 GDI Classic metrics,避免视觉字距与布局宽度不一致。
建议修复
建议集中封装 text layout 创建逻辑:
SMOOTH / CLEARTYPE:继续使用 CreateTextLayout();
SHARP / GRAY_SHARP:使用 CreateGdiCompatibleTextLayout(),useGdiNatural = FALSE;
pixelsPerDip 使用当前窗口 DPI,而不是固定 1.0f;
- 绘制、MeasureText、TextArea 缓存布局、caret/hit-test 和 SVG 文字等路径共用同一策略;
- 文字模式或 DPI 改变时清理相关 layout cache。
建议补充不同 DPI(100% / 125% / 150%)和 11–14 DIP 字号的截图回归测试,并核对 GetMetrics() 与实际绘制 advance 一致。
环境
v1.7.0问题描述
启用
UI_TEXT_RENDER_SHARP后,小字号文字虽然笔画更锐利,但同一行内会出现局部字距忽宽忽窄的问题。UI_TEXT_RENDER_GRAY_SHARP也使用相同的GDI_CLASSICrendering mode,因此存在同类风险。问题不是
GDI_CLASSIC本身,而是 Core UI 只切换了字形的 rendering mode,却仍然使用默认的 natural metrics 创建、测量和布局文字,导致渲染模式与度量模式不匹配。最小复现
随后用 12–13 DIP 字号显示一行中英文混排或连续中文,例如:
与
UI_TEXT_RENDER_CLEARTYPE对比,SHARP 模式下局部字符间距会显得不均匀。根因分析
Renderer::ApplyTextRenderMode()把 SHARP 和 GRAY_SHARP 设置为DWRITE_RENDERING_MODE_GDI_CLASSIC:https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1034-L1070
但核心绘制路径仍统一调用
IDWriteFactory::CreateTextLayout():https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1231-L1283
文字宽度、高度和公开 layout helper 同样使用
CreateTextLayout():https://github.com/ghboke/core-ui/blob/v1.7.0/src/ui/renderer.cpp#L1286-L1346
微软对
DWRITE_RENDERING_MODE_GDI_CLASSIC的说明明确要求使用DWRITE_MEASURING_MODE_GDI_CLASSIC获取 glyph advances:https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_rendering_mode
而
CreateGdiCompatibleTextLayout(..., useGdiNatural = FALSE, ...)才会让 text layout 使用对应的 GDI Classic metrics:https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefactory-creategdicompatibletextlayout
当前实现只通过
SetTextRenderingParams()改变 raster rendering,没有同步改变 text layout 的 metrics。期望行为
选择
UI_TEXT_RENDER_SHARP时,文字的绘制、宽度测量、换行、ellipsis、caret 和 hit-test 应使用一致的 GDI Classic metrics,避免视觉字距与布局宽度不一致。建议修复
建议集中封装 text layout 创建逻辑:
SMOOTH/CLEARTYPE:继续使用CreateTextLayout();SHARP/GRAY_SHARP:使用CreateGdiCompatibleTextLayout(),useGdiNatural = FALSE;pixelsPerDip使用当前窗口 DPI,而不是固定1.0f;建议补充不同 DPI(100% / 125% / 150%)和 11–14 DIP 字号的截图回归测试,并核对
GetMetrics()与实际绘制 advance 一致。