Graphics.DrawString 和 TextRenderer.DrawText 哪个可以提供更好的质量?

18
TextRenderer基于GDI,而Graphics.DrawString基于GDI+。在绘制图像时,这两个函数哪一个可以实现更好质量的文本呢?

可能是 https://dev59.com/LVbUa4cB1Zd3GeqPAJpN 的重复问题。 - Ry-
4
我会尽力为您翻译。内容如下:@minitech 这个问题有些不同,而且它还没有得到解决。 - techno
如果这个问题被关闭为重复,那么答案将会去到那个问题上...此外,没有人回答过这个问题。 - Ry-
4个回答

89
我将翻译来自here的回答,以便信息得到传播。请注意,保留HTML标记。

.NET 有两种绘制文本的方式:

  • GDI+graphics.MeasureStringgraphics.DrawString
  • GDITextRenderer.MeasureTextTextRenderer.DrawText

在 .NET 1.1 中,所有文本渲染都使用了 GDI+但是存在一些问题:

  • GDI+ 的无状态特性会导致性能问题,每次调用后设备上下文都会被设置并恢复原始状态。
  • 国际化文本的字形引擎已经多次更新,包括 Windows/Uniscribe 和 Avalon(Windows Presentation Foundation),但尚未更新 GDI+,这导致新语言的国际化呈现支持的质量不同。

因此,他们知道他们想要改变 .NET 框架,停止使用 GDI+ 的文本渲染系统,而使用 GDI。起初,他们希望可以简单地更改:

graphics.DrawString

在编写与Windows相关的应用程序时,我们需要使用旧的DrawTextAPI而不是GDI+。但他们无法使文本换行和间距与GDI+完全匹配

在Windows Forms 2.0中,我们添加了对绘制GDI文本的支持。起初,我们计划通过调整DrawText API来使其完全匹配GDI+的DrawString API。我认为我们已经非常接近了,但是在单词换行和字符间距方面存在根本性差异,作为两个API的普通使用者,Windows Forms无法解决这些问题。

因此,现在我们面临一个问题:我们希望将所有人都切换到新的TextRenderer API,以便文本看起来更好、本地化更好、与操作系统中其他对话框更一致... ...但我们不想破坏那些依赖GDI+测量字符串进行文本对齐计算的人。

因此,他们被迫保留graphics.DrawString以调用GDI+(兼容性原因;那些调用graphics.DrawString的人会突然发现他们的文本换行方式与以前不同)。来自MSDN
在.NET Framework 2.0中引入了基于GDI的TextRenderer类,以提高性能、使文本更加美观,并改进对国际字体的支持。在较早版本的.NET Framework中,使用基于GDI+的Graphics类执行所有文本渲染。GDI和GDI+对字符间距和单词换行的计算方式不同。在使用Graphics类渲染文本的Windows Forms应用程序中,这可能导致使用TextRenderer的控件的文本与应用程序中的其他文本不同。为解决此不兼容性,您可以将UseCompatibleTextRendering属性设置为true以针对特定控件进行设置。要为应用程序中的所有受支持控件设置UseCompatibleTextRenderingtrue,请调用Application.SetCompatibleTextRenderingDefault方法并将参数设置为true
创建了一个新的静态TextRenderer类来包装GDI文本渲染。它有两个方法:
TextRenderer.MeasureText
TextRenderer.DrawText

注意:`TextRenderer`是GDI的包装器,而`graphics.DrawString`仍然是GDI+的包装器。

然后有一个问题是如何处理所有现有的.NET控件,例如:

  • Label
  • Button
  • TextBox

他们想要将它们切换到使用TextRenderer(即GDI),但他们必须小心。可能会有人依赖于他们的控件在.NET 1.1中的绘制方式。因此,“兼容文本渲染”应运而生。

默认情况下,应用程序中的控件的行为与.NET 1.1中的相同(它们是“兼容的”)。

您可以通过调用以下方法来关闭兼容模式:

Application.SetCompatibleTextRenderingDefault(false);

这将使您的应用程序更好、更快,具有更好的国际支持。总之:
SetCompatibleTextRenderingDefault(true)  SetCompatibleTextRenderingDefault(false)
=======================================  ========================================
 default                                  opt-in
 bad                                      good
 the one we don't want to use             the one we want to use
 uses GDI+ for text rendering             uses GDI for text rendering
 graphics.MeasureString                   TextRenderer.MeasureText
 graphics.DrawString                      TextRenderer.DrawText
 Behaves same as 1.1                      Behaves *similar* to 1.1
                                          Looks better
                                          Localizes better
                                          Faster

值得注意的是,GDI+的TextRenderingHint与用于GDI字体绘制的相应LOGFONT Quality之间的映射也很有用:

TextRenderingHint           mapped by TextRenderer to LOGFONT quality
========================    =========================================================
ClearTypeGridFit            CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit            ANTIALIASED_QUALITY (4)
AntiAlias                   ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit    PROOF_QUALITY (2)
SingleBitPerPixel           DRAFT_QUALITY (1)
else (e.g.SystemDefault)    DEFAULT_QUALITY (0)

示例

这里是GDI+(graphics.DrawString)和GDI(TextRenderer.DrawText)文本渲染的一些比较:

GDI+TextRenderingHintClearTypeGridFitGDICLEARTYPE_QUALITY

enter image description here

GDI+:文本呈现提示抗锯齿,GDI:抗锯齿质量。

enter image description here

GDI+: TextRenderingHintAntiAliasGridFit, GDI: 不支持,使用ANTIALIASED_QUALITY:

enter image description here

GDI+TextRenderingHintSingleBitPerPixelGridFitGDIPROOF_QUALITY

enter image description here

GDI+:TextRenderingHintSingleBitPerPixel,GDI:DRAFT_QUALITY。

enter image description here

我觉得很奇怪,DRAFT_QUALITYPROOF_QUALITY 以及 CLEARTYPE_QUALITY 是完全相同的。

另见


1
DRAFT_QUALITYPROOF_QUALITY相同的原因是您选择的字体。 DRAFT_QUALITY只是意味着字体映射器在选择字体时应优先考虑指定的逻辑属性匹配而不是字符质量; PROOF_QUALITY则反转了这种关系。如果字体映射器不必做出选择,则两个值将生成相同的输出。至于为什么这两者都与CLEARTYPE_QUALITY相同,那是因为您的系统启用了ClearType(并且字体支持),因此DRAFT_QUALITYPROOF_QUALITY都使用它。 - Cody Gray
1
不严格相关于问题,但也请注意,当使用超过32000个字符的字符串时,DrawString()会抛出“ExternalException A generic error occurred in GDI+”异常。而TextRenderer.DrawText()则不会。 - Oliver Bock
你如何为TextRenderer设置与质量相关的设置? - AmirSina Mashayekh
@AmirSinaMashayekh Graphics.TextRenderingHint - Ian Boyd
我之前试过了。与 Graphics.DrawString() 不同,TextRenderer.DrawText() 根本不关心图形对象的属性!看看这里 - AmirSina Mashayekh

6
只是我的个人意见:我总是使用Graphics.DrawString,除非我需要为我的(Windows Forms)控件进行自定义绘制。例如,在一个设置了OwnerDraw的列表框中,如果我附加了一个DrawItem事件处理程序来完全绘制项目,包括项目文本。或者在我必须自己绘制的自定义控件中。
在使用Visual Styles并启用它的操作系统上运行的应用程序中,使用Graphics.DrawString绘制的文本与其他控件绘制的常规文本相比看起来“不对劲”。这似乎主要是由于“ClearType”的处理方式(或未处理),尽管我不确定,并且我没有文档支持这一说法。(它有点像在.Net 1.x上绘制文本或从标准样式切换到系统样式等情况下的文本。)
在这种情况下(仅限Winforms控件上的文本绘制),我只使用TextRenderer.DrawText使文本更好地融入其他控件中。
如果“与原生控件融合”不是您关心的问题(因为您想在图像上绘制),那么我会选择Graphics.DrawString。此外,如果您想要打印,您必须使用Graphics.DrawString,因为TextRenderer仅适用于屏幕(而不是打印机画布)。

1

我的个人经验(我只知道这两个区别):

DrawString支持Alpha通道,抗锯齿

TextRenderer支持Uniscribe


0

我只是随便加入一些测试代码:

class Form1: Form
{
    private string str = "hello world hello world hello world";
    private int x = 32, yLabel = 0, yDraw = 64, yRenderer = 32;

    public Form1()
    {
        Font = new Font("Times", 16);

        Label label = new Label();
        label.BorderStyle = BorderStyle.FixedSingle;
        label.AutoSize = true;
        label.Text = str;
        label.Location = new Point(x, yLabel);
        Controls.Add(label);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        SizeF a;

        // TextRenderer
        a = TextRenderer.MeasureText(str, Font);
        TextRenderer.DrawText(e.Graphics, str, Font, new Point(x, yRenderer), Color.Pink);
        e.Graphics.DrawRectangle(new Pen(Color.Blue), x, yRenderer, a.Width, a.Height);

        // DrawString
        e.Graphics.DrawString(str, Font, new SolidBrush(Color.Red), x, yDraw);
        a = e.Graphics.MeasureString(str, Font);
        e.Graphics.DrawRectangle(new Pen(Color.Lime), x, yDraw, a.Width, a.Height);

        base.OnPaint(e);
    }
}

底线是:与简单的标签相比,TextRenderer更准确。

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