开发者

TextBlock inside a Viewbox - strange rendering

开发者 https://www.devze.com 2023-01-29 16:52 出处:网络
This is a question regarding a very simple construction - I have the following XAML: <Viewbox Height=\"100\" Stretch=\"Uniform\">

This is a question regarding a very simple construction - I have the following XAML:

    <Viewbox Height="100" Stretch="Uniform">
        <TextBlock FontFamily="Georgia">My Cool Text</TextBlock>
    </Viewbox>

This is quite 开发者_如何学Csimple to understand. Yet when I start the program I get strange blurry text (there are no bitmap effects anywhere in my project).

TextBlock inside a Viewbox - strange rendering

(left side - the designer view in VS2010, right side - the running application)

Does anyone have ANY suggestions about why this is happening??


While Jefim has correctly answered his own question, I wanted to explain why you get this behaviour when using this particular set of features. Jefim suggests that this is a bug in WPF, but it's not. The problem arises as a result of asking for WPF to do something impossible. It has to pick a compromise when you've asked for this impossible thing, and the result is what you see above.

The explanation is a bit long for a comment, which is why I'm putting it in a separate answer.

This example uses two mutually contradictory features of WPF. Those features are:

  1. The ability to render visuals consistently at any scale
  2. The ability to render text in the same way GDI32 renders text

You can't use both features at once. GDI32 renders text in a way that cannot be scaled consistently: if a particular piece of text at a certain font size happens to be 200 pixels wide, if you multiply the font size by 3, and render the same text in the same font family at that new font size, in GDI32 it probably won't be 600 pixels - it'll be close, but it will typically not be quite right.

GDI32 messes with the shapes and widths of characters in order to enhance the clarity and sharpness of the text. Specifically, it bends letters out of shape so that their features align better with the pixels on your screen. And where necessary, it will adjust the width of individual characters to be an exact number of pixels wide. Since this letter bending is all based around actual pixels, it bends text in different ways at different font sizes.

While that gives you nice sharp looking text, it looks absolutely horrible if you try to change the scale gradually. If you try to animate the font size of some text rendered in this fashion, the thing will seem to shimmer and shudder, because the adjustments made in the name of clarity end up being slightly different at each font size. Even if you're not animating, it can still produce poor results - if you have a single typeface shown at a number of sizes, it can look quite different at each size; if your application has a zoom feature, the character of the text can seem to change significantly as you zoom in and out. (And so can the layout. If you use Microsoft Word, you may have noticed that you sometimes get odd-looking extra wide spaces between certain words. This is the result of Word fighting with GDI32 - Word attempts to keep the on-screen layout as close as possible to how things will look when printing, which means it sometimes clashes with GDI32's grid fitting.)

So WPF offers a different approach to text rendering: it can render text in a way that is as faithful as possible to the original design of the font. This distorts the text less, and it means that you don't get discontinuities as you scale.

The downside is that the text looks blurry, compared to how text rendered by GDI32 looks. (The distortions made by GDI32 are all aimed at improving clarity.)

So in WPF 4.0, Microsoft added the ability to render text in the way GDI32 would. That's what TextOptions.TextFormattingMode="Display" does.

By turning on that option, you are saying "I don't need consistent scaling, and I'd prefer clarity, so generate the same pixels you would have done in GDI32." If you then go on to apply scaling, having told WPF you didn't need scalability, you get crappy results. WPF carefully generated a bitmap representation of the text exactly to your specifications, and you then told it to render that text at a different scale. And so it looks like what it is: a scaled bitmap of some text that was generated for a different resolution.

You could argue that WPF could do something different here: if you apply a scale transform in GDI32 you'd see different behaviour - you'd see the inconsistency at different scales described earlier. If you really want that effect in WPF you can get it by modifying the font size directly. But WPF doesn't prioritise getting that same effect - its goals are to make it possible to get GDI32-style crisp text when you really need it, and to provide consistent scaling by default.

And what you're running into here is the "consistent scaling". Turning on GDI32-style text rendering doesn't break consistent scaling: applying a scale factor (either directly, via a ScaleTransform or indirectly via a Viewbox) will change the dimensions of the visual by exactly the specified scale factor. If it were to re-generate the text visuals by grid fitting to the newly scaled size, the text would come out at a different width. That would actually cause the Viewbox problems: it applies a scale factor based on the content's natural size, designed to make it fit the available space. But if it re-did the grid fit after scaling, that would actually change the width. Because of the inconsistencies inherent in how GDI32 text rendering works, it might not even be possible for the ViewBox to find a suitable scale - it's possible to come up with a piece of text which, when rendered in a particular font, will never come out at 200 pixels wide. For some font sizes, the rounding inherent in grid fitting might bump the size down to, say, 198, and it might stick at that as you make tiny increments in the font size, until you go past some threshold at which point it might jump to 202 pixels.

For a Viewbox attempting to force the text to fit into exactly 200 pixels, that would be a problem. But Viewbox doesn't work that way - it uses WPF's consistent scaling, downstream of the point at which you chose the font size to which GDI32-style text rendering is working. So Viewbox will always be able to do what it is designed to do, but that is task that's fundamentally incompatible with GDI32-style text rendering.

So in short, WPF renders the text for the font size you requested, and then scales the result.

So you have to pick just one feature - you can't have both because that's simply impossible. Either don't attempt to render text in a context in which an arbitrary scale factor may be applied (e.g. Viewbox) or don't turn on GDI32-style text rendering. Otherwise, you get that weird pixelated text that you've encountered.


Ok, bug found. My Window style has the following setter:

    <Setter Property="TextOptions.TextFormattingMode" Value="Display"/>

If I set it back to "Ideal" (which is the default value) then it renders the text inside the viewbox correctly. I would say that this is a bug inside WPF. Basically, if you try this:

<Viewbox Height="100" Stretch="Uniform" TextOptions.TextFormattingMode="Display">
    <TextBlock FontFamily="Georgia">My Cool Text</TextBlock>
</Viewbox>

You will get the same result as in my initial picture.

0

精彩评论

暂无评论...
验证码 换一张
取 消