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

Reply via email to