freetype-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ft-devel] Writing about the new stem darkening autohinter


From: Nikolaus Waxweiler
Subject: [ft-devel] Writing about the new stem darkening autohinter
Date: Fri, 6 Nov 2015 21:45:28 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0

Hey list,
I figured I should write about the new changes before they hit the unsuspecting world in a future release. Here's a first draft I just wrote. Please comment.

It is supposed to go on the main news page of freetype.org (Werner? :)). I'll also try to spread it to LWN.net, Phoronix and heise.de once it's in shape for publication.

Stem darkening for the autohinter
=================================

FreeType's master branch has merged a new feature that users, developers and packagers should know about. With commit b6fd5bc06c4434a832328c4682544c436484265e, stem darkening has been implemented in the autohinter.

[[http://postimg.org/image/id3qr94av/full/]]

Stem darkening emboldens glyphs at smaller sizes to make them more readable on common low-DPI screens. If this sounds familiar to you, that's because Adobe's CFF engine has been doing it since it's been contributed in 2013. You might have noticed that OpenType/CFF fonts (commonly suffixed .otf) like GNOME3's default UI font Cantarell appear bolder and fuzzier than other fonts. The autohinter will do the exact same thing now.

But why would you do this if small glyphs have been fairly readable already? It turns out that font rendering in the Linux ecosystem has been done wrong since scalable fonts were introduced to it.

When FreeType outputs a grayscale glyph image, it really outputs a coverage map. If a pixel is completely covered by a filled-in outline, the pixel is made 100% black. If a pixel is only 50% covered, the pixel is made 50% black (read: some middle shade of gray or 50% brightness). On high-DPI screens like on smartphones and tablets, the pixels are so small that their chance of being completely covered and therefore completely black are fairly good. On the low-DPI screens most of us are sadly stuck with, the situation is different. The pixels are too large for most of the details of a glyph and shades of gray are the norm rather than the exception. All our screens have a second problem: they are not linear. 1 + 1 is not 2. Twice the value does not result in twice the brightness. When a pixel is only 50% covered, the coverage map says 50% black, and this translates to a pixel value of 128 when you use 8 bits per channel (0-255). However, this does not translate to 50% brightness for that pixel on our sRGB and gamma 2.2 screens. Due to their non-linearity, they dwell longer in the darks and only pixel value of about 186 results in 50% brightness -- 128 ends up too dark! The net result is that the fonts on our desktops have been looking burnt-out, pixely and blotchy for 20 years or something. On high-DPI screens where smaller, fully black pixels reign supreme, this doesn't matter, but on our low-DPI screens with all the gray shades, it does. 0% and 100% brightness are the same thing in linear and non-linear space, just all the shades in-between aren't.

The correct way of rendering a glyph image on a surface is to alpha blend it onto the surface in linear space and then apply gamma correction to translate the linear coverage map to something that is correct for our screens. No toolkit in the Linux ecosystem does it by default, even though Qt5 and Skia can and will do it on other platforms. Windows and Mac OS X do it natively. This procedure is especially important if glyphs should be subpixel-rendered (think ClearType and Mac OS X) with as little color-fringing as possible.

[[http://postimg.org/image/3z1cvvxih/full/]]
We want to get to "Gamma 1.8, darkened". Note how it's the cleanest rendering of all.

Back to stem darkening. Assume we render fonts correctly. Gamma correction essentially lightens fonts since shades of grey are shifted to higher pixel values (= higher brightness) to match the original intention to the reality of our screens. The side-effect is that glyphs that were rendered incorrectly but fairly readable suddenly "thin out". Correctly rendered but hard-to-read text doesn't do anyone a favor. So Mac OS X and Adobe's proprietary font rendering library implement a counter-measure: stem darkening at smaller sizes where shades of gray dominate. By emboldening a glyph slightly in relation to its' pixel size, individual pixels get higher coverage of filled-in outlines and are therefore "blacker". This increases contrast and prevents "thinning out" of glyphs. Text remains readable at smaller sizes.

[[http://postimg.org/image/6vx7wxvhv/full/]]

And that is the story behind this feature.

Now, with the new autohinter, your fonts will actually render much "worse" than before. Just like OpenType/CFF fonts. I already mentioned the reason: No toolkit in the Linux ecosystem does linear alpha blending and gamma correction of text by default, but stem darkening absolutely *requires* it.

I see the "new" autohinter as a call to implement and/or enable linear alpha blending and gamma correction in all rendering libraries. Before, only .otf fonts were darkened but those aren't that common. So most people probably never saw incorrect stem darkening in action. With my recent push to establish the autohinter as the default in GNOME3 and in upstream fontconfig, more people will probably be exposed to it in the near future. I hope that maintainers of rendering libraries take note.

I plan on decoupling stem darkening from the autohinter as hinting and stem darkening should be orthogonal. The TrueType driver should also benefit from stem darkening. This future work, though.

I don't want this
---
Like with the CFF engine's stem darkening, there is no way to turn it off from fontconfig. Refer to http://freetype.org/freetype2/docs/reference/ft2-cff_driver.html#no-stem-darkening to turn it off at run-time. You can also patch FreeType as the maintainer on Debian did.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]