Rasmus' blog
Graphics and game development.
Home All posts About Follow (@rasmusbarr) RSS
JANUARY 14, 2016

Subpixel accurate GPU rendering of text using a glyph atlas

This post introduces a simple way to achieve high-quality rendering of subpixel positioned glyphs without requiring a special pixel shader to perform fancy interpolation. Note that this is about subpixel positioning, not subpixel rendering (but the topics are somewhat related).

I'll start off with some background/motivation and end with a description of the technique.

Glyph atlas brief

Rendering glyphs from a glyph atlas is probably the most common way to render text in OpenGL and Direct3D. Individual glyphs are rasterized into a texture atlas, on-demand or in a preprocess, from a vector representation, such as a TrueType font. To render text on the screen, each glyph is drawn as a screen-aligned quad using the graphics processor.

To achieve high-quality text rendering, the texels in the glyph atlas and pixels on the screen should align perfectly. Misaligned glyphs will make text blurry, if filtering is active, or have an "uneven" appearance. Glyphs are therefore typically rendered at whole pixels. A result of rendering the string "aaaa" with this method is shown in Figure 1.

Figure 1: Rendering "aaaa" using pixel-aligned glyphs from a glyph atlas. The grid indicates the pixels and the dot in the center of each pixel is the sample position that determines the pixel color.

Why subpixel positioning?

The distance between glyphs in a word is called glyph advances. Vector-based fonts have a resolution independent set of advances, meaning that a pixel-aligned position for each glyph can be a rather crude approximation of the intended layout, depending on how they are calculated. One possibility, that would preserve the intended lengths of words, is to compute the location of each glyph in high precision and round to whole pixels. This will unfortunately cause the text to have a jittery appearance with varying distances between the same set of glyphs. The other option is to pixel align glyph advances, which results in good-looking and legible text. However, the length error along the line is magnified with each added glyph (see Figure 2).

Figure 2: Using pixel snapped advances (above in each box) or subpixel advances (below in each box) have great impact on the length of the text.

The issue also comes up when an application targets multiple resolutions, zoom levels, and display densities, while wanting to maintain the same layout for text at all resolutions.

Subsampling a prefiltered representation

Because lines of text flow in the horizontal direction (x), the error doesn't accumulate in the vertical direction (y). As a result, it is typically fine to round to pixels along y. (Languages where the converse is true can of course simply swap the handling of the two coordinates. The technique can also be extended to both directions at the cost of memory and rasterization effort.) Note that the rasterization phase is free to rasterize the glyph at any single subpixel y-location, such as aligned with the baseline of the font. With this in mind, a method to render glyphs at subpixel positions along x is presented below.

The idea is to create a glyph atlas with multiple versions of each glyph (Figure 3). Each version represents a different subpixel offset. We will stick to four subpixel versions here which will allow each glyph pixel to be packed into 32 bits (four 8-bit alpha texels). The texels are arranged in interleaved form (Figure 4).

Figure 3: An "a" rendered at 4 subpixel positions, 1/4 of a pixel apart.
Figure 4: The versions of the "a" from Figure 3 have been interleaved along the x-direction.

While it is intuitive to interleave four separately offset versions of a glyph, it is a relatively slow approach. A faster method is to render the glyph scaled 4x along x and then apply a box filter in that direction with a kernel width of a whole pixel (four texels).

The glyph in Figure 4 represents a prefiltered representation of the glyph that we can subsample to achieve subpixel positioning. We simply introduce four times the number of texels in each glyph quad and offset them by 3/8 pixel along x so the first texel is properly aligned with the sample center (see Figure 5).

Figure 5: Subsampling four times the pixels to achieve subpixel positioned glyphs.

As of now, this approach provides four subpixel locations for each glyph. The nice thing is that the approach works well with ordinary linear filtering (see Figure 6).

Figure 6: Same as Figure 5 but utilizing linear filtering to achieve interpolated subpixel locations.

That's it. You have already seen the final result in Figure 2. I have successfully used this technique to render high-quality text with exactly the same layout at multiple resolutions in my secret project. Make sure to keep a lookout for the next post if this first attempt of mine was interesting to you.

EDIT: It turns out that this technique have been used in stb for some time: oversample.