Win32 API中绘制格式化文本的最快方法是什么?

14

我正在使用纯Win32 API在C++中实现文本编辑器,现在我正在寻找实现语法高亮的最佳方法。我知道已经有像Scintilla这样的现成控件,但是我这样做只是为了好玩,所以我想自己完成大部分工作。同时,我也希望它快速且轻量化。

从我所学到的知识来看,使用GDI绘制文本最低级的选项是使用TextOut函数。然而,如果我需要不断地更改字体颜色,那么这意味着我需要多次调用TextOut来绘制一个具有混合格式的文本块。这样做是否效率低下呢?当语法高亮和富文本控件实现时,它们是否有可能在后台使用TextOut,还是有其他方法?在GDI中,每种绘制文本的方法是否都是TextOut的高级封装?


1
正确地完成已经是一个挑战。使用Uniscribe显示文本 是解决你需要面对的问题的良好入门。 - MSalters
3个回答

15

DrawText和TextOut都是对ExtTextOut的封装,因此ExtTextOut是底层API。在我的经验中,ExtTextOut非常快,所以我怀疑您不会看到ExtTextOut本身的性能问题。但是,创建/选择字体可能会成为性能问题的来源,因此如果您在字体之间来回切换,则可以通过缓存和重用字体(HFONT),而不是每次使用CreateFont / SelectObject / DeleteObject来实现显着的性能提升。基本上,当您创建新字体后第一次调用SelectObject时,Windows将执行字体匹配过程,以查找最佳物理字体以匹配您请求的逻辑字体。这是一个相当复杂的过程,因此在性能重要的情况下,您需要将其发生的次数最小化。

我很多年前开发过一个丰富的编辑控件,它本质上是Microsoft Word的迷你版。我使用ExtTextOut作为所有文本输出的主要工具。该控件将维护最近使用的字体的字体缓存(默认缓存大小为10个字体)。它支持WYSIWYG布局,因此实际上是使用打印机DC和字体进行所有布局,然后使用屏幕DC和类似的字体呈现与屏幕兼容的版本,因此执行了很多额外的工作,这可能不适用于您的情况。即使如此,其性能在当时的典型硬件上运行良好(例如266 MHz Pentium)。


2
这并不完全准确。例如,ExtTextOut 又使用了像 Uniscribe 这样的 API。而 Uniscribe 可能会为您执行字体替换。因此,如果您正在寻找“最低级”的 API,则 Uniscribe 可能更好。它具有像 ScriptItemize 这样的基元,“将 Unicode 字符串分解为可单独进行形状处理的项目”。 - MSalters
7
虽然ExtTextOut可能会使用Uniscribe API,但Uniscribe最终仍需回调到ExtTextOut进行实际渲染,因此我仍会认为ExtTextOut是"the"低级别API(详情请参见此链接:www.catch22.net/tuts/neatpad/11)。尽管如此,在功能方面,Uniscribe可能是更好的选择,但绘制文本的“最快方式”是使用ExtTextOut。 - cbranch
DrawText和TextOut是ExtTextOut的包装器,这在Windows 7中是正确的,但不适用于Windows XP。除此之外,PolyTextOut是唯一不调用ExtTextOut的文本绘制API。测试PolyTextOut的速度可能会很有趣。 - Elmue
1
另一个加速改进是避免使用像GetTextExtentPoint32等API来测量绘制字符串之前的大小。如果您必须在彼此之后绘制多个字符串(例如每个单词具有不同的颜色),则可以通过GetCurrentPositionEx获取当前绘图位置,并使用MoveToEx更改它。 - Elmue
1
当你首先处理 DC 句柄时,如何缓存字体?例如,通过调用 BeginPaint 获得的 DC 是从缓存的 DC 复制而来的,该 DC 选择了默认字体,那么如何缓存字体 [和字体选择]?据我所理解,当你像这样获取 DC 时,即使你之前已经准备好了字体句柄,你也必须重新选择字体。 - Armen Michaeli

6

与其考虑哪个“绘制文本”函数最快,更有利的做法可能是考虑“如何尽可能地减少需要渲染的文本数量”,通过聪明地选择何时重新绘制/失效以及如何缓存滚动的已渲染文本。


有趣的观点。我之前没有研究过缓存。谢谢你的建议。 - Graeme Hill
3
这不是对问题的回答。如果你例如将大量的日志输出绘制到窗口或ListBox中,那么没有什么可以被重复使用或省略的。 - Elmue

1

对于复杂的用法,您可能需要使用DrawText,因为它比TextOut提供更多的控制。它具有一些基本的格式支持,但不足以满足编辑器的需求。下一步是来自公共控件库的富文本编辑器,几乎可以为您处理所有这些。


1
DrawText是一个包装器函数,还是完全独立的便利函数?我进行了一些非常简单的测试,发现TextOut明显更快。我知道有像Riched32.dll中的RICHEDIT窗口类这样的富文本控件,但如果可能的话,我想使用低级函数自己实现富文本控件。 - Graeme Hill
1
Win32 APIs大多数都是“黑匣子”,但如果记录DrawText对metafile的调用并进行检查,您将看到一系列对ExtTextOutW的调用。 - Tim Sylvester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接