Chuniversiteit logomarkChuniversiteit.nl
Flat Earth

Rendering text on a pixel grid

Fonts in macOS look different from text that’s been rendered in Windows, mostly due to different philosophies about font rendering.

The character “R”, typeset in Adobe Garamond Pro
Due to an oversight, the illustrations in this article were not created using the R programming language. I would like to apologise for this missed opportunity.

r

arely does the topic of font rasterisation ever come up naturally in casual dinner table conversations, but fortunately this is my personal blog, so I can just write about it if I want to.

Characters in modern fonts are nearly always defined as a combination of various geometric shapes. This allows them to be scaled to all sizes without any loss in quality. Font rasterisation is the process that converts those shapes to raster representations that can be displayed on monitors, which work with pixel grids.

Rasterisation is one of those things that usually just works and doesn’t require a lot of thought, if any at all. Most of the time fonts simply look great, especially on Windows and macOS.

However, those with a keen eye for typography may notice some curious differences in the way fonts are rendered on the two platforms. Fonts in Windows tend to look a bit crispier than in macOS, which Windows users often describe as “blurry”. This is because Windows and macOS approach font rasterisation in slightly different ways.

These differences are especially noticeable when text is rendered at small sizes. In such situations a trade-off needs to be made between legibility and truthfulness to a font’s original design.

Tradeoffs

Link

Imagine that you want to render the letter “R” using the Adobe Garamond Pro font on a small grid of 10⨉12 pixels. In the figure below, the grey “R” shows what the rasterised version ideally looks like:

A 10⨉12 pixel grid overlaid on top of a high-resolution “R”

Unfortunately the shape of this character is too complex to draw properly, as we only have 10⨉12 pixels at our disposal. How should we approach this problem?

Let’s start with a very naive approach, that involves simply colouring every pixel that “touches” the ideal version of our Adobe Garamond Pro “R”:

The rendered result is too thick and looks like a bold version of the “R”

The resulting render is clearly recognisable as a capital R, so it’s certainly legible. However, it looks nothing like the original version: not only are the proportions all wrong, the character also takes up way more space than it should.

You might be tempted to only colour pixels if they overlap at least 50% with the ideal version of our “R”. Paradoxically, the resulting “R” is more truthful to the real “R” yet also less recognisable as the letter “R”, given that important parts of the character are missing:

The rendered result is too thin and barely resembles the letter “R”

A different threshold like, say 30%, might produce better-looking results for this particular font and character. But there’s no easy way to tell whether that threshold works as well for other fonts and characters.

Aliasing

Link

In the examples above, each pixel could only have two possible values: on or off. This is of course not how most modern displays work. In practice, monitors are capable of showing 255 shades of grey, ranging from completely white to completely black.

We can use this to our advantage by colouring each pixel such that the blackness level corresponds with the percentage that the pixel overlaps with the ideal version of the “R”:

A blurry-looking “R”

This gradual approach produces rasterisations that look much more truthful to the real “R”. It’s kind of blurry though and it might be mistaken for a “K” from a larger distance, but other than that it’s already reasonably legible.

Subpixel rendering

Link

One of the problems that we ran into in the previous example, is that pixels are atomic, i.e. they cannot be further subdivided. Note that the horizontal parts of the original “R” occupy about half a pixel. ?

It turns out there is an (albeit somewhat hacky) way to increase the horizontal resolution that’s based on the way we perceive LCD displays.

If you look at an older LCD monitor up close, you’ll notice that each pixel consists of a red, green, and blue subpixel. Their relative brightness can be independently adjusted to produce a total of 282^8 different colours. When black text is displayed on a white background, all three subpixels would normally have the same brightness, as that’s how you get the colours black, white, and everything in between.

The same aliasing trick that we used earlier also works on subpixels. Instead of varying the brightness of the pixel as a whole, we now vary the brightness of each subpixel individually. On displays with a traditional RGB layout, this effectively allows us to draw characters using only one third or two thirds of a pixel.

A pixel grid with visible red, green, and blue subpixels, that shows the character “R”

You’d probably expect that using different brightness values for red, green and blue would result in a something that resembles a colourful mosaic. This is indeed what happens when we look at things up close:

What the pixel grid looks like on screenshots. The left and right edges of the “R” looks orange and blue respectively.

The orange and blueish colours will be clearly visible on screenshots, especially when they are enlarged. However, from a normal reading distance the rendered text will look fairly normal:

An “R”, where mostly the top and bottom still look a bit blurry.

I would argue that this is a slight improvement over the previous greyscale attempt. , you might already have a hard time telling it apart from Adobe Garamond Pro’s “R”. We still haven’t fixed all of the blurriness though.

Hinting

Link

Let’s have another look at the illustration that I showed at the start of this article:

A 10⨉12 pixel grid overlaid on top of a high-resolution “R”

Not all parts of the “R” shape map cleanly onto pixels. Some parts only occupy half of a pixel – or less. Rendering the character would be much easier if its shape was more aligned to the pixel grid.

This just happens to be exactly what hinting is. Hinting is a process where shapes are subtly transformed such that their outlines are “snapped” to the pixel grid. While this changes the appearance of the character slightly, it also means that we don’t have to deal with half-filled pixels as often anymore.

I’ve made a lousy attempt to draw a hinted version of the “R” here:

It’s legible, but not really something to write home about. Fortunately, commercially available fonts do a much better job at figuring out how they should be rendered due to special hinting instructions that are added manually by the fonts’ designer or using automated conversion tools.

When combined with the aliasing technique that I discussed earlier, our “R” might end up looking like one of these two renders:

Adobe Garamond Pro “R”, rendered in macOS (l) and Windows (r).