Archives

Archives / 2021 / January
  • WPF, Text Rendering and the Blues

    I am currently working on a small webcam utility written in C#/WPF that I intend to release as freeware later this year.

    On the main window, I use a ComboBox for selecting a webcam in a dropdown list, with the toggle button of the control styled to look like a heading:

    When you move the mouse pointer over the toggle button, the text and the glyph turn blue – nothing special.

    The application does not have a main menu, only a settings menu that you open by clicking a gear icon:

    The icon also turns blue when the pointer hovers over the general area:

    But wait a second… is this the same blue? A quick check:

    • The XAML says the text color is #0987c3.
    • The color picker in Photoshop tells me that the color of most icon pixels is… also #0987c3!

    What is going on?

    On a screenshot zoomed to 300%, the gear icon looks like this:

    And this is the text at 300% size:

    The colored pixels to the left and the right of the characters are caused by ClearType. You will also notice that many pixels “inside” the character shapes also have a different color. This is because less pixels than one might expect are fully covered in blue – which means that more pixels have their color modified by ClearType.

    What did I do wrong?

    It is not what I did do, but what I did not do. I did not tweak WPF’s text rendering to suit my needs. Which sounds curious, because writing a desktop application that looks good at typical application font sizes is not exactly an exotic usage scenario.

    When WPF was developed, it was designed to be device-independent and ready for a future of high pixel density displays (“HiDPI”). So WPF rendered text exactly as specified by the font metrics, which on normal density displays led to blurry characters. It took until .NET 4 for WPF to offer a way to render small text with an emphasis on readability instead of correctness (read this old article by Scott Hanselman for a look way back in time).

    What to do

    As a WPF developer, you can influence the text rendering by using the attached properties TextOptions.TextFormattingMode and TextOptions.TextRenderingMode. You apply them directly on an element, e.g., a TextBlock or set them on a window.

    TextOptions.TextFormattingMode

    The property TextOptions.TextFormattingMode can be set to Display or Ideal (which is the default).

    • With Ideal, text is rendered according to the font's metrics. Which is fine for large text, but not so much for small text on a non-HiDPI display.
    • With Display, the text rendering favors of aligning letters to pixels. This is especially important for small text on normal density screens.

    TextOptions.TextRenderingMode

    The property TextOptions.TextRenderingMode controls the way rendered text will be anti-aliased. Possible values are Aliased (which we will see can be confusing), Auto (the default), ClearType, and Grayscale.

    To show off the possible combinations, I placed the gear icon directly in front of the text (where it does not make any sense, just for comparing colors):

    Display Aliased
    Display Auto
    Display ClearType
    Display Grayscale
    Ideal Aliased
    Ideal Auto
    Ideal ClearType
    Ideal Grayscale

    So many options…

    In a first round, I eliminated:

    • Display&Aliased (no anti-aliasing),
    • Ideal&ClearType (the combination that caused me to write this article in the first place),
    • Ideal&Auto (which in my simple scenario looks the same as Ideal&ClearType), and
    • Display&ClearType (even though it looks better than Ideal&ClearType)

    Which left me with the following options:

    Display Grayscale
    Ideal Aliased
    Ideal Grayscale

    In the end, I decided to use Ideal&Aliased because I wanted the bolder text without a larger font size. And Segoe UI in “real” bold does not look good at this font size. Interesting that with Ideal, the setting Aliased is actually anti-aliased…

    I set TextOptions.TextFormattingMode="Display" on my application window but did not specify TextOptions.TextRenderingMode there.

    For the dropdown list, I set TextOptions.TextFormattingMode="Ideal" and TextOptions.TextRenderingMode="Aliased" on the ContentPresenter of the toggle button.

    A note on “Display” vs. “Ideal” used with “Grayscale”

    While writing this blog post, I had to juggle a larger number of image files than usual. When I saw the letter “P” in the enlarged versions of Display&Grayscale and Ideal&Grayscale, I first suspected I had mixed up the screenshots. But that was not the case; it is just a coincidence that the “H” and the “P” look sharper.

    This becomes clear when you look at a different text:

    Display Grayscale
    Ideal Grayscale

    Note the way the “m” is rendered and how more vertical lines are aligned to full pixels in Display mode.

    A good example for small sample sizes leading to wrong conclusions…