Here's my promised documentation including the filter change. Please review.
>From b1d0881d06bda44f090053dc90cd2a6a75271799 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <madig...@gmail.com> Date: Fri, 27 Nov 2015 19:26:00 +0100 Subject: [PATCH] Change default LCD filter to normalized, color-balanced; Documentation
--- include/freetype/freetype.h | 80 ++++++++++++++++++++++++++++++++-- include/freetype/ftautoh.h | 41 +++++++++--------- include/freetype/ftlcdfil.h | 102 +++++++++++++++++++++++++------------------- src/base/ftlcdfil.c | 6 +-- 4 files changed, 155 insertions(+), 74 deletions(-) diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index c239f1b..382c715 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -2828,9 +2828,13 @@ FT_BEGIN_HEADER * @FT_LOAD_TARGET_MONO instead. * * FT_LOAD_TARGET_LIGHT :: - * A lighter hinting algorithm for non-monochrome modes. Many - * generated glyphs are more fuzzy but better resemble its original - * shape. A bit like rendering on Mac OS~X. + * A lighter hinting algorithm for gray-level modes. Many generated + * glyphs are fuzzier but better resemble their original shape. This is + * achieved by snapping glyphs to the pixel grid only vertically (Y-axis), + * as is done by Microsoft's ClearType and Adobe's proprietary font + * renderer. This preserves inter-glyph spacing in horizontal text. The + * snapping is done either by the native font driver if the driver itself + * and the font support it or by the autohinter. * * FT_LOAD_TARGET_MONO :: * Strong hinting algorithm that should only be used for monochrome @@ -2937,7 +2941,9 @@ FT_BEGIN_HEADER /* field in the @FT_GlyphSlotRec structure gives the format of the */ /* returned bitmap. */ /* */ - /* All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity. */ + /* All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity as */ + /* in coverage. Use linear alpha blending and gamma correction to */ + /* correctly render non-monochrome glyph bitmaps onto a surface, see @FT_Render_Glyph.*/ /* */ /* <Values> */ /* FT_RENDER_MODE_NORMAL :: */ @@ -3006,6 +3012,72 @@ FT_BEGIN_HEADER /* Convert a given glyph image to a bitmap. It does so by inspecting */ /* the glyph image format, finding the relevant renderer, and */ /* invoking it. */ + /* + * When FreeType outputs a bitmap of a glyph, it really outputs an alpha + * coverage map. If a pixel is completely covered by a filled-in outline, + * the bitmap will contain 0xFF at that pixel, meaning that 0xFF/0xFF + * fraction of that pixel is covered, meaning the pixel is 100% black (or + * 0% bright). If a pixel is only 50% covered (e.g. 0x80), the pixel is + * made 50% black (50% bright or a middle shade of grey) and 0% covered + * means 0% black (100% bright or white). + * + * 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, 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. + * + * This is relevant because 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 a pixel + * value of about 186 results in 50% brightness -- 128 ends up too dark on + * both bright and dark backgrounds. The net result is that dark text + * looks burnt-out, pixely and blotchy on bright background, bright text + * too frail on dark backgrounds and colored text (e.g. red) on colored + * background (e.g. green) seems to have dark halos or "dirt" around it. + * The situation is especially ugly for diagonal stems like in 'w' where + * the quality of FreeType's anti-aliasing depends on the correct display + * of grays. 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 things in + * linear and non-linear space, just all the shades in-between aren't. + * + * The blending function for placing text over a background is + * + * { + * dst = alpha * src + (1 - alpha) * dst , + * } + * + * which is known as OVER. + + * To correctly composite an antialiased pixel of a glyph onto a surface, + * 1. Take the foreground and background colors (e.g., in sRGB space) and + * apply gamma to get them in a linear space. + * 2. Use OVER to blend the two linear colors using the glyph pixel as the + * alpha value (remember, the glyph bitmap is a coverage bitmap) + * 3. Apply inverse gamma to the blended pixel and write it back to the + * image. + * Internal testing at Adobe found that a target inverse gamma of 1.8 for + * step 3 gives good results across a wide range of displays with a sRGB + * gamma curve or a similar one. + * + * This process can cost performance. There is an approximation that does + * not need to know about the background color. See + * https://bel.fi/alankila/lcd/ and + * https://bel.fi/alankila/lcd/alpcor.html for details. + * + * *ATTENTION*: Linear blending is even more important when dealing with + * subpixel-rendered glyphs to prevent color-fringing! The + * subpixel-rendered glyph must first be filtered with a filter that gives + * equal weight to the three color primaries and does not exceed a sum of + * 0x100, see @lcd_filtering. Then the only difference to gray linear + * blending is that subpixel-rendered linear blending is done 3 times per + * pixel. + */ /* */ /* <InOut> */ /* slot :: A handle to the glyph slot containing the image to */ diff --git a/include/freetype/ftautoh.h b/include/freetype/ftautoh.h index ff72e5e..776fddb 100644 --- a/include/freetype/ftautoh.h +++ b/include/freetype/ftautoh.h @@ -445,33 +445,32 @@ FT_BEGIN_HEADER * no-stem-darkening[autofit] * * @description: - * *Experimental* *only* - * - * The main purpose of emboldening glyphs or `stem darkening' is to - * enhance readability at smaller sizes. The smaller the size, the more - * emboldening is applied to keep glyphs from `thinning out'. All - * glyphs that pass through the autohinter will be emboldened unless - * this property is set to TRUE. - * - * See the description of the CFF driver for algorithmic details. Total + * *Experimental* *only,* *requires* *linear* *alpha* *blending* *and* *gamma* *correction* + * + * Stem darkening emboldens glyphs at smaller sizes to make them more + * readable on common low-DPI screens when using linear alpha blending and + * gamma correction, see @FT_Render_Glyph. When not using linear alpha + * blending and gamma correction, glyphs will appear heavy and fuzzy! + * + * 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 `thin out'. 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 counteracts the + * `thinning out' of glyphs. Text remains readable at smaller sizes. All + * glyphs that pass through the autohinter will be emboldened unless this + * property is set to TRUE. + * + * See the description of the CFF driver for algorithmic details. Total * consistency with the CFF driver is currently not achieved because the * emboldening method differs and glyphs must be scaled down on the * Y-axis to keep outline points inside their precomputed blue zones. * The smaller the size (especially 9ppem and down), the higher the loss * of emboldening versus the CFF driver. * - * *ATTENTION*: This feature has been developed with linear alpha - * blending and gamma correction of glyphs in mind: A rendering library - * must apply linear alpha blending while compositing glyph bitmaps onto - * a surface and then apply gamma correction to the glyph pixels to get - * from linear space to display space (unless the display works in - * linear space). Internal testing at Adobe found that a gamma - * correction value of 1.8 gives good results across a wide range of - * displays with a sRGB gamma curve or a similar one. - * - * If this is not possible, it might be better to disable stem - * darkening. Currently, this can only be done globally. - * */ diff --git a/include/freetype/ftlcdfil.h b/include/freetype/ftlcdfil.h index 3a62528..e7f4206 100644 --- a/include/freetype/ftlcdfil.h +++ b/include/freetype/ftlcdfil.h @@ -41,56 +41,58 @@ FT_BEGIN_HEADER * LCD Filtering * * @abstract: - * Reduce color fringes of LCD-optimized bitmaps. + * Reduce color fringes of subpixel-rendered bitmaps. * * @description: - * The @FT_Library_SetLcdFilter API can be used to specify a low-pass - * filter, which is then applied to LCD-optimized bitmaps generated - * through @FT_Render_Glyph. This is useful to reduce color fringes - * that would occur with unfiltered rendering. + * Subpixel rendering exploits the color-striped structure of LCD pixels, + * increasing the available resolution in the direction of the stripe + * (usually horizontal RGB) times 3. Since these subpixels are color + * pixels, using them unfiltered will create severe color fringes. Use the + * @FT_Library_SetLcdFilter API to specify a low-pass filter, which is then + * applied to subpixel-rendered bitmaps generated through @FT_Render_Glyph. * * Note that no filter is active by default, and that this function is * *not* implemented in default builds of the library. You need to * #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING in your `ftoption.h' file * in order to activate it. * - * FreeType generates alpha coverage maps, which are linear by nature. - * For instance, the value 0x80 in bitmap representation means that - * (within numerical precision) 0x80/0xFF fraction of that pixel is - * covered by the glyph's outline. The blending function for placing - * text over a background is - * - * { - * dst = alpha * src + (1 - alpha) * dst , - * } - * - * which is known as OVER. However, when calculating the output of the - * OVER operator, the source colors should first be transformed to a - * linear color space, then alpha blended in that space, and transformed - * back to the output color space. - * - * When linear light blending is used, the default FIR5 filtering - * weights (as given by FT_LCD_FILTER_DEFAULT) are no longer optimal, as - * they have been designed for black on white rendering while lacking - * gamma correction. To preserve color neutrality, weights for a FIR5 - * filter should be chosen according to two free parameters `a' and `c', - * and the FIR weights should be - * - * { - * [a - c, a + c, 2 * a, a + c, a - c] . - * } - * - * This formula generates equal weights for all the color primaries - * across the filter kernel, which makes it colorless. One suggested - * set of weights is - * - * { - * [0x10, 0x50, 0x60, 0x50, 0x10] , - * } - * - * where `a' has value 0x30 and `c' value 0x20. The weights in filter - * may have a sum larger than 0x100, which increases coloration slightly - * but also improves contrast. + * A filter should have two properties: + * 1) It should be normalized, meaning the sum of the 5 components should + * be 0x100 or 256. It is possible to go above or under this target sum, + * however: going under it means tossing out contrast, going over it + * means invoking clamping and thereby non-linearities that increase + * contrast somewhat at the expense of greater distortion and + * color-fringing. Contrast is better enhanced through stem darkening. + * 2) It should be color-balanced, meaning a filter { a, b, c, b, a } where + * a + b = c. It distributes the computed coverage for one subpixel to + * all subpixels equally, sacrificing some won resolution but + * drastically reducing color-fringing. Positioning improvements remain! + * Note that color-fringing can only really be minimized when using a + * color-balanced filter and alpha-blending the glyph onto a surface in + * linear space, see @FT_Render_Glyph. + * Regarding the form, a filter can be a "boxy" filter or a "beveled" + * filter. Boxy filters are sharper but are less forgiving of non-ideal + * gamma curves of a screen (viewing angles!), beveled filters are fuzzier + * but more tolerant. + * + * Examples: + * - [0x10 0x40 0x70 0x40 0x10] is beveled and neither balanced nor normalized. + * - [0x1A 0x33 0x4D 0x33 0x1A] is beveled, balanced but not normalized. + * - [0x19 0x33 0x66 0x4c 0x19] is beveled, normalized but not balanced. + * - [0x00 0x4c 0x66 0x4c 0x00] is boxily beveled, normalized but not balanced. + * - [0x00 0x55 0x56 0x55 0x00] is boxy, normalized and almost balanced. + * - [0x08 0x4D 0x56 0x4D 0x08] is beveled, normalized and almost balanced. + * + * It is important to understand that linear alpha blending and gamma + * correction is critical for correctly rendering glyphs onto surfaces + * without artifacts and even more critical when subpixel rendering is + * involved. + * + * Each of the 3 alpha values (subpixels) is independently used to blend + * one color channel. That is, red alpha blends the red channel of the text + * color with the red channel of the background pixel. The distribution of + * density values by the color-balanced filter assumes alpha blending will + * be done in linear space, only then will the color artifacts cancel out. */ @@ -111,10 +113,20 @@ FT_BEGIN_HEADER * The default filter reduces color fringes considerably, at the cost * of a slight blurriness in the output. * + * It is a beveled, normalized and color-balanced five-tap filter that is + * more forgiving to screens with non-ideal gamma curves and viewing + * angles. Note that while color-fringing is reduced, it can only be + * minimized by using linear alpha blending and gamma correction to + * render glyphs onto surfaces. + * * FT_LCD_FILTER_LIGHT :: - * The light filter is a variant that produces less blurriness at the - * cost of slightly more color fringes than the default one. It might - * be better, depending on taste, your monitor, or your personal vision. + * The light filter is a variant that is sharper at the cost of + * slightly more color fringes than the default one. + * + * It is a boxy, normalized and color-balanced three-tap filter that is + * less forgiving to screens with non-ideal gamma curves and viewing + * angles. This filter works best when the rendering system uses linear + * alpha blending and gamma correction to render glyphs onto surfaces. * * FT_LCD_FILTER_LEGACY :: * This filter corresponds to the original libXft color filter. It diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c index b50383c..5ee7e0a 100644 --- a/src/base/ftlcdfil.c +++ b/src/base/ftlcdfil.c @@ -305,12 +305,10 @@ FT_Library_SetLcdFilter( FT_Library library, FT_LcdFilter filter ) { + static const FT_Byte default_filter[5] = + { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; static const FT_Byte light_filter[5] = { 0x00, 0x55, 0x56, 0x55, 0x00 }; - /* the values here sum up to a value larger than 256, */ - /* providing a cheap gamma correction */ - static const FT_Byte default_filter[5] = - { 0x10, 0x40, 0x70, 0x40, 0x10 }; if ( !library ) -- 2.5.0
_______________________________________________ Freetype-devel mailing list Freetype-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/freetype-devel