A second spring of text rasterization

Posted by Eskil Abrahamsen Blomfeldt on September 9, 2010 · 24 comments

Albert Camus said that “autumn is a second spring when every leaf is a flower”. Similarly, text rasterization on Mac OS X is a constant summer where every character is in permanent bloom.

The background for this blog post is a text rendering task which has been sitting in my Jira for a while, and which I recently got around to completing. The goal of the task was to make text rendering on Mac identical whether you used the native paint engine or the raster paint engine. One of the reasons why this is important to us, is that we’ve been considering making QRasterPaintEngine the default paint engine on Mac/Cocoa at some point. This is our reference paint engine, already the default on Windows, and it has proven to give better visual results than the native Mac OS X paint engine, as well as better performance in many key areas. Also, by unifying the default technologies across several different platforms, we reduce our own maintenance load and make it possible for developers to focus more on other types of improvements than fixing platform regressions (read: everybody wins.) Of course, before we can do this, we have to make sure that using the raster paint engine does not ruin Qt’s native look and feel on the platform. Qt Bug 5053 was one of the obstacles standing in the way.

In addition, the task is part of a larger, ongoing project to provide typographically correct text rendering in Qt wherever possible, which I’ll hopefully come back to in future blogs. Right now I’ll focus on the changes to the raster engine.

To illustrate the original problem with using the raster paint engine as a replacement for the current one, let’s look at some screenshots. The first image is from the TextEdit demo using the native paint engine to display text.

Text rendering with the native paint engine in Qt 4.7 on Mac OS X

Both Mac OS X and Windows use a form of antialiasing when rasterizing character shapes on screen which is called subpixel antialiasing. The trick behind this is to recognize that pixels on an LCD screen is divided into three separate components: Red, green and blue. These three colors are physically separate inside each of the pixels on screen. So where regular antialiasing would have you apply an opacity to the pixel you are drawing depending on how large part of the pixel the shape is actually covering, subpixel antialiasing will have you apply three separate opacities instead – one for the red component, one for the green component and one for the blue – depending on how large a part of each subcomponent of the pixel the shape covers.

The result is, as you can see from the zoomed area in the screenshot above, that the edges of the shapes are littered with pixels in all the colors of the rainbow, like someone sprayed your screen with tiny, tiny confetti.

One key difference, however, between text rendering on Mac and text rendering on Windows, is that Windows does horizontal hinting on each glyph before rasterizing it, while Mac does not. Hinting is the execution of small programs embedded in the font (or an automatic algorithm) to move the control points of the vector shapes around so that they are aligned to the current pixel grid and visually optimized to be displayed in a given size at a given resolution. This makes the text look clearer on a monitor, which typically has a very low resolution, but since the shapes defined by the font are altered, both in appearance and size, a layout applied to text rendered in the font will give you different results at different resolutions: Specifically, a layout defined for a document as it looks on screen, might not be correct for the same document when it’s rendered on a printer. The unhinted approach is what I refer to as typographically correct above, and even on platforms where you want the UI to be rendered with hinted glyphs, there are use cases where the ability to switch this off would be very convenient.

One effect of the font hinting is that each glyph on Windows has a single unique rasterization for a given font and point size. Regardless of where the glyph’s vector shape is placed on the screen, it will be altered to align with the pixel grid, thus it will cover each subpixel in the same degree, whether it’s horizontally positioned at, say, 0.0, 0.5 or 0.986574.

On Mac, however, the characters can be placed at subpixel positions, affecting the coverage when they are rasterized.

Let’s zoom in a little bit more on the previous example.

Look at e.g. the character i in this screenshot. In the word ipsum it’s rendered with red and blue. In the words in and elit, however, it’s different, rendered in black, light blue and beige. This is because of a difference in the subpixel positions of the glyphs, causing the subpixels they touch to be covered in different amounts.

Now let’s look at how the same text is rendered using the raster engine in Qt 4.7.

Compare this to the screenshot made with the native paint engine. The first thing you might notice is in the unzoomed area of the image, where the characters look like they are spaced unevenly (click on the image to make it full size. A good example is the spacing of the s and u in the word posuere) If you look closer at the zoomed area, you will see that the reason for this is that the rasterization of each glyph is the same for all subpixel positions.

When the raster paint engine was written, the UI text presentation needs on Mac were not considered, as the Mac already had a functioning native paint engine to handle this. Later, the paint engine was extended to use glyph caching: The first time a given character in a given font and size is rasterized, the image is saved in a glyph atlas and then reused whenever the same character is rendered again. This gives a performance improvement, as the actual rasterization of the glyph is very expensive. Since the needs of Mac were not considered in the design of this, only a single rasterization is cached per set of font, glyph and size. On Mac, there can be several such rasterizations. The number of different rasterizations supported is arbitrary, depending on the font and font size. (Currently, Qt 4.8 attempts to detect this number by some experimental reverse engineering, drawing the same glyph at twelve different subpixel locations and counting the number of unique rasterizations we get back. Higher granularity would most likely not be visible unless you zoom in, but please let me know if anyone has a better idea on how to determine the number of unique rasterizations used in the native text rendering.)

In conclusion, Qt 4.8 will see the end of this gap, and rendering the example text presented above with the raster engine will yield identical results to the native engine (in fact it’s so identical that I won’t even bother showing a screenshot.) To make it even better, other font engines will be able to benefit from this when they are written to support subpixel positioning. The future on all the modern desktop platforms holds great things, including typographically correct text that, when zoomed, will look like a beautiful, lush garden of flowers, rather than the drab and uniform plasticity of hinted glyphs.

QShare(this)

Possibly related posts:

  1. Insanity is shaping the same text again and expecting a different result
  2. Hint, hint, nudge, nudge, say no more!

24 comments

1 Tim September 9, 2010 at 6:09 pm
 

Excellent. Now all we need are high density displays. No-one seems to care about making them though.

I can’t find it now, but I remember reading a while ago about line-based hinting, where they do do hinting but keep the length of a line constant.

2 qt fun September 9, 2010 at 7:22 pm
 

But 4.8 is going to be so far away! :)

I spent part of the holiday weekend trying to make my Qt text look like text I created in Interface Builder (cocoa, xcode) and could not figure out what it was that looked different.. Now I know.

So this affects the 4.7 cocoa build for OS X as well?

Oh well, at least this important OS X issue is now being addressed. thank you Trolls.

3 scorp1us September 9, 2010 at 7:25 pm
 

I haven’t had to print anything for a while, but when I did with 4.4, it was horrible. Forget sub-pixel hinting, the kerning was inconsistent across the board and sometimes some non-bold ‘s’s were thicker than others. It was quite embarrassing. This was simply printing a QTextDocument.

Has that been fixed?

4 Dan Leinir Turthra Jensen September 9, 2010 at 7:50 pm
 

@Tim Oh yes, sure they do! What you forget is that Qt also runs on mobile devices. i have in my possession a device which runs at over 300 dpi (the N900), so… it certainly is used :)

5 Josh September 9, 2010 at 8:28 pm
 

Does this mean that Qt on Mac will also use Harfbuzz in 4.8? This is the cause of one of the major differences between text in Windows/Linux and Mac at the moment for my program. The Core Text engine doesn’t support as many of the advanced glyph placement features as Harfbuzz. Also, can you let us know how much of this will make it into 4.7.x or if we need to completely wait for 4.8 to see any of these changes? Thanks!

6 Chris September 9, 2010 at 8:39 pm
 

Just please don’t make text look like WPF. Most normal UIs use 8-10 point fonts, and font hinting is basically mandatory to get any kind of reasonable contrast at those sizes.

7 Josh September 9, 2010 at 8:49 pm
 

I’d also like to make the observation that currently, rendering text is MUCH slower on Mac than Linux/Windows. I believe this is because Qt/Mac is using Core Text. I compiled my program for both Qt/Mac and Qt/X11 (using Fink) on the same Mac and the Qt/X11 version seemed to render text roughly twice as fast (I didn’t benchmark, so take this with a grain of salt)…

8 NuShrike September 10, 2010 at 3:02 am
 

As long as an eye is paid to sub-pixel rendering as pointed out by AGG instead of the horrid Microsoft “ClearType” rendering, and an even closer eye is paid to memory footprint.

In my experience, a lot of these software optimizations, especially the current migration of the core rendering paths away from hardware APIs, has increased Qt’s footprint substantially. It’s now a lot of difficult stripping, and customized code to fit Qt in embedded, opengles deployments without ending up rewriting Qt’s renderers.

9 Eskil Abrahamsen Blomfeldt September 10, 2010 at 8:54 am
 

scorp1us: I’m not sure which bug you are referring to. Has it been reported?

Josh: Sorry, this will not make it into Qt 4.7.x. In that series, you will have to use the native paint engine to get subpixel positioning.

Chris: We have no plans to change the default rasterization on Windows. The idea is to have unhinted text as an option for applications which are more typographically inclined.

10 Mike McQuaid September 10, 2010 at 10:24 am
 

This would be great but the bigger font problem on OSX is the fact that all the Qt fonts default to the wrong sizes, meaning any application written in Qt immediately looks incredibly out of place on OSX. It would be good if the default font sizes for e.g. labels actually followed the OSX HCI guidelines here:
http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGText/XHIGText.html#//apple_ref/doc/uid/TP30000365-DontLinkElementID_1394

11 Eskil Abrahamsen Blomfeldt September 10, 2010 at 12:37 pm
 

If anyone has bugs to report, please refer to http://bugreports.qt.nokia.com

12 jlarcombe September 10, 2010 at 5:45 pm
 

This is very interesting. I would very much welcome consistency in text rendering between the two platforms. I’m curious though to know precisely what aspects if QRasterPaintEngine have been “proven to give better visual results than the native Mac OS X paint engine”, and in which areas you see better performance?

I’d also love to know which advanced glyph placement features Josh has found that work better in Harfbuzz than in Core Text – this may well affect our application too.

Thanks.

13 Josh September 10, 2010 at 7:19 pm
 

@Eskil Can you comment on whether this will mean Qt will use Harfbuzz in 4.8? Will this be an option to guarentee identical output across platforms? Thanks for all your work on this! Much appreciated.

14 Josh September 10, 2010 at 8:35 pm
 

@Eskil Sorry, just to clarify. I’m asking whether Qt will use Harfbuzz on Qt/Mac in 4.8…

15 jiang September 11, 2010 at 1:06 pm
 

@Josh: The shaping process in Qt/Cocoa will still going to use Core Text, Qt/Carbon will use ATSUI. Shaping with Harfbuzz on Mac is technically possible but we haven’t investigated it any further (and I’m not aware of any plans to do that). If you (or other people) observed performance issue in the Core Text based text layout/rendering, please file a bug report in http://bugreports.qt.nokia.com and preferably attach a test case to compare the performance in the same platform. (Comparing it with Qt/Linux will not be practical since a lot of factors change, but you are welcomed to hack on Qt to make the Harfbuzz code path work on Mac, so that you may be able to compare it directly with the Core Text code path.)

16 Eskil Abrahamsen Blomfeldt September 13, 2010 at 9:47 am
 

jlarcombe:
As can be seen from the bug reports in bugreports.qt.nokia.com, the native paint engine on Mac has a number of outstanding problems with different types of painting which do not exist/have already been fixed in the raster engine and which therefore could be fixed simply by running the raster engine instead. This is, of course, not an unfixable problem, and I didn’t mean to imply that there were any conceptual differences causing the gap, but raster is currently a more complete and better paint engine than the native implementation, so if as long as there are no downsides to using it as the default, it seems like a good idea.

As for the performance, I don’t have any benchmarks available right now, but the improvement should be noticeable just by running an application with “-graphicssystem raster”. We have done benchmarks in the past which favor the raster engine, however, and we’ll need to do more.

Josh: See Jiang’s response.

17 Nils September 13, 2010 at 10:39 am
 

Just tried to run Creator with -graphicssystem raster on Mac OS X (10.5). The font rendering does not just look a little bit worse than the native rendering, it looks totally broken. Is this what the changes will fix?

18 Eskil Abrahamsen Blomfeldt September 13, 2010 at 11:50 am
 

Nils: That depends on your threshold for calling something “totally broken” :-) This screenshot illustrates what has been fixed: http://labs.trolltech.com/blogs/wp-content/uploads/2010/09/raster_engine_4_7.png

If your results are more broken than that, then it’s a different bug.

19 Thomas Zander September 13, 2010 at 1:31 pm
 

@Nils; please write a report on http://bugreports.qt.nokia.com with a screenshot of what you see and a description of why its a problem. Thanks :)

20 Nils September 13, 2010 at 1:32 pm
 

This is a screenshot from Creator when started with -graphicssystem raster: http://picturepush.com/public/4177081.

21 Eskil Abrahamsen Blomfeldt September 13, 2010 at 2:30 pm
 

Nils: That looks very much like the problem which is the topic for this blog, yes. Try the master branch of Qt in a few days and it should hopefully be fixed.

22 Nils September 13, 2010 at 3:01 pm
 

@eskil: Thank you for fixing that. And yes, I would call it “totally broken” ;-)

23 RealNC September 13, 2010 at 9:55 pm
 

Subpixel positioning is useful on Linux too, not just OS X. And there we also have the advantage of partial hinting, a good middle-ground between Windows (full hints) vs OS X (no hints at all). All that’s missing for the “perfect font look” is subpixel positioning (I think Freetype is able to do that, but Qt won’t make use of it.)

24 Eskil Abrahamsen Blomfeldt September 14, 2010 at 9:43 am
 

RealNC: Take a look at the task http://bugreports.qt.nokia.com/browse/QTBUG-10615 that was mentioned in the blog. As I said, we are doing research to investigate the possibility of making this an optional feature for the use cases where it makes sense on Windows using DirectWrite and on Linux using Freetype.

Comments on this entry are closed.

Previous post:

Next post: