Graphics.DrawString vs TextRenderer.DrawText?Which can Deliver Better Quality
Asked Answered
I

4

19

TextRenderer is based on GDI and Graphics.DrawString is based on GDI+.Which of these functions can deliver better quality text while drawing text on an image.

Inerney answered 27/11, 2011 at 4:23 Comment(3)
Possible duplicate of #5800146?Dempsey
@minitech The Question is a Bit different and moreover its unresolvedInerney
If this one is closed as a duplicate, the point is that answers will go to that one... moreover, no-one's answered this.Dempsey
J
93

i'm going to cross-post my answer from over here, just so that the information gets around.


There are two ways of drawing text in .NET:

  • GDI+: graphics.MeasureString and graphics.DrawString
  • GDI: TextRenderer.MeasureText and TextRenderer.DrawText

In .NET 1.1 everything used GDI+ for text rendering. But there were some problems:

  • There are some performance issues caused by the somewhat stateless nature of GDI+, where device contexts would be set and then the original restored after each call.
  • The shaping engines for international text have been updated many times for Windows/Uniscribe and for Avalon (Windows Presentation Foundation), but have not been updated for GDI+, which causes international rendering support for new languages to not have the same level of quality.

So they knew they wanted to change the .NET framework to stop using GDI+'s text rendering system, and use GDI. At first they hoped they could simply change:

graphics.DrawString

to call the old DrawText API, instead of GDI+. But they couldn't make the text-wrapping and spacing match exactly as what GDI+ did.

In Windows Forms 2.0, we added support for drawing GDI text. At first we had grandiose plans of poking and prodding at the DrawText API such that we could make it match up exactly how GDI+'s DrawString API works. I actually think we got pretty close, but there are fundamental differences in word wrapping and character spacing that as mere consumers of the both APIs, Windows Forms could not solve.

So now we're presented with a problem: we want to switch everyone over to the new TextRenderer APIs so text will look better, localize better, draw more consistently with other dialogs in the operating system... ...but we dont want to break folks counting on GDI+ measure string for calculations of where their text should line up.

So they were forced to keep graphics.DrawString to call GDI+ (compatiblity reasons; people who were calling graphics.DrawString would suddenly find that their text didn't wrap the way it used to). From MSDN:

The GDI based TextRenderer class was introduced in the .NET Framework 2.0 to improve performance, make text look better, and improve support for international fonts. In earlier versions of the .NET Framework, the GDI+ based Graphics class was used to perform all text rendering. GDI calculates character spacing and word wrapping differently from GDI+. In a Windows Forms application that uses the Graphics class to render text, this could cause the text for controls that use TextRenderer to appear different from the other text in the application. To resolve this incompatibility, you can set the UseCompatibleTextRendering property to true for a specific control. To set UseCompatibleTextRendering to true for all supported controls in the application, call the Application.SetCompatibleTextRenderingDefault method with a parameter of true.

A new static TextRenderer class was created to wrap GDI text rendering. It has two methods:

TextRenderer.MeasureText
TextRenderer.DrawText

Note: TextRenderer is a wrapper around GDI, while graphics.DrawString is still a wrapper around GDI+.


Then there was the issue of what to do with all the existing .NET controls, e.g.:

  • Label
  • Button
  • TextBox

They wanted to switch them over to use TextRenderer (i.e. GDI), but they had to be careful. There might be people who depended on their controls drawing like they did in .NET 1.1.

And so was born "compatible text rendering"

By default, controls in application behave like they did in .NET 1.1 (they are "compatible").

You turn off compatibility mode by calling:

Application.SetCompatibleTextRenderingDefault(false);

This makes your application better, faster, with better international support. To sum up:

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

It's also useful to note the mapping between GDI+ TextRenderingHint and the corresponding LOGFONT Quality used for GDI font drawing:

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)

Samples

Here's some comparisons of GDI+ (graphics.DrawString) verses GDI (TextRenderer.DrawText) text rendering:

GDI+: TextRenderingHintClearTypeGridFit, GDI: CLEARTYPE_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAlias, GDI: ANTIALIASED_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAliasGridFit, GDI: not supported, uses ANTIALIASED_QUALITY:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixelGridFit, GDI: PROOF_QUALITY:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixel, GDI: DRAFT_QUALITY:

enter image description here

i find it odd that DRAFT_QUALITY is identical to PROOF_QUALITY, which is identical to CLEARTYPE_QUALITY.

See also

Journalism answered 22/4, 2014 at 21:29 Comment(5)
The reason that DRAFT_QUALITY is identical to PROOF_QUALITY is because of the font you've chosen. DRAFT_QUALITY just means that the font mapper should prioritize the matching of the specified logical attributes over character quality when selecting a font; PROOF_QUALITY inverts this relationship. If the font mapper doesn't have to make a choice, then the two values will generate the same output. As for why these are both identical to CLEARTYPE_QUALITY, that's because you have ClearType enabled on your system (and font supports), and so both DRAFT_QUALITY and PROOF_QUALITY use it.Pittsburgh
Not strictly related to the question, but also note that DrawString() throws ExternalException A generic error occurred in GDI+ when used with strings over 32000 characters. TextRenderer.DrawText() doesn't.Plutus
How do you set quality related settings for TextRenderer?Hubris
@AmirSinaMashayekh Graphics.TextRenderingHintJournalism
I tried it before. Unlike Graphics.DrawString(), TextRenderer.DrawText() doesn't care about properties of graphics object at all! Look here.Hubris
P
6

Just my 2 cents: I always use Graphics.DrawString, except when I need to do custom painting for my (Windows Forms) controls. For example in a listbox that has OwnerDraw set, if I attach a DrawItem event handler that fully paints items, including item text. Or in a custom control I have to paint myself.

In an application that uses Visual Styles on an OS that supports it and has it enabled, text drawn with Graphics.DrawString looks "off" when compared to regular text drawn by other controls. This appears to be mainly because of differences in the way "ClearType" is (or is not) handled, although I am not sure and I do not have docs to back that statement up. (It sort of looks like the way text did on .Net 1.x or when switching FlatStyle from Standard to System and v.v.)

In such cases only (text painting on Winforms controls) I use TextRenderer.DrawText to make the text better blend in with the other controls.

If "blending in with the natives" is not one of your concerns (which it looks like, since you want to draw on an image) I'd go for Graphics.DrawString. Also, if you'd want printing, you must, since TextRenderer only works on screen (not the printer canvas).

Pulsation answered 8/2, 2012 at 15:12 Comment(0)
T
1

My Personal Experience (I only know these two differences):

DrawString supports Alpha Channel, Anti Aliasing

TextRenderer supports Uniscribe

Tongs answered 26/1, 2012 at 8:55 Comment(0)
I
0

I'll just throw in some test code:

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);
    }
}

Bottom line: compared to a simple Label, TextRenderer is more accurate.

Ineffective answered 3/7, 2014 at 7:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.