Native-looking text in QML 2

Published Wednesday August 8th, 2012 | by

One of the comments in Morten’s blog post about desktop components in QML 2 was that the text looks out of place on Windows. This is because QML 2 uses a custom rendering mechanism called “distance fields” for the text which allows hardware accelerated drawing of transformable text. Yoann blogged about this a year back.

This is what the distance field text looks like in the desktop components:

Qt 5 desktop components rendered with distance field text

Qt 5 desktop components rendered with distance field text

While distance field text is perfect for UI components for which you want smooth animated movement, rotations and zooms, it will look different from the text rendered by native applications on Windows. This is especially true because the distance field text renderer does not apply grid-fitting hints to the glyphs, whereas this is the standard on Windows. Note that on e.g. Mac OS X, the standard is to draw the text without applying grid-fitting, so the distance field text will not look as out-of-place on this platform.

Grid-fitting means that the shapes of the glyphs are modified for the current font size and device resolution in order to make them look sharper. This is done by moving control points around e.g. to avoid features of the glyph being positioned between two pixels, which would cause the anti-aliasing to fill both those pixels, partially blending the font color with the current background in order to give the visual impression that the glyph touches half of each pixel. A sharper image can be made if the feature fills exactly one full pixel instead. The mechanism is especially effective for low pixel densities and small font sizes.

The downside of grid-fitting, in addition to certain typographical consequences, is that the text and its layout becomes unzoomable. This is because the modifications to the glyphs happen based on the target size of the glyph on screen. When you scale it, the shapes of the glyphs will change, making the shapes wobble, and the metrics of the text will also change, requiring a relayout of the text. If you zoom on a web page, for instance, the paragraph you wanted to see up close might have moved around significantly by the time you get to a text size you can comfortably read, and then you will have to pan around to find it again.

When using distance fields, we will render the glyphs without grid-fitting to make them scalable. An additional convenience of this is that we only have to cache the shape of each glyph once, whereas grid-fitted glyphs have to be cached per size. This saves some memory and also makes effects like animated zooms a lot faster, since the glyphs can be redrawn at every zoom level using the same distance field cache.

Here’s what the previous screen shot looks like if you scale the push button by a factor of two:

Distance field rendered text with zoomed button

Distance field rendered text with zoomed button

But while the distance fields have many advantages, they don’t cover the need to have applications that look like they belong together with other Windows applications. For an application using desktop components, you can imagine this being a more important goal than having smoothly transformable text items. On a desktop machine running Windows, the memory cost of a glyph cache might not be the primary concern either. I’ve been meaning to fix this for a while. The code for drawing text through the system back-end instead of the distance field renderer has been in the scene graph since the text nodes were originally written, but so far there has not been any convenient way for developers to choose it over the default. With change 6a16f63df4a51edee03556f841d34aad573870f2 to Qt Declarative, this option has now been added. On any text component in QML, you can now set the renderType property to NativeRendering in order to use the system back-end to rasterize the glyphs instead of Qt. This will apply to any platform, although it will have the largest effect on Windows or on Linux when hinting is turned on in the system settings.

Text {
    text: "Some text"
    renderType: Text.NativeRendering
}

I’ve added the branch qt5-nativetext to the desktop components repository where the effect can be tested. The components in this branch will use the distance field renderer by default, but by setting the DESKTOPCOMPONENTS_USE_NATIVE_TEXT environment variable to “1”, you will be able to see the difference. Running qmlscene on Gallery.qml with this environment variable set yields the following result:

QML 2 widgets with native-looking text

QML 2 widgets with native-looking text

So the appearance is crisper and the text fits in on the platform, but scaling the text does not give the nice results we had before, and will instead look pixelated. See the following screen shot for comparison:

The effect of zooming native-looking text

The effect of zooming native-looking text

So the choice is yours. If you’re targeting Windows and you want your application to look like a standard, native Windows application, you can use the renderType property to achieve this. If you want the lean, flexible and transformable text of Qt, leave the property unaltered.

Did you like this? Share it:

Posted in Qt, Qt Quick, Qt Quick 2, Qt Quick 2.0, Windows

22 comments to Native-looking text in QML 2

sierdzio says:

Great post, thanks! Good to see this going forward.

And what if we turn the NativeRendering on only after the zoom finishes? That would require some logic to be added to scaling methods, sure, but is it at all possible? Or will it also cause the object to change it’s look in an uncanny way?

Mad says:

Wouldn’t the better solution be to implement Sub-pixel antialiasing for the Distance field based text rendering ?

Eskil Abrahamsen Blomfeldt says:

Sierdzio: In that case you will notice the text changing appearance and layout when the animation finishes, but it’s something to try at least. I’ve seen web browsers that have done similar things (scaled the original text layout until the zoom finishes, and relayouting the hinted text when you stop.)

Mad: If you zoom the images of the distance field text, you’ll see that it does indeed do sub-pixel anti-aliasing, and this does indeed make the text sharper than without. But to get perfectly native-looking text on Windows, the hinting needs to be applied, otherwise the shapes and sharpness of the characters will not match those in other applications. The native back-end on Windows does both grid-fitting and sub-pixel anti-aliasing on the glyphs.

Mad says:

Ah, I missed that. Is grid fitting moving the glyphs so they sit on “integer” positions instead of slightly off? Can’t that be done in the shader? Or is it more complicated than that ?

Eskil Abrahamsen Blomfeldt says:

Mad: It’s more complicated than that. The hints are actually small programs in the font file (often generated) which are executed and which alters the shape of each glyph to optimize its appearance in a given size and resolution. This will both alter the glyphs so they line up with the pixel grid (changing the metrics of the glyph) and alter features inside the glyphs to get maximum sharpness at the given size. If you try to animate the font size of a text component in a Windows application, you will see the effect of this, that the glyphs drawn at different sizes are actually not the same glyphs, but variations over the outline stored in the font.

charley says:

Great post. Very interesting, and thanks for the Windows option for “native”.

QUESTION: When everything goes “Retina-display-quality”, will this become a non-issue, because no one will care about “fractions-of-a-pixel” for aligning text glyphs?

Eskil Abrahamsen Blomfeldt says:

Charley: For high density displays, this is not an issue, correct =)

Florian Link says:

There is another issue which need to be mentioned: The native Windows font render makes use of the ClearType font rendering, rendering the anti-aliasing using different colors depending on the arrangement of the LEDs of the LCD display. This is why the zoomed version looks so “colorful”. ClearType text can’t be transformed/zoomed without being re-rendered with the given transformation without looking strange.

yyyy says:

Nice to know this going forward.

But when will Qt5 release?

Scorp1us says:

Wow. Thanks for making this possible!

Benjamin Arnaud says:

Eskil,

Regarding the support of a high density display with Qt 5 what will be the best practice:

– Apply a scale value to the whole GraphicsView canvas.
OR
– Apply the scale from QML: multiplying sizes by a scaling ratio.

Thanks,

B.A.

somebody says:

Tahoma font for windows 7? Doesn’t feel like native.

Eskil Abrahamsen Blomfeldt says:

Benjamin: In Qt 5, the graphics view is not used as back-end for QML. You could however give the root component of your scene a scale. Adding scale factors to every component seems like a lot of work.

I think I would focus on making the QML adaptable by using anchors and relative sizes. Then you can easily swap out images that deserves to be re-rendered for the target resolution (judging from iPhone 3-apps that were run on Retina-screens, this seems to be particularly important for images containing text and other complex shapes.)

This can be combined with adding a scale to the root component, or, if it’s a full screen app where the root component adapts to the screen size, it should just work. I’ve tried this with existing QML apps using the UIKit plugin on iPhone 4, and if the QML is written to be adaptable, you can get very nice results pretty quickly.

faldżip says:

Nice! Finally, after years of development, we can achieve with QML the same result as with Qt Widgets :P

RealNC says:

Does it work on Linux the same way? (And obeying local font settings like anti-aliasing and LCD filters?)

Eskil Abrahamsen Blomfeldt says:

RealNC: Yes. Setting renderType to native will make Qt use the font engine defined in the platform plugin. On a default Linux build this will be FreeType. Basically the font rendering will be equivalent to Qt 4 and QPainter when the property is set.

Nathan Moos says:

I’m not sure if this is the right place to post this but does this mean that a KDE based on Qt5/QML2 apps would use distance-field text rendering?

RealNC says:

Eskil: I just built Qt5 from Git. All my apps have ugly text rendering. How do I enable native rendering? I’m using QWidgets, not QML.

Eskil Abrahamsen Blomfeldt says:

RealNC: QWidgets use QPainter to draw text, which will in turn use the system back-end, so there must be some other reason why the text rendering appears ugly to you.

mcv says:

Is it possible to use distance field rendering on QWidgets?

I need subpixel positioning of the text for smooth animations. I’m trying to find something in Qt docs and experimenting with different flags on QPainter and QFont, but the effect is that fonts are always aligned to pixels.

Eskil Abrahamsen Blomfeldt says:

mcv: No, it’s not possible to use distance field-rendering with QWidgets. You can however get the unhinted outlines directly from the font using QPainterPath::addText(), which gives you the opportunity to render the glyphs yourself however you please.

Another option is to use QFont::HintingPreference. Set this to PreferNoHinting or PreferVerticalHinting in order to turn off the horizontal pixel alignment. On Windows, this is only supported if you configure Qt with the -directwrite option, and it will limit the target platforms of the application to more recent Windows versions. See the QFont documentation for more details on this.

mcv says:

Hah! Although setting PreferNoHinting or PreferVerticalHinting didn’t seem to give me any results on GNU/Linux, I tried the QPainterPath, and it works perfectly. Thanks!

Commenting closed.