photo of Macallan

Rants & Ramblings

Anti-aliased fonts in wsdisplay

, , , , ,

After a little exchange on IRC about having more usable fonts for wsdisplay, anti-aliasing and using things like TrueType fonts in the console I went to work with freetype2, mostly because it's already part of NetBSD's build process.
As it turned out having freetype spit out 8bit alpha maps for individual glyphs is almost trivial, what's not so trivial is to find the right character cell size. The problem is that you give freetype a font height in pixels but this height does not include space for diacritics that may or may not appear above and below characters and all metrics are relative to the base line with no obvious indication where in the character cell to put it. My naive approach works like this - for a given character cell height request a type with 90% of that height, then measure the height of the capital W to find the base line within the requested height, leave the extra 10% for diacritics. The reason is this - for a console font we don't want too much empty space between lines and diacritics are rare so we accept the occasional cut off for the sake of something closer resembling a traditional terminal font. For every glyph truetype also gives us a distance to advance in X direction for drawing the next character, with a monospace font this should be the same for all glyphs which is exactly what we need for wsdisplay.
My ttf2wsfont utility currently takes a font, does the measurements above for a given cell height, then allocates memory and renders all ISO1 characters as alpha maps and spits the result out as a C header file which resembles the existing wsfont files except that font data are 8 bit instead of monochrome.
Now to the kernel part. First, since rendering alpha maps isn't really feasible with colour-indexed video modes we need to make sure there is always a fallback to a monochrome font and we need to make sure never to feed an alpha map font to a monochrome only rendering routine. For now I just keep alpha and mono fonts in separate lists and explicitly check the alpha list when we know we can handle them.
That leaves the actual rendering which is surprisingly trivial. For each pixel in the alpha map the result is simply alpha * foreground_colour + (1 - alpha) * background_colour. Or, since we're dealing with 8 bit values here, each component is (alpha * foregound_component + (255 - alpha) * background_component) >> 8. I only implemented this for rasops32, adding it to 15 and 16 is trivial.
The result looks pretty, even on a laptop where the VESA BIOS is too braindead to initialize a video mode that matches the panel's native resolution.
The next step is to implement the alpha blending in hardware with at least a few drivers - ffb and crmfb come to mind, mostly because it's ridiculously easy to do on the corresponding hardware. No need for messing with texture mapping, ffb has RAMs with built-in ALUs that support alpha blending so all we need to do is to set a few registers and then scribble the alpha map into the 8bit smart aperture as is. With crmfb it's just as easy - the drawing engine supports alpha for all operations, including bitblts, so we can keep the font in video memory and drawing a character is a few register writes with no actual data uploads.
Since I've been hacking on Gdium for the last couple weeks of course I had to add support there too, unfortunately the hardware's alpha blending support is useless here - all it can do is to combine two images with a constant alpha value, it has no concept of per pixel alpha. The only thing we can do here is to use host blits to draw characters instead of scribbling into video memory, that way at least all operations go through the pipeline and we don't have to sync the drawing engine every time we need to draw something.

Gdium surgeryAlpha-blending vs. FFB

Write a comment

New comments have been disabled for this post.