Ok, here's a new patch, rebased against master and without an automatic
 switch to the autohinter if the font driver can't darken stems. As
Behdad insinuated, it might be better to avoid surprises.

Toolkits would have to implement the switch themselves if they so
desire or enable it only after querying the autohinter or font driver.
And/or only enable it when LIGHT hinting.

The primary purpose and effect of this API now would be to enable
better thread-safety by making at least one commonly used setting face-
local.

Ok, next stop: improve the look of autohinter stem darkening.
From 051ceb284b8cbd48a2798f79105e8f44d6445a6d Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Mon, 26 Dec 2016 21:09:56 +0100
Subject: [PATCH 2/2] Implement FT_Face_Option() [2/2]

Next up is the stem darkening option.

* Honor the face-level option in the CFF driver and autohinter. Stem
  darkening in the autohinter only works well in LIGHT hinting mode, so
  only trigger it there.
---
 src/autofit/afloader.c | 7 ++++++-
 src/autofit/afmodule.c | 8 ++++----
 src/cff/cf2ft.c        | 7 ++++++-
 src/cff/cffdrivr.c     | 8 ++++----
 4 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c
index 7f75fc3..4717cc0 100644
--- a/src/autofit/afloader.c
+++ b/src/autofit/afloader.c
@@ -334,8 +334,13 @@
      *  `standard_{vertical,horizontal}_width' change.
      *
      *  Ignore errors and carry on without emboldening.
+     *
+     *  Oh, and stem darkening only works well in LIGHT mode.
      */
-    if ( !module->no_stem_darkening )
+    if ( scaler.render_mode == FT_RENDER_MODE_LIGHT &&
+         ( !face->internal->no_stem_darkening ||
+           ( face->internal->no_stem_darkening < 0 &&
+             !module->no_stem_darkening ) ) )
       af_loader_embolden_glyph_in_slot( loader, face, style_metrics );
 
     loader->transformed = internal->glyph_transformed;
diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c
index f325ebf..719f44b 100644
--- a/src/autofit/afmodule.c
+++ b/src/autofit/afmodule.c
@@ -291,10 +291,10 @@
         long         nsd = ft_strtol( s, NULL, 10 );
 
 
-        if ( nsd == 0 )
-          module->no_stem_darkening = 0;
-        else if ( nsd == 1 )
-          module->no_stem_darkening = 1;
+        if ( !nsd )
+          module->no_stem_darkening = FALSE;
+        else if ( nsd )
+          module->no_stem_darkening = TRUE;
         else
           return FT_THROW( Invalid_Argument );
       }
diff --git a/src/cff/cf2ft.c b/src/cff/cf2ft.c
index c0d067e..b203650 100644
--- a/src/cff/cf2ft.c
+++ b/src/cff/cf2ft.c
@@ -340,6 +340,9 @@
       CFF_Builder*  builder = &decoder->builder;
       CFF_Driver    driver  = (CFF_Driver)FT_FACE_DRIVER( builder->face );
 
+      FT_Bool no_stem_darkening_driver = driver->no_stem_darkening;
+      FT_Char no_stem_darkening_font   = builder->face->root.internal->no_stem_darkening;
+
       /* local error */
       FT_Error       error2 = FT_Err_Ok;
       CF2_BufferRec  buf;
@@ -373,7 +376,9 @@
       font->renderingFlags = 0;
       if ( hinted )
         font->renderingFlags |= CF2_FlagsHinted;
-      if ( scaled && !driver->no_stem_darkening )
+      if ( scaled && ( !no_stem_darkening_font ||
+                       ( no_stem_darkening_font < 0 &&
+                         !no_stem_darkening_driver ) ) )
         font->renderingFlags |= CF2_FlagsDarkened;
 
       font->darkenParams[0] = driver->darken_params[0];
diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c
index 3182528..06094f0 100644
--- a/src/cff/cffdrivr.c
+++ b/src/cff/cffdrivr.c
@@ -853,10 +853,10 @@
         long         nsd = ft_strtol( s, NULL, 10 );
 
 
-        if ( nsd == 0 )
-          driver->no_stem_darkening = 0;
-        else if ( nsd == 1 )
-          driver->no_stem_darkening = 1;
+        if ( !nsd )
+          driver->no_stem_darkening = FALSE;
+        else if ( nsd )
+          driver->no_stem_darkening = TRUE;
         else
           return FT_THROW( Invalid_Argument );
       }
-- 
2.9.3

From 06500fa9fa8655ef8546731cde6a5ff2e60e567f Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Mon, 26 Dec 2016 21:09:28 +0100
Subject: [PATCH 1/2] Implement FT_Face_Option() [1/2]

FT_Face_Option() can override certain library or module-wide options on
a face-by-face basis. These options are stored in the 'internal'
structure of a FT_Face. They are "unset" by default.

First up are custom LCD filter weights.

* Implement FT_Face_Option() and declare the option tags.

* Implement the code to honors the face-level LCD filter option.

* Make the default and light filter weights publicly accessible as
  FT_LCD_FILTER_DEFAULT_WEIGHTS and FT_LCD_FILTER_LIGHT_WEIGHTS.

* Modify the signature of FT_Bitmap_LcdFilterFunc to take FT_Byte*
  instead of FT_Library as the last argument, change _ft_lcd_filter_fir
  and _ft_lcd_filter_legacy accordingly.

* Rename _ft_lcd_filter_fir to ft_lcd_filter_fir and make it accessible
  from within the FreeType source.
---
 include/freetype/freetype.h        | 80 ++++++++++++++++++++++++++++++++++++++
 include/freetype/ftlcdfil.h        | 10 +++++
 include/freetype/internal/ftobjs.h | 25 +++++++++++-
 src/base/ftlcdfil.c                | 41 ++++++++++---------
 src/base/ftobjs.c                  | 65 +++++++++++++++++++++++++++++++
 src/smooth/ftsmooth.c              | 62 +++++++++++++++++++++++------
 6 files changed, 250 insertions(+), 33 deletions(-)

diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h
index d979605..998c682 100644
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -3583,6 +3583,86 @@ FT_BEGIN_HEADER
 
 
   /*************************************************************************/
+  /*
+   * <Function>
+   *    FT_Face_Option
+   *
+   * <Description>
+   *    This function can be used to set or override certain (library or
+   *    module-wide) options on a face-by-face basis. Useful for finer grained
+   *    control and avoiding locks on shared structures (threads can modify
+   *    their own faces as they see fit).
+   *
+   * <Input>
+   *    face       :: A handle to the source face object.
+   *    num_params :: The number of parameters that follow.
+   *    parameters :: A handle to the num_params FT_Parameters.
+   *
+   * <Return>
+   *    FreeType error code.  0~means success.
+   *
+   * <Note>
+   *    The accepted parameters are:
+   *
+   *    1) FT_FACE_OPTION_ENABLE_STEM_DARKENING:
+   *       Toggles stem darkening. Overrides the module setting.
+   *
+   *       This is a passive setting that only takes effect when the font
+   *       driver or autohinter honors it, which e.g. the CFF driver always
+   *       does, the autohinter only in LIGHT hinting mode (as of version
+   *       2.7.0).
+   *
+   *    2) FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS:
+   *       Sets the weights for the 5-tap in-place FIR filter used by default
+   *       (the legacy intra-pixel filter is not supported). Overrides the
+   *       library setting. See @lcd_filtering.
+   *
+   *    Pass NULL as data for a tag to reset the option and use the library or
+   *    module default again.
+   *
+   *    Example:
+   *    {
+   *        // Define FT_CONFIG_OPTION_SUBPIXEL_RENDERING for the LCD filter
+   *        // examples to work.
+   *
+   *        FT_Parameter parameter1;
+   *        FT_Bool darken_stems = 1;
+   *        parameter1.tag  = FT_FACE_OPTION_ENABLE_STEM_DARKENING;
+   *        parameter1.data = &darken_stems;
+   *
+   *        FT_Parameter parameter2;
+   *        FT_LcdFiveTapFilter custom_weight = { 0x10, 0x40, 0x70, 0x40, 0x10 };
+   *        parameter2.tag  = FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS;
+   *        parameter2.data = custom_weight;
+   *
+   *        FT_Parameter parameters[2] = { parameter1, parameter2 };
+   *
+   *        FT_Face_Option( face, 2, parameters );
+   *
+   *        // Switch options around.
+   *        darken_stems = 0;
+   *        ft_memcpy(parameters[1].data, FT_LCD_FILTER_LIGHT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS);
+   *
+   *        FT_Face_Option( face, 2, parameters );
+   *
+   *        // Example for a single option.
+   *        FT_Parameter parameter3;
+   *        parameter3.tag  = FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS;
+   *        parameter3.data = NULL; // Reset option to driver/module default
+   *
+   *        FT_Face_Option( face, 1, &parameter3 );
+   *    }
+   */
+  FT_EXPORT( FT_Error )
+  FT_Face_Option( FT_Face       face,
+                  FT_UInt       num_params,
+                  FT_Parameter* parameters);
+
+#define FT_FACE_OPTION_ENABLE_STEM_DARKENING  FT_MAKE_TAG( 'd', 'a', 'r', 'k' )
+#define FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS FT_MAKE_TAG( 'l', 'c', 'd', 'f' )
+
+
+  /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
   /*    FT_Get_Name_Index                                                  */
diff --git a/include/freetype/ftlcdfil.h b/include/freetype/ftlcdfil.h
index e06a895..cc3338f 100644
--- a/include/freetype/ftlcdfil.h
+++ b/include/freetype/ftlcdfil.h
@@ -197,6 +197,16 @@ FT_BEGIN_HEADER
 
   } FT_LcdFilter;
 
+  #define FT_LCD_FILTER_FIVE_TAPS 5
+
+  typedef FT_Byte FT_LcdFiveTapFilter[FT_LCD_FILTER_FIVE_TAPS];
+
+  static const FT_LcdFiveTapFilter FT_LCD_FILTER_DEFAULT_WEIGHTS =
+                            { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
+  static const FT_LcdFiveTapFilter FT_LCD_FILTER_LIGHT_WEIGHTS =
+                            { 0x00, 0x55, 0x56, 0x55, 0x00 };
+
+
 
   /**************************************************************************
    *
diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h
index 25b18a5..88ef2d3 100644
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -347,6 +347,16 @@ FT_BEGIN_HEADER
   /*      created.  @FT_Reference_Face increments this counter, and        */
   /*      @FT_Done_Face only destroys a face if the counter is~1,          */
   /*      otherwise it simply decrements it.                               */
+  /*
+   *    no_stem_darkening ::
+   *      Overrides the module-level default, see e.g. @stem-darkening[cff].
+   *      FALSE and TRUE toggle stem darkening on and off, respectively, -1
+   *      means to use the module/driver default.
+   *
+   *    lcd_weights ::
+   *      Overrides the library default with custom weights for the 5-tap FIR
+   *      filter. {0, 0, 0, 0, 0} means to use the library default.
+   */
   /*                                                                       */
   typedef struct  FT_Face_InternalRec_
   {
@@ -362,6 +372,11 @@ FT_BEGIN_HEADER
 
     FT_Int              refcount;
 
+    FT_Char             no_stem_darkening;
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+    FT_LcdFiveTapFilter lcd_weights;   /* Preset or custom filter weights. */
+#endif
   } FT_Face_InternalRec;
 
 
@@ -780,7 +795,13 @@ FT_BEGIN_HEADER
 
   typedef void  (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap*      bitmap,
                                             FT_Render_Mode  render_mode,
-                                            FT_Library      library );
+                                            FT_Byte*        weights );
+
+  /* This is the default LCD filter, an in-place, 5-tap FIR filter. */
+  FT_BASE( void )
+  ft_lcd_filter_fir( FT_Bitmap*          bitmap,
+                     FT_Render_Mode      mode,
+                     FT_LcdFiveTapFilter weights );
 
 
   /*************************************************************************/
@@ -876,7 +897,7 @@ FT_BEGIN_HEADER
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
     FT_LcdFilter             lcd_filter;
     FT_Int                   lcd_extra;        /* number of extra pixels */
-    FT_Byte                  lcd_weights[5];   /* filter weights, if any */
+    FT_LcdFiveTapFilter      lcd_weights;      /* filter weights, if any */
     FT_Bitmap_LcdFilterFunc  lcd_filter_func;  /* filtering callback     */
 #endif
 
diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c
index 8bcbed7..2c1a72e 100644
--- a/src/base/ftlcdfil.c
+++ b/src/base/ftlcdfil.c
@@ -30,12 +30,11 @@
 #define  USE_LEGACY
 
   /* FIR filter used by the default and light filters */
-  static void
-  _ft_lcd_filter_fir( FT_Bitmap*      bitmap,
-                      FT_Render_Mode  mode,
-                      FT_Library      library )
+  FT_BASE( void )
+  ft_lcd_filter_fir( FT_Bitmap*          bitmap,
+                     FT_Render_Mode      mode,
+                     FT_LcdFiveTapFilter weights )
   {
-    FT_Byte*  weights = library->lcd_weights;
     FT_UInt   width   = (FT_UInt)bitmap->width;
     FT_UInt   height  = (FT_UInt)bitmap->rows;
 
@@ -176,7 +175,7 @@
   static void
   _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
                          FT_Render_Mode  mode,
-                         FT_Library      library )
+                         FT_Byte*        weights )
   {
     FT_UInt  width  = (FT_UInt)bitmap->width;
     FT_UInt  height = (FT_UInt)bitmap->rows;
@@ -189,7 +188,7 @@
       { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
     };
 
-    FT_UNUSED( library );
+    FT_UNUSED( weights );
 
 
     /* horizontal in-place intra-pixel filter */
@@ -295,8 +294,8 @@
     if ( !weights )
       return FT_THROW( Invalid_Argument );
 
-    ft_memcpy( library->lcd_weights, weights, 5 );
-    library->lcd_filter_func = _ft_lcd_filter_fir;
+    ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
+    library->lcd_filter_func = ft_lcd_filter_fir;
     library->lcd_extra       = 2;
 
     return FT_Err_Ok;
@@ -307,12 +306,6 @@
   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 };
-
-
     if ( !library )
       return FT_THROW( Invalid_Library_Handle );
 
@@ -324,14 +317,14 @@
       break;
 
     case FT_LCD_FILTER_DEFAULT:
-      ft_memcpy( library->lcd_weights, default_filter, 5 );
-      library->lcd_filter_func = _ft_lcd_filter_fir;
+      ft_memcpy( library->lcd_weights, FT_LCD_FILTER_DEFAULT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS );
+      library->lcd_filter_func = ft_lcd_filter_fir;
       library->lcd_extra       = 2;
       break;
 
     case FT_LCD_FILTER_LIGHT:
-      ft_memcpy( library->lcd_weights, light_filter, 5 );
-      library->lcd_filter_func = _ft_lcd_filter_fir;
+      ft_memcpy( library->lcd_weights, FT_LCD_FILTER_LIGHT_WEIGHTS, FT_LCD_FILTER_FIVE_TAPS );
+      library->lcd_filter_func = ft_lcd_filter_fir;
       library->lcd_extra       = 2;
       break;
 
@@ -356,6 +349,16 @@
 
 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
+  FT_BASE( void )
+  ft_lcd_filter_fir( FT_Bitmap*          bitmap,
+                     FT_Render_Mode      mode,
+                     FT_LcdFiveTapFilter weights )
+  {
+    FT_UNUSED( bitmap );
+    FT_UNUSED( mode );
+    FT_UNUSED( weights );
+  }
+
   FT_EXPORT_DEF( FT_Error )
   FT_Library_SetLcdFilterWeights( FT_Library      library,
                                   unsigned char  *weights )
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index 64791e6..5f7f767 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -2421,6 +2421,11 @@
       internal->transform_delta.y = 0;
 
       internal->refcount = 1;
+
+      internal->no_stem_darkening = -1;
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+      ft_memset( internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS );
+#endif
     }
 
     if ( aface )
@@ -3586,6 +3591,66 @@
 
   /* documentation is in freetype.h */
 
+  FT_EXPORT_DEF( FT_Error )
+  FT_Face_Option( FT_Face       face,
+                  FT_UInt       num_params,
+                  FT_Parameter* parameters)
+  {
+    FT_Error   error = FT_Err_Ok;
+
+
+    if ( num_params > 0 && !parameters )
+    {
+      error = FT_THROW( Invalid_Argument );
+      goto Exit;
+    }
+
+    for ( ; num_params > 0; num_params-- )
+    {
+      if ( parameters->tag == FT_FACE_OPTION_ENABLE_STEM_DARKENING )
+      {
+        if ( parameters->data )
+        {
+          if ( *((FT_Bool*)parameters->data) == TRUE )
+            face->internal->no_stem_darkening = FALSE;
+          else
+            face->internal->no_stem_darkening = TRUE;
+        }
+        else /* Use module default. */
+          face->internal->no_stem_darkening = -1;
+      }
+      else if ( parameters->tag == FT_FACE_OPTION_SET_LCD_FILTER_WEIGHTS )
+      {
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+        if ( parameters->data )
+          ft_memcpy( face->internal->lcd_weights, parameters->data, FT_LCD_FILTER_FIVE_TAPS );
+        else /* NULL == no custom weights, use library default. Signaled by
+                filling the weight field with zeros. */
+          ft_memset( face->internal->lcd_weights, 0, FT_LCD_FILTER_FIVE_TAPS );
+#else
+        error = FT_THROW( Unimplemented_Feature );
+        goto Exit;
+#endif
+      }
+      else
+      {
+        error = FT_THROW( Invalid_Argument );
+        goto Exit;
+      }
+
+      if ( error )
+        break;
+
+      parameters++;
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in freetype.h */
+
   FT_EXPORT_DEF( FT_UInt )
   FT_Face_GetCharVariantIndex( FT_Face   face,
                                FT_ULong  charcode,
diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c
index 5f6aa60..c5f28c6 100644
--- a/src/smooth/ftsmooth.c
+++ b/src/smooth/ftsmooth.c
@@ -122,6 +122,47 @@
     FT_Bool  have_outline_shifted = FALSE;
     FT_Bool  have_buffer          = FALSE;
 
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+    FT_Int                   lcd_extra = 0;
+    FT_LcdFiveTapFilter      lcd_weights = { 0 };
+    FT_Bool                  have_custom_weight = FALSE;
+    FT_Bitmap_LcdFilterFunc  lcd_filter_func = NULL;
+
+    if ( slot->face )
+    {
+      FT_Char weight_i = 0;
+
+      for ( ; weight_i < FT_LCD_FILTER_FIVE_TAPS; weight_i++ )
+        if ( slot->face->internal->lcd_weights[weight_i] != 0 )
+        {
+          have_custom_weight = TRUE;
+          break;
+        }
+    }
+
+    /* The LCD filter can be set library-wide and per-face. Face overrides
+     * library. If the face filter weights are all-zero (the default), it means
+     * that the library default should be used. */
+    if ( have_custom_weight )
+    {
+      /* A per-font filter is set. It will always use the default 5-tap
+       * in-place FIR filter that needs 2 extra pixels. */
+      lcd_filter_func = ft_lcd_filter_fir;
+      lcd_extra = 2;
+      ft_memcpy( lcd_weights, slot->face->internal->lcd_weights, FT_LCD_FILTER_FIVE_TAPS );
+    }
+    else
+    {
+      /* The face's lcd_weights is {0, 0, 0, 0, 0}, use library default. If the
+       * library is set to use no LCD filtering (lcd_filter_func == NULL),
+       * lcd_filter_func here will also be set to NULL and the tests further
+       * below will pass over the filtering process. */
+      lcd_filter_func = slot->library->lcd_filter_func;
+      lcd_extra = slot->library->lcd_extra;
+      ft_memcpy( lcd_weights, slot->library->lcd_weights, FT_LCD_FILTER_FIVE_TAPS );
+    }
+#endif
+
 
     /* check glyph image format */
     if ( slot->format != render->glyph_format )
@@ -178,24 +219,21 @@
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
-    if ( slot->library->lcd_filter_func )
+    if ( lcd_filter_func )
     {
-      FT_Int  extra = slot->library->lcd_extra;
-
-
       if ( hmul )
       {
-        x_shift += 64 * ( extra >> 1 );
-        x_left  -= extra >> 1;
-        width   += 3 * extra;
+        x_shift += 64 * ( lcd_extra >> 1 );
+        x_left  -= lcd_extra >> 1;
+        width   += 3 * lcd_extra;
         pitch    = FT_PAD_CEIL( width, 4 );
       }
 
       if ( vmul )
       {
-        y_shift += 64 * ( extra >> 1 );
-        y_top   += extra >> 1;
-        height  += 3 * extra;
+        y_shift += 64 * ( lcd_extra >> 1 );
+        y_top   += lcd_extra >> 1;
+        height  += 3 * lcd_extra;
       }
     }
 
@@ -299,8 +337,8 @@
     if ( error )
       goto Exit;
 
-    if ( slot->library->lcd_filter_func )
-      slot->library->lcd_filter_func( bitmap, mode, slot->library );
+    if ( lcd_filter_func )
+      lcd_filter_func( bitmap, mode, lcd_weights );
 
 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
-- 
2.9.3

_______________________________________________
Freetype-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to