shithub: freetype+ttf2subf

Download patch

ref: 8765c71b41625406fe87598645d842ca8451983c
parent: 49c77a87c37702cf3bbc0a2c1c10f2ff01b181f1
author: David Turner <[email protected]>
date: Fri Nov 10 11:49:42 EST 2006

* include/freetype/ftlcdfil.h, include/internal/ftobjs.h,
       src/base/ftlcdfilt.c, src/smooth/ftsmooth.c: API change for
       the LCD filter, the FT_LcdFilter value is a enum describing
       which filter to apply, new values FT_LCD_FILTER_LIGHT and
       FT_LCD_FILTER_LEGACY (the latter implements the LibXft original
       algorithm which produces incredible color fringes for everything
       except very-well hinted text)

       * src/autofit/aflatin.c: various tiny improvements that drastically
       improve the handling of serif fonts and of LCD/LCD_V hinting modes.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-11-10  David Turner  <[email protected]>
+
+       * include/freetype/ftlcdfil.h, include/internal/ftobjs.h,
+       src/base/ftlcdfilt.c, src/smooth/ftsmooth.c: API change for
+       the LCD filter, the FT_LcdFilter value is a enum describing
+       which filter to apply, new values FT_LCD_FILTER_LIGHT and
+       FT_LCD_FILTER_LEGACY (the latter implements the LibXft original
+       algorithm which produces incredible color fringes for everything
+       except very-well hinted text)
+
+       * src/autofit/aflatin.c: various tiny improvements that drastically
+       improve the handling of serif fonts and of LCD/LCD_V hinting modes.
+
 2006-11-09  David Turner  <[email protected]>
 
 	* src/pshinter/pshalgo.c (psh_glyph_compute_inflections): Fix
--- a/README
+++ b/README
@@ -51,7 +51,7 @@
 
 ----------------------------------------------------------------------
 
-Copyright 2001, 2002, 2003, 2004, 2006 by
+Copyright 2001-2006 by
 David Turner, Robert Wilhelm, and Werner Lemberg.
 
 This  file is  part of  the FreeType  project, and  may only  be used,
--- a/include/freetype/ftlcdfil.h
+++ b/include/freetype/ftlcdfil.h
@@ -27,7 +27,52 @@
 FT_BEGIN_HEADER
 
 
+ /****************************************************************************
+  *
+  * @func:
+  *   FT_LcdFilter
+  *
+  * @description:
+  *    a list of values used to identify various types of LCD filters
+  *
+  * @values:
+  *   FT_LCD_FILTER_NONE :: value 0 means do not perform filtering. when
+  *     used with subpixel rendering, this will result in sometimes severe
+  *     color fringes
+  *
+  *   FT_LCD_FILTER_DEFAULT ::
+  *      the default filter reduces color fringes considerably, at the cost of
+  *      a slight bluriness in the output
+  *
+  *   FT_LCD_FILTER_LIGHT ::
+  *      the light filter is a variant that produces less bluriness
+  *      at the cost of slightly more color fringes than the default one. It
+  *      might be better than the default one, depending on your monitor and
+  *      personal vision.
+  *
+  *   FT_LCD_FILTER_LEGACY ::
+  *      this filter corresponds to the original libXft color filter, this
+  *      provides high contrast output, but can exhibit really bad color fringes
+  *      if your glyphs are not extremely well hinted to the pixel grid. In
+  *      other words, it only works well when enabling the TrueType bytecode
+  *      interpreter *and* using high-quality hinted fonts. It will suck for
+  *      all other cases.
+  *
+  *      this filter is only provided for comparison purposes, and might be
+  *      disabled/unsupported in the future...
+  */
+  typedef enum
+  {
+    FT_LCD_FILTER_NONE    = 0,
+    FT_LCD_FILTER_DEFAULT = 1,
+    FT_LCD_FILTER_LIGHT   = 2,
+    FT_LCD_FILTER_LEGACY  = 16,
 
+    FT_LCD_FILTER_MAX   /* do not remove */
+
+  } FT_LcdFilter;
+
+
   /**************************************************************************
    *
    * @func:
@@ -85,34 +130,8 @@
    *
    */
   FT_EXPORT( FT_Error )
-  FT_Library_SetLcdFilter( FT_Library      library,
-                           const FT_Byte*  filter_weights );
-
-
-
-  /**************************************************************************
-   *
-   * @enum:
-   *   FT_LCD_FILTER_XXX
-   *
-   * @description:
-   *   A list of constants which correspond to useful lcd filter settings
-   *   for the @FT_Library_SetLcdFilter function.
-   *
-   * @values:
-   *   FT_LCD_FILTER_NONE ::
-   *     The value NULL is reserved to indicate that LCD color filtering
-   *     should be disabled.
-   *
-   *   FT_LCD_FILTER_DEFAULT ::
-   *     This value is reserved to indicate a default FIR filter that should
-   *     work well on most LCD screen.  It corresponds to the array 0x10,
-   *     0x40, 0x70, 0x40, 0x10.
-   *
-   */
-#define FT_LCD_FILTER_NONE     ( (const FT_Byte*)NULL )
- 
-#define FT_LCD_FILTER_DEFAULT  ( (const FT_Byte*)(void*)(ft_ptrdiff_t)1 )
+  FT_Library_SetLcdFilter( FT_Library    library,
+                           FT_LcdFilter  filter );
 
   /* */
 
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -29,6 +29,7 @@
 #include <ft2build.h>
 #include FT_RENDER_H
 #include FT_SIZES_H
+#include FT_LCD_FILTER_H
 #include FT_INTERNAL_MEMORY_H
 #include FT_INTERNAL_GLYPH_LOADER_H
 #include FT_INTERNAL_DRIVER_H
@@ -637,7 +638,7 @@
 
   typedef void  (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap*      bitmap,
                                             FT_Render_Mode  render_mode,
-                                            FT_Byte*        weights );
+                                            FT_Library      library );
 
 
   /*************************************************************************/
@@ -714,8 +715,10 @@
     FT_DebugHook_Func  debug_hooks[4];
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
-    FT_Byte                  lcd_filter_weights[5];
-    FT_Bitmap_LcdFilterFunc  lcd_filter;
+    FT_LcdFilter             lcd_filter;
+    FT_Int                   lcd_extra;        /* number of extra pixels */
+    FT_Byte                  lcd_weights[7];   /* filter weights, if any */
+    FT_Bitmap_LcdFilterFunc  lcd_filter_func;  /* filtering callback     */
 #endif
 
   } FT_LibraryRec;
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -965,7 +965,7 @@
      *  to avoid many problems with serif fonts.  We compute the
      *  corresponding threshold in font units.
      */
-    if ( dim == AF_DIMENSION_VERT )
+    if ( dim == AF_DIMENSION_HORZ )
         segment_length_threshold = FT_DivFix( 64, hints->y_scale );
     else
         segment_length_threshold = 0;
@@ -1536,6 +1536,7 @@
     else
     {
       /* strong hinting process: snap the stem width to integer pixels */
+      FT_Pos  org_dist = dist;
 
       dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
 
@@ -1571,7 +1572,27 @@
             dist = ( dist + 64 ) >> 1;
 
           else if ( dist < 128 )
+          {
+           /* ok, we're only going to round to an integer width if
+            * the corresponding distorsion is less than 1/4 pixel
+            * otherwise, this really screws everything, since the
+            * diagonals, which are not hinted, will appear a lot
+            * more bolder or thinner than the vertical stems
+            */
+            FT_Int  delta;
+
             dist = ( dist + 22 ) & ~63;
+            delta = dist - org_dist;
+            if ( delta < 0 )
+              delta = -delta;
+
+            if (delta >= 16)
+            {
+              dist = org_dist;
+              if ( dist < 48 )
+                dist = (dist + 64) >> 1;
+            }
+          }
           else
             /* round otherwise to prevent color fringes in LCD mode */
             dist = ( dist + 32 ) & ~63;
--- a/src/base/ftlcdfil.c
+++ b/src/base/ftlcdfil.c
@@ -24,14 +24,17 @@
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
-  /* The smooth rasterizer invokes this function. */
+#define  USE_LEGACY
+
+  /* FIR filter used by the default and light filters */
   static void
-  _ft_lcd_filter( FT_Bitmap*      bitmap,
-                  FT_Render_Mode  mode,
-                  FT_Byte*        weights )
+  _ft_lcd_filter_fir( FT_Bitmap*      bitmap,
+                      FT_Render_Mode  mode,
+                      FT_Library      library )
   {
-    FT_UInt  width  = (FT_UInt)bitmap->width;
-    FT_UInt  height = (FT_UInt)bitmap->rows;
+    FT_Byte* weights = library->lcd_weights;
+    FT_UInt  width   = (FT_UInt)bitmap->width;
+    FT_UInt  height  = (FT_UInt)bitmap->rows;
 
 
     /* horizontal in-place FIR filter */
@@ -155,28 +158,149 @@
   }
 
 
+#ifdef USE_LEGACY
+  /* FIR filter used by the default and light filters */
+  static void
+  _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
+                         FT_Render_Mode  mode,
+                         FT_Library      library )
+  {
+    FT_UInt  width   = (FT_UInt)bitmap->width;
+    FT_UInt  height  = (FT_UInt)bitmap->rows;
+    FT_Int   pitch   = bitmap->pitch;
+
+    static const int    filters[3][3] =
+    {
+      { 65538*9/13, 65538*1/6,  65538*1/13 },
+      { 65538*3/13, 65538*4/6,  65538*3/13 },
+      { 65538*1/13, 65538*1/6,  65538*9/13 }
+    };
+
+    FT_UNUSED(library);
+
+    /* horizontal in-place FIR filter */
+    if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
+    {
+      FT_Byte*  line = bitmap->buffer;
+
+
+      for ( ; height > 0; height--, line += pitch )
+      {
+        FT_UInt  xx;
+
+        for ( xx = 0; xx < width; xx += 3 )
+        {
+          FT_UInt  r = 0;
+          FT_UInt  g = 0;
+          FT_UInt  b = 0;
+          FT_UInt  p;
+
+          p  = line[xx];
+          r += filters[0][0]*p;
+          g += filters[0][1]*p;
+          b += filters[0][2]*p;
+
+          p = line[xx+1];
+          r += filters[1][0]*p;
+          g += filters[1][1]*p;
+          b += filters[1][2]*p;
+
+          p  = line[xx+2];
+          r += filters[2][0]*p;
+          g += filters[2][1]*p;
+          b += filters[2][2]*p;
+
+          line[xx]   = (FT_Byte)( r / 65536 );
+          line[xx+1] = (FT_Byte)( g / 65536 );
+          line[xx+2] = (FT_Byte)( b / 65536 );
+        }
+      }
+    }
+    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
+    {
+      FT_Byte*   column = bitmap->buffer;
+
+      for ( ; width > 0; width--, column++ )
+      {
+        FT_Byte*   col     = column;
+        FT_Byte*   col_end = col + height*pitch;
+
+        for ( ; col < col_end; col += 3*pitch )
+        {
+          FT_UInt  r = 0;
+          FT_UInt  g = 0;
+          FT_UInt  b = 0;
+          FT_UInt  p;
+
+          p  = col[0];
+          r += filters[0][0]*p;
+          g += filters[0][1]*p;
+          b += filters[0][2]*p;
+
+          p = col[pitch];
+          r += filters[1][0]*p;
+          g += filters[1][1]*p;
+          b += filters[1][2]*p;
+
+          p  = col[pitch*2];
+          r += filters[2][0]*p;
+          g += filters[2][1]*p;
+          b += filters[2][2]*p;
+
+          col[0]       = (FT_Byte)( r / 65536 );
+          col[pitch]   = (FT_Byte)( g / 65536 );
+          col[2*pitch] = (FT_Byte)( b / 65536 );
+        }
+      }
+    }
+  }
+#endif /* USE_LEGACY */
+
   FT_EXPORT( FT_Error )
-  FT_Library_SetLcdFilter( FT_Library      library,
-                           const FT_Byte*  filter_weights )
+  FT_Library_SetLcdFilter( FT_Library     library,
+                           FT_LcdFilter   filter )
   {
+    static const FT_Byte  light_filter[5]   = { 0, 85, 86, 85, 0 };
     static const FT_Byte  default_filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 };
 
-
     if ( library == NULL )
       return FT_Err_Invalid_Argument;
 
-    if ( filter_weights == FT_LCD_FILTER_NONE )
+    switch ( filter )
     {
-      library->lcd_filter = NULL;
-      return 0;
-    }
+        case FT_LCD_FILTER_NONE:
+            library->lcd_filter_func = NULL;
+            library->lcd_extra       = 0;
+            break;
 
-    if ( filter_weights == FT_LCD_FILTER_DEFAULT )
-      filter_weights = default_filter;
+        case FT_LCD_FILTER_DEFAULT:
+#if 0  /* DEBUGGING */
+            library->lcd_filter_func = _ft_lcd_filter_legacy;
+            library->lcd_extra       = 0;
+#else
+            memcpy( library->lcd_weights, default_filter, 5 );
+            library->lcd_filter_func = _ft_lcd_filter_fir;
+            library->lcd_extra       = 2;
+#endif
+            break;
 
-    memcpy( library->lcd_filter_weights, filter_weights, 5 );
-    library->lcd_filter = _ft_lcd_filter;
+        case FT_LCD_FILTER_LIGHT:
+            memcpy( library->lcd_weights, light_filter, 5 );
+            library->lcd_filter_func = _ft_lcd_filter_fir;
+            library->lcd_extra       = 2;
+            break;
 
+#ifdef USE_LEGACY
+        case FT_LCD_FILTER_LEGACY:
+            library->lcd_filter_func = _ft_lcd_filter_legacy;
+            library->lcd_extra       = 0;
+            break;
+#endif
+        default:
+            return FT_Err_Invalid_Argument;
+    }
+
+    library->lcd_filter = filter;
     return 0;
   }
 
@@ -183,11 +307,11 @@
 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
   FT_EXPORT( FT_Error )
-  FT_Library_SetLcdFilter( FT_Library      library,
-                           const FT_Byte*  filter_weights )
+  FT_Library_SetLcdFilter( FT_Library    library,
+                           FT_LcdFilter  filter )
   {
     FT_UNUSED( library );
-    FT_UNUSED( filter_weights );
+    FT_UNUSED( filter );
 
     return FT_Err_Unimplemented_Feature;
   }
--- a/src/smooth/ftsmooth.c
+++ b/src/smooth/ftsmooth.c
@@ -171,20 +171,22 @@
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
-    if ( slot->library->lcd_filter )
+    if ( slot->library->lcd_filter_func )
     {
+      FT_Int  extra = slot->library->lcd_extra;
+
       if ( hmul )
       {
-        x_shift -= 64;
-        width   += 6;
+        x_shift -= 64*(extra >> 1);
+        width   += 3*extra;
         pitch    = FT_PAD_CEIL( width, 4 );
-        x_left  -= 1;
+        x_left  -= (extra >> 1);
       }
       if ( vmul )
       {
-        y_shift -= 64;
-        height  += 6;
-        y_top   += 1;
+        y_shift -= 64*(extra >> 1);
+        height  += 3*extra;
+        y_top   += (extra >> 1);
       }
     }
 
@@ -213,20 +215,17 @@
 
     /* implode outline if needed */
     {
-      FT_Int      n;
+      FT_Vector*  points     = outline->points;
+      FT_Vector*  points_end = points + outline->n_points;
       FT_Vector*  vec;
 
 
       if ( hmul )
-        for ( vec = outline->points, n = 0;
-              n < outline->n_points;
-              n++, vec++ )
+        for ( vec = points; vec < points_end; vec++ )
           vec->x *= 3;
 
       if ( vmul )
-        for ( vec = outline->points, n = 0;
-              n < outline->n_points;
-              n++, vec++ )
+        for ( vec = points; vec < points_end; vec++ )
           vec->y *= 3;
     }
 
@@ -235,26 +234,22 @@
 
     /* deflate outline if needed */
     {
-      FT_Int      n;
+      FT_Vector*  points     = outline->points;
+      FT_Vector*  points_end = points + outline->n_points;
       FT_Vector*  vec;
 
 
       if ( hmul )
-        for ( vec = outline->points, n = 0;
-              n < outline->n_points;
-              n++, vec++ )
+        for ( vec = points; vec < points_end; vec++ )
           vec->x /= 3;
 
       if ( vmul )
-        for ( vec = outline->points, n = 0;
-              n < outline->n_points;
-              n++, vec++ )
+        for ( vec = points; vec < points_end; vec++ )
           vec->y /= 3;
     }
 
-    if ( slot->library->lcd_filter )
-      slot->library->lcd_filter( bitmap, mode,
-                                 slot->library->lcd_filter_weights );
+    if ( slot->library->lcd_filter_func )
+      slot->library->lcd_filter_func( bitmap, mode, slot->library );
 
 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
@@ -264,7 +259,7 @@
     /* expand it horizontally */
     if ( hmul )
     {
-      FT_Byte*  line = bitmap->buffer + ( height - height_org ) * pitch;
+      FT_Byte*  line = bitmap->buffer;
       FT_UInt   hh;