bLi*_*ght 9 windows delphi winapi high-resolution text-rendering
出于某些奇怪的原因,调用WinAPI的ExtTextOutW函数在高分辨率位图(2560x1440/3840x2160)上绘制剪切文本会在使用Creators版本更新更新Windows 10后导致性能降低~x50.从我的用户的测试和调试日志中可以看出,位图或字体大小的细微差别可能会触发性能损失.
这是一个显示性能命中的调试日志:
10/05/2017 15:51:50 [ 63227,186] : Calculate Rect
10/05/2017 15:51:50 [ 63227,190] : Rect : Left=263, Top=504, Right=3561, Bottom=2155
10/05/2017 15:51:50 [ 63227,193] : Set Shadow Color
10/05/2017 15:51:50 [ 63227,198] : Render Text Shadow
10/05/2017 15:51:50 [ 63236,650] : Set Text Color
10/05/2017 15:51:50 [ 63236,661] : Render Text "Kingdom come Deliverance"
10/05/2017 15:51:50 [ 63246,062] : Rendering complete
Run Code Online (Sandbox Code Playgroud)
从日志中可以看出,对ExtTextgOutW的单次调用需要大约9.5ms,而同一次调用在创建者更新之前的1ms内完成.
以下是您可以与上面的调试输出进行比较的实际代码:
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Calculate Rect');{$ENDIF}
cRect := Rect(X,Y,Width+X,MainForm.Monitor.Height-(1+(MainForm.Monitor.Height div 540)));
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Rect : Left='+IntToStr(cRect.Left)+', Top='+IntToStr(cRect.Top)+', Right='+IntToStr(cRect.Right)+', Bottom='+IntToStr(cRect.Bottom));{$ENDIF}
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Set Shadow Color');{$ENDIF}
srcColor := txtCanvas.Font.Color;
txtCanvas.Font.Color := OutLineColor;
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Render Text Shadow');{$ENDIF}
Windows.ExtTextOutW(txtCanvas.Handle,X ,Y+(MainForm.Monitor.Height div 540),ETO_CLIPPED,@cRect,@S[1],I,nil);
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Set Text Color');{$ENDIF}
txtCanvas.Font.Color := srcColor;
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Render Text "'+S+'"');{$ENDIF}
Windows.ExtTextOutW(txtCanvas.Handle,X ,Y ,ETO_CLIPPED,@cRect,@S[1],I,nil);
{$IFDEF TEXTRENDERTRACE}DebugMsgFT('c:\log\.TextRender.txt','Rendering complete'+CRLF);{$ENDIF}
Run Code Online (Sandbox Code Playgroud)
此代码通过将相同的文本呈现两次,并且Y偏移和颜色略有不同,从而实现了非常简单的阴影效果.
以下是我的论坛用户的完整讨论,我们尝试在各种硬件上调试问题(帖子中包含其他调试日志):http: //forum.inmatrix.com/index.php?showtopic = 14995&page = 2
我们在DPI设置为100%的情况下进行了测试,以确保触发器与Creators Edition中引入的DPI更改无关.
有谁知道是什么引发了这个?是否有解决方法?
*****更新1*****
至少在初始测试中,"DrawTextExW"似乎也受到性能损失的影响.在测试期间使用的字体是Arial,并且性能问题似乎与字体的大小有关,因为用户报告在屏幕上添加更多较小尺寸的行(更多文本以较低分辨率呈现)大大提高了性能.
*****更新2*****
我写了一个小工具来描述这个问题,你可以在这个GitHub存储库中找到它:https: //github.com/bLightZP/WindowsTextRenderingProfiler
似乎问题取决于字体大小,例如,在2560x1440屏幕上,渲染一行"Arial"字体文本,大小为"35"21ms来渲染,而大小为"34"需要2ms.
这被呈现为具有32位像素格式的Delphi TBitmap的HDC,并且禁用剪辑仅对性能具有较小影响.
*****更新3*****
Sebastian Z的答案确实恢复了预创作者版本的性能水平,我已经更新了GitHub上的示例代码以反映他的答案,但我已经能够在Windows 7 64位和1920x1080屏幕上重现该问题,所以它是不限于Windows 10创建者版或高分辨率显示器,当字体质量设置为ANTIALIASED时,只需触发阈值更高.在我在Windows 7下的测试中,使用字体Arial,触发点的字体大小为"109"(快),而字体大小为"110"(x10速度较慢或性能较差).在使用Sebastian Z的答案来禁用cleartype之后,Windows 10中存在相同的触发阈值.
Delphi 使用lfQuality := DEFAULT_QUALITY;. 默认质量曾经是抗锯齿质量。但自从 Windows 10 Creators 更新后,现在默认为cleartype。而且这个速度相当慢。因此,解决方案是手动强制抗锯齿质量。
如果您使用的是当前的 Delphi 版本,那么您可以简单地设置 Font.Quality 属性:
Procedure RenderText(oBitmap : TBitmap; X,Y : Integer; cRect : TRect; S : WideString; testFunction : Integer; TxtEffect : Integer; EffectColor : TColor; Clipping : Boolean);
// [...]
begin
obitmap.Canvas.Font.Quality := fqClearType;
Run Code Online (Sandbox Code Playgroud)
在旧的 Delphi 版本中,情况有点复杂:
var
lf: TLogFont;
begin
if GetObject(oBitmap.Canvas.Font.Handle, SizeOf(TLogFont), @lf) = sizeof(TLogFont) then
begin
lf.lfQuality := ANTIALIASED_QUALITY;
oBitmap.Canvas.Font.Handle := CreateFontIndirect(lf);
end;
Run Code Online (Sandbox Code Playgroud)
这在 Windows 10 Creators Update 中是一个相当大的问题,因为 ClearType 文本并不总是合适,并且可能会导致意外结果。