Done!

https://github.com/madig/freetype2/commits/stem-darkening-autohinter-clean

One issue I came across is that I want to reuse the property names but docmaker.py assigns them to what the CFF driver header defines. Hm.
>From 2ec9b1884ce55c4fc44e4c0cb8c5ec80948fb80f Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Sat, 24 Oct 2015 16:04:24 +0200
Subject: [PATCH 5/5] Implement the actual emboldening in af_loader_load_g().

---
 src/autofit/afloader.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 99 insertions(+), 4 deletions(-)

diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c
index 99523f4..6e9254a 100644
--- a/src/autofit/afloader.c
+++ b/src/autofit/afloader.c
@@ -84,6 +84,7 @@
 
   static FT_Error
   af_loader_load_g( AF_Loader  loader,
+                    AF_Module  module,
                     AF_Scaler  scaler,
                     FT_UInt    glyph_index,
                     FT_Int32   load_flags )
@@ -103,6 +104,103 @@
     if ( error )
       goto Exit;
 
+    /* Apply stem darkening (emboldening) here before hints are applied to the
+     * outline. Glyphs are scaled down proportionally to the emboldening so
+     * that curve points don't fall outside their precomputed blue zones.
+     *
+     * Any emboldening done by the font driver (e.g. the CFF driver) doesn't
+     * reach here because the autohinter loads the unprocessed glyphs in font
+     * units for analysis (af_*_metrics_init_*) and then above to prepare it
+     * for the rasterizers by itself, independently of the font driver. So
+     * emboldening must be done here, within the autohinter.
+     *
+     * All glyphs to be autohinted pass through here one by one. The standard
+     * widths can therefore change from one glyph to the next, depending on
+     * what script a glyph is assigned to (each script has its' own set of
+     * standard widths and other metrics). The darkening amount must therefore
+     * be recomputed for each size and standard_(vertical|horizontal)_width
+     * change.
+     */
+#define cf2_intToFixed( i )   ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
+#define cf2_fixedToInt( x )   ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define cf2_floatToFixed( f ) ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
+    if ( !module->no_stem_darkening )
+    {
+      AF_FaceGlobals globals = loader->globals;
+      FT_Pos         stdVW   = 0;
+      FT_Pos         stdHW   = 0;
+      FT_Bool        size_changed = face->size->metrics.x_ppem
+                                    != globals->stem_darkening_for_ppem;
+      FT_Fixed       emSize  = cf2_intToFixed( face->units_per_EM );
+      FT_Fixed       emRatio = FT_DivFix( cf2_intToFixed( 1000 ), emSize );
+      FT_Matrix      scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };
+      AF_WritingSystemClass writing_system_class;
+
+      /* Skip stem darkening for broken fonts. */
+      if ( face->units_per_EM <= 0 )
+        goto After_Emboldening;
+
+      /* We depend on the writing system (script analyzers) to supply
+       * standard_widths for the script of the glyph we're looking at. If it
+       * can't deliver, stem darkening is effectively disabled. */
+      writing_system_class =
+        AF_WRITING_SYSTEM_CLASSES_GET[metrics->style_class->writing_system];
+
+      if ( writing_system_class->style_metrics_getstdw )
+        writing_system_class->style_metrics_getstdw( metrics, &stdHW, &stdVW );
+      else
+        goto After_Emboldening;
+
+
+      if ( size_changed || ( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
+      {
+        FT_Fixed darken_by_font_units_x, darken_x;
+
+        globals->standard_vertical_width = stdVW;
+        globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
+        darken_by_font_units_x = cf2_intToFixed( af_loader_compute_darkening( module, face, stdVW ) );
+        darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x, face->size->metrics.x_scale ),
+                              emRatio );
+        globals->darken_x = cf2_fixedToInt( darken_x );
+      }
+
+      if ( size_changed || ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
+      {
+        FT_Fixed darken_by_font_units_y, darken_y;
+
+        globals->standard_horizontal_width = stdHW;
+        globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
+        darken_by_font_units_y = cf2_intToFixed( af_loader_compute_darkening( module, face, stdHW ) );
+        darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y, face->size->metrics.y_scale ),
+                              emRatio );
+        globals->darken_y = cf2_fixedToInt( darken_y );
+
+        /* Scale outlines down on the Y-axis to keep them inside their blue
+         * zones. The stronger the emboldening, the stronger the downscaling
+         * (plus heuristical padding to prevent outlines still falling out
+         * their zones due to rounding).
+         *
+         * Reason: FT_Outline_Embolden works by shifting the rightmost points of stems
+         * farther to the right, and topmost points farther up. This lands
+         * points on the Y-axis outside their pre-computed blue zones and leads
+         * to distortion when applying the hints in the code further below.
+         * Code outside this emboldening block doesn't know we're presenting it
+         * with modified outlines the analyzer didn't see!
+         *
+         * An unfortunate side-effect of downscaling is that the emboldening
+         * effect is slightly decreased. The loss becomes more pronounced
+         * versus the CFF driver at smaller sizes, e.g. 9ppem and below. */
+        globals->scale_down_factor = FT_DivFix( emSize - (darken_by_font_units_y + cf2_intToFixed( 6 )),
+                                                emSize );
+      }
+
+      FT_Outline_EmboldenXY( &slot->outline, globals->darken_x, globals->darken_y );
+
+      scale_down_matrix.yy = globals->scale_down_factor;
+      FT_Outline_Transform( &slot->outline, &scale_down_matrix );
+    }
+
+After_Emboldening:
     loader->transformed = internal->glyph_transformed;
     if ( loader->transformed )
     {
@@ -397,7 +495,7 @@
             goto Exit;
         }
 
-        error = af_loader_load_g( loader, &scaler, gindex, load_flags );
+        error = af_loader_load_g( loader, module, &scaler, gindex, load_flags );
       }
     }
   Exit:
@@ -410,9 +508,6 @@
    * algorithm.
    * XXX: Currently a crude adaption of the original algorithm. Do better?
    */
-#define cf2_intToFixed( i )   ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
-#define cf2_fixedToInt( x )   ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
-#define cf2_floatToFixed( f ) ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
   FT_LOCAL_DEF( FT_Int32 )
   af_loader_compute_darkening( AF_Module module,
                                FT_Face   face,
-- 
2.4.3

>From 3986a7fd7b0c7f8f98545a8e38b05c4f255a3d51 Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Sat, 24 Oct 2015 15:54:24 +0200
Subject: [PATCH 4/5] Implement darkening computation function

Crude adaption of the original cf2_computeDarkening().
---
 src/autofit/afloader.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/autofit/afloader.h |   5 ++
 2 files changed, 133 insertions(+)

diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c
index 722ffd3..99523f4 100644
--- a/src/autofit/afloader.c
+++ b/src/autofit/afloader.c
@@ -405,4 +405,132 @@
   }
 
 
+  /* Compute amount of font units the face should be emboldened by, analogous
+   * to the CFF driver's cf2_computeDarkening(). See there for details of the
+   * algorithm.
+   * XXX: Currently a crude adaption of the original algorithm. Do better?
+   */
+#define cf2_intToFixed( i )   ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
+#define cf2_fixedToInt( x )   ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define cf2_floatToFixed( f ) ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
+  FT_LOCAL_DEF( FT_Int32 )
+  af_loader_compute_darkening( AF_Module module,
+                               FT_Face   face,
+                               FT_Pos    standard_width )
+  {
+    FT_UShort units_per_EM;
+    FT_Fixed  ppem, emRatio;
+    FT_Fixed  stemWidth, stemWidthPer1000, scaledStem, darkenAmount;
+    FT_Int    logBase2;
+    FT_Int    x1, y1, x2, y2, x3, y3, x4, y4;
+
+    ppem         = FT_MAX( cf2_intToFixed( 4 ),
+                           cf2_intToFixed( face->size->metrics.x_ppem ) );
+    units_per_EM = face->units_per_EM;
+
+    if ( units_per_EM < 0 )
+      /* If something goes wrong, don't embolden. */
+      return 0;
+
+    emRatio = FT_DivFix( cf2_intToFixed( 1000 ), cf2_intToFixed ( units_per_EM ) );
+    if ( emRatio < cf2_floatToFixed( .01 ) )
+    {
+      /* If something goes wrong, don't embolden. */
+      return 0;
+    }
+
+    x1 = module->darken_params[0];
+    y1 = module->darken_params[1];
+    x2 = module->darken_params[2];
+    y2 = module->darken_params[3];
+    x3 = module->darken_params[4];
+    y3 = module->darken_params[5];
+    x4 = module->darken_params[6];
+    y4 = module->darken_params[7];
+
+    if ( standard_width <= 0)
+    {
+      stemWidth = cf2_intToFixed( 75 ); /* Taken from cf2font.c */
+      stemWidthPer1000 = stemWidth;
+    }
+    else
+    {
+      stemWidth = cf2_intToFixed( standard_width );
+      stemWidthPer1000 = FT_MulFix( stemWidth, emRatio );
+    }
+
+    logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) +
+      FT_MSB( (FT_UInt32)ppem );
+
+    if ( logBase2 >= 46 )
+      /* possible overflow */
+      scaledStem = cf2_intToFixed( x4 );
+    else
+      scaledStem = FT_MulFix( stemWidthPer1000, ppem );
+
+    /* now apply the darkening parameters */
+    if ( scaledStem < cf2_intToFixed( x1 ) )
+      darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );
+
+    else if ( scaledStem < cf2_intToFixed( x2 ) )
+    {
+      FT_Int  xdelta = x2 - x1;
+      FT_Int  ydelta = y2 - y1;
+      FT_Int  x      = stemWidthPer1000 -
+        FT_DivFix( cf2_intToFixed( x1 ), ppem );
+
+
+      if ( !xdelta )
+        goto Try_x3;
+
+      darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+        FT_DivFix( cf2_intToFixed( y1 ), ppem );
+    }
+
+    else if ( scaledStem < cf2_intToFixed( x3 ) )
+    {
+Try_x3:
+      {
+        FT_Int  xdelta = x3 - x2;
+        FT_Int  ydelta = y3 - y2;
+        FT_Int  x      = stemWidthPer1000 -
+          FT_DivFix( cf2_intToFixed( x2 ), ppem );
+
+
+        if ( !xdelta )
+          goto Try_x4;
+
+        darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+          FT_DivFix( cf2_intToFixed( y2 ), ppem );
+      }
+    }
+
+    else if ( scaledStem < cf2_intToFixed( x4 ) )
+    {
+Try_x4:
+      {
+        FT_Int  xdelta = x4 - x3;
+        FT_Int  ydelta = y4 - y3;
+        FT_Int  x      = stemWidthPer1000 -
+          FT_DivFix( cf2_intToFixed( x3 ), ppem );
+
+
+        if ( !xdelta )
+          goto Use_y4;
+
+        darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+          FT_DivFix( cf2_intToFixed( y3 ), ppem );
+      }
+    }
+
+    else
+    {
+Use_y4:
+      darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
+    }
+
+    /* Convert darkenAmount from per 1000 em to true character space. */
+    return cf2_fixedToInt( FT_DivFix( darkenAmount, emRatio ) );
+  }
+
 /* END */
diff --git a/src/autofit/afloader.h b/src/autofit/afloader.h
index 37cfd14..f2963ce 100644
--- a/src/autofit/afloader.h
+++ b/src/autofit/afloader.h
@@ -75,6 +75,11 @@ FT_BEGIN_HEADER
                         FT_UInt    gindex,
                         FT_Int32   load_flags );
 
+  FT_LOCAL_DEF( FT_Int32 )
+  af_loader_compute_darkening( AF_Module module,
+                               FT_Face   face,
+                               FT_Pos    standard_width );
+
 /* */
 
 
-- 
2.4.3

>From 50e8bb2d7dc42223228464aa14a0b86f9112cb3c Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Sat, 24 Oct 2015 15:46:20 +0200
Subject: [PATCH 3/5] Add and implement af_*_get_standard_widths for
 AF_WritingSystem

We need the computed standard horizontal and vertical widths for the
emboldening calculation. This method provides a convenient way to
extract it from writing-system-specific metrics structures, all script
definitions must implement it.
---
 src/autofit/afcjk.c    | 17 +++++++++++++++++
 src/autofit/afdummy.c  |  1 +
 src/autofit/afindic.c  | 17 +++++++++++++++++
 src/autofit/aflatin.c  | 16 ++++++++++++++++
 src/autofit/aflatin2.c | 16 ++++++++++++++++
 src/autofit/aftypes.h  |  9 +++++++++
 6 files changed, 76 insertions(+)

diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c
index 0ade4be..74c200b 100644
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -688,6 +688,21 @@
   }
 
 
+  /* Extract standard_width from writing system/script specific metrics class */
+
+  FT_LOCAL_DEF( void )
+  af_cjk_get_standard_widths( AF_CJKMetrics  metrics,
+                              FT_Pos*        stdHW,
+                              FT_Pos*        stdVW )
+  {
+    if ( stdHW )
+      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
+
+    if ( stdVW )
+      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
+  }
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -2280,6 +2295,7 @@
     (AF_WritingSystem_InitMetricsFunc) af_cjk_metrics_init,
     (AF_WritingSystem_ScaleMetricsFunc)af_cjk_metrics_scale,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)af_cjk_get_standard_widths,
 
     (AF_WritingSystem_InitHintsFunc)   af_cjk_hints_init,
     (AF_WritingSystem_ApplyHintsFunc)  af_cjk_hints_apply
@@ -2299,6 +2315,7 @@
     (AF_WritingSystem_InitMetricsFunc) NULL,
     (AF_WritingSystem_ScaleMetricsFunc)NULL,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)NULL,
 
     (AF_WritingSystem_InitHintsFunc)   NULL,
     (AF_WritingSystem_ApplyHintsFunc)  NULL
diff --git a/src/autofit/afdummy.c b/src/autofit/afdummy.c
index 18dd301..9142c78 100644
--- a/src/autofit/afdummy.c
+++ b/src/autofit/afdummy.c
@@ -65,6 +65,7 @@
     (AF_WritingSystem_InitMetricsFunc) NULL,
     (AF_WritingSystem_ScaleMetricsFunc)NULL,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)NULL,
 
     (AF_WritingSystem_InitHintsFunc)   af_dummy_hints_init,
     (AF_WritingSystem_ApplyHintsFunc)  af_dummy_hints_apply
diff --git a/src/autofit/afindic.c b/src/autofit/afindic.c
index 05b6bdd..2a578a4 100644
--- a/src/autofit/afindic.c
+++ b/src/autofit/afindic.c
@@ -89,6 +89,21 @@
   }
 
 
+  /* Extract standard_width from writing system/script specific metrics class */
+
+  static void
+  af_indic_get_standard_widths( AF_CJKMetrics  metrics,
+                                FT_Pos*        stdHW,
+                                FT_Pos*        stdVW )
+  {
+    if ( stdHW )
+      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
+
+    if ( stdVW )
+      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
+  }
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -108,6 +123,7 @@
     (AF_WritingSystem_InitMetricsFunc) af_indic_metrics_init,
     (AF_WritingSystem_ScaleMetricsFunc)af_indic_metrics_scale,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)af_indic_get_standard_widths,
 
     (AF_WritingSystem_InitHintsFunc)   af_indic_hints_init,
     (AF_WritingSystem_ApplyHintsFunc)  af_indic_hints_apply
@@ -127,6 +143,7 @@
     (AF_WritingSystem_InitMetricsFunc) NULL,
     (AF_WritingSystem_ScaleMetricsFunc)NULL,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)NULL,
 
     (AF_WritingSystem_InitHintsFunc)   NULL,
     (AF_WritingSystem_ApplyHintsFunc)  NULL
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index 363f721..ddf3b6c 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -1149,6 +1149,21 @@
   }
 
 
+  /* Extract standard_width from writing system/script specific metrics class */
+
+  FT_LOCAL_DEF( void )
+  af_latin_get_standard_widths( AF_LatinMetrics  metrics,
+                                FT_Pos*          stdHW,
+                                FT_Pos*          stdVW )
+  {
+    if ( stdHW )
+      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
+
+    if ( stdVW )
+      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
+  }
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -2944,6 +2959,7 @@
     (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init,
     (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc) af_latin_get_standard_widths,
 
     (AF_WritingSystem_InitHintsFunc)   af_latin_hints_init,
     (AF_WritingSystem_ApplyHintsFunc)  af_latin_hints_apply
diff --git a/src/autofit/aflatin2.c b/src/autofit/aflatin2.c
index 0380ffc..0996e7f 100644
--- a/src/autofit/aflatin2.c
+++ b/src/autofit/aflatin2.c
@@ -693,6 +693,21 @@
   }
 
 
+  /* Extract standard_width from writing system/script specific metrics class */
+
+  FT_LOCAL_DEF( void )
+  af_latin2_get_standard_widths( AF_LatinMetrics  metrics,
+                                 FT_Pos*          stdHW,
+                                 FT_Pos*          stdVW )
+  {
+    if ( stdHW )
+      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
+
+    if ( stdVW )
+      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
+  }
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -2392,6 +2407,7 @@
     (AF_WritingSystem_InitMetricsFunc) af_latin2_metrics_init,
     (AF_WritingSystem_ScaleMetricsFunc)af_latin2_metrics_scale,
     (AF_WritingSystem_DoneMetricsFunc) NULL,
+    (AF_WritingSystem_GetStdWidthsFunc)af_latin2_get_standard_widths,
 
     (AF_WritingSystem_InitHintsFunc)   af_latin2_hints_init,
     (AF_WritingSystem_ApplyHintsFunc)  af_latin2_hints_apply
diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h
index 43b3800..86152de 100644
--- a/src/autofit/aftypes.h
+++ b/src/autofit/aftypes.h
@@ -211,6 +211,11 @@ extern void*  _af_debug_hints;
   typedef void
   (*AF_WritingSystem_DoneMetricsFunc)( AF_StyleMetrics  metrics );
 
+  typedef void
+  (*AF_WritingSystem_GetStdWidthsFunc)( AF_StyleMetrics  metrics,
+                                        FT_Pos*          stdHW,
+                                        FT_Pos*          stdVW );
+
 
   typedef FT_Error
   (*AF_WritingSystem_InitHintsFunc)( AF_GlyphHints    hints,
@@ -276,6 +281,7 @@ extern void*  _af_debug_hints;
     AF_WritingSystem_InitMetricsFunc   style_metrics_init;
     AF_WritingSystem_ScaleMetricsFunc  style_metrics_scale;
     AF_WritingSystem_DoneMetricsFunc   style_metrics_done;
+    AF_WritingSystem_GetStdWidthsFunc  style_metrics_getstdw;
 
     AF_WritingSystem_InitHintsFunc     style_hints_init;
     AF_WritingSystem_ApplyHintsFunc    style_hints_apply;
@@ -489,6 +495,7 @@ extern void*  _af_debug_hints;
           m_init,                                        \
           m_scale,                                       \
           m_done,                                        \
+          m_stdw,                                        \
           h_init,                                        \
           h_apply )                                      \
   FT_CALLBACK_TABLE_DEF                                  \
@@ -501,6 +508,7 @@ extern void*  _af_debug_hints;
     m_init,                                              \
     m_scale,                                             \
     m_done,                                              \
+    m_stdw,                                              \
                                                          \
     h_init,                                              \
     h_apply                                              \
@@ -577,6 +585,7 @@ extern void*  _af_debug_hints;
     ac->style_metrics_init  = m_init;                                     \
     ac->style_metrics_scale = m_scale;                                    \
     ac->style_metrics_done  = m_done;                                     \
+    ac->style_metrics_getstdw = m_stdw;                                   \
                                                                           \
     ac->style_hints_init    = h_init;                                     \
     ac->style_hints_apply   = h_apply;                                    \
-- 
2.4.3

>From 8f5a5927dc8ab01a6456b02443ce37620f6dfe1d Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Sat, 24 Oct 2015 15:33:39 +0200
Subject: [PATCH 2/5] Extend AF_FaceGlobalsRec_ to hold emboldening data

And zero everything upon initialization and freeing.
---
 src/autofit/afglobal.c | 12 ++++++++++++
 src/autofit/afglobal.h | 18 ++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c
index b071cc7..631cb64 100644
--- a/src/autofit/afglobal.c
+++ b/src/autofit/afglobal.c
@@ -355,6 +355,12 @@
     /* right after the globals structure come the glyph styles */
     globals->glyph_styles = (FT_UShort*)( globals + 1 );
     globals->module       = module;
+    globals->stem_darkening_for_ppem = 0;
+    globals->darken_x     = 0;
+    globals->darken_y     = 0;
+    globals->standard_vertical_width = 0;
+    globals->standard_horizontal_width = 0;
+    globals->scale_down_factor = 0;
 
 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
     globals->hb_font = hb_ft_font_create( face, NULL );
@@ -407,6 +413,12 @@
 #endif
 
       globals->glyph_count  = 0;
+      globals->stem_darkening_for_ppem = 0;
+      globals->darken_x = 0;
+      globals->darken_y = 0;
+      globals->standard_vertical_width = 0;
+      globals->standard_horizontal_width = 0;
+      globals->scale_down_factor = 0;
       globals->glyph_styles = NULL;  /* no need to free this one! */
       globals->face         = NULL;
 
diff --git a/src/autofit/afglobal.h b/src/autofit/afglobal.h
index ffb2f86..f35ef7b 100644
--- a/src/autofit/afglobal.h
+++ b/src/autofit/afglobal.h
@@ -117,6 +117,24 @@ FT_BEGIN_HEADER
 
     AF_StyleMetrics  metrics[AF_STYLE_MAX];
 
+    FT_UShort        stem_darkening_for_ppem; /* Compute darkening amount once
+                                                 per size. Use this to check if
+                                                 darken_(x|y) needs to be
+                                                 recomputed. */
+    FT_Pos           standard_vertical_width; /* Copy from e.g.
+                                                 AF_LatinMetrics.axis[AF_DIMENSION_HORZ],
+                                                 to compute the darkening
+                                                 amount. */
+    FT_Pos           standard_horizontal_width; /* Copy from e.g.
+                                                   AF_LatinMetrics.axis[AF_DIMENSION_VERT],
+                                                   to compute the darkening
+                                                   amount. */
+    FT_Pos           darken_x;       /* The actual amount to darken a glyph by (X axis). */
+    FT_Pos           darken_y;       /* The actual amount to darken a glyph by (Y axis). */
+    FT_Fixed         scale_down_factor; /* Amount to scale down by to keep
+                                           emboldened points on the Y-axis in
+                                           pre-computed blue zones. */
+
     AF_Module        module;         /* to access global properties */
 
   } AF_FaceGlobalsRec;
-- 
2.4.3

>From a0a298f9528e9ca0d9011c41c721bd1158579a5f Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <[email protected]>
Date: Sat, 24 Oct 2015 15:21:01 +0200
Subject: [PATCH 1/5] Properties for setting up stem darkening in the
 autohinter

- Property setters and getters for no-stem-darkening and
  darkening-parameters.
- Documentation in ftautoh.h
---
 include/freetype/ftautoh.h | 51 +++++++++++++++++++++++++++++++
 src/autofit/afmodule.c     | 76 ++++++++++++++++++++++++++++++++++++++++++++++
 src/autofit/afmodule.h     |  3 +-
 3 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/include/freetype/ftautoh.h b/include/freetype/ftautoh.h
index ab39c21..fdfc3cb 100644
--- a/include/freetype/ftautoh.h
+++ b/include/freetype/ftautoh.h
@@ -439,6 +439,57 @@ FT_BEGIN_HEADER
    */
 
 
+  /**************************************************************************
+   *
+   * @property:
+   *   no-stem-darkening
+   *
+   * @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
+   *   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. This means, a
+   *   rendering library must apply linear alpha blending when 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.
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @property:
+   *   darkening-parameters
+   *
+   * @description:
+   *   *Experimental* *only*
+   *
+   *   See the description of the CFF driver for details. This implementation
+   *   appropriates the CFF_CONFIG_OPTION_DARKENING_PARAMETER_* #defines for
+   *   consistency. Note the differences described in @no-stem-darkening.
+   *
+   */
+
+
   /* */
 
 
diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c
index bbf1372..6a318ba 100644
--- a/src/autofit/afmodule.c
+++ b/src/autofit/afmodule.c
@@ -177,6 +177,45 @@
       return error;
     }
 #endif /* AF_CONFIG_OPTION_USE_WARPER */
+    else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+    {
+        FT_Int*  darken_params = (FT_Int*)value;
+
+        FT_Int  x1 = darken_params[0];
+        FT_Int  y1 = darken_params[1];
+        FT_Int  x2 = darken_params[2];
+        FT_Int  y2 = darken_params[3];
+        FT_Int  x3 = darken_params[4];
+        FT_Int  y3 = darken_params[5];
+        FT_Int  x4 = darken_params[6];
+        FT_Int  y4 = darken_params[7];
+
+        if ( x1 < 0   || x2 < 0   || x3 < 0   || x4 < 0   ||
+             y1 < 0   || y2 < 0   || y3 < 0   || y4 < 0   ||
+             x1 > x2  || x2 > x3  || x3 > x4              ||
+             y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
+          return FT_THROW( Invalid_Argument );
+
+        module->darken_params[0] = x1;
+        module->darken_params[1] = y1;
+        module->darken_params[2] = x2;
+        module->darken_params[3] = y2;
+        module->darken_params[4] = x3;
+        module->darken_params[5] = y3;
+        module->darken_params[6] = x4;
+        module->darken_params[7] = y4;
+
+        return error;
+    }
+    else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+    {
+        FT_Bool*  no_stem_darkening = (FT_Bool*)value;
+
+
+        module->no_stem_darkening = *no_stem_darkening;
+
+        return error;
+    }
 
     FT_TRACE0(( "af_property_set: missing property `%s'\n",
                 property_name ));
@@ -253,6 +292,33 @@
       return error;
     }
 #endif /* AF_CONFIG_OPTION_USE_WARPER */
+    else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+    {
+        FT_Int*  darken_params = module->darken_params;
+        FT_Int*  val           = (FT_Int*)value;
+
+
+        val[0] = darken_params[0];
+        val[1] = darken_params[1];
+        val[2] = darken_params[2];
+        val[3] = darken_params[3];
+        val[4] = darken_params[4];
+        val[5] = darken_params[5];
+        val[6] = darken_params[6];
+        val[7] = darken_params[7];
+
+        return error;
+    }
+    else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+    {
+        FT_Bool   no_stem_darkening = module->no_stem_darkening;
+        FT_Bool*  val               = (FT_Bool*)value;
+
+
+        *val = no_stem_darkening;
+
+        return error;
+    }
 
     FT_TRACE0(( "af_property_get: missing property `%s'\n",
                 property_name ));
@@ -304,6 +370,16 @@
 #ifdef AF_CONFIG_OPTION_USE_WARPER
     module->warping        = 0;
 #endif
+    module->no_stem_darkening = FALSE;
+
+    module->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
+    module->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
+    module->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
+    module->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
+    module->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
+    module->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
+    module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
+    module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
 
     return FT_Err_Ok;
   }
diff --git a/src/autofit/afmodule.h b/src/autofit/afmodule.h
index b9c2fd8..d23018a 100644
--- a/src/autofit/afmodule.h
+++ b/src/autofit/afmodule.h
@@ -41,7 +41,8 @@ FT_BEGIN_HEADER
 #ifdef AF_CONFIG_OPTION_USE_WARPER
     FT_Bool       warping;
 #endif
-
+    FT_Bool       no_stem_darkening;
+    FT_Int        darken_params[8];
   } AF_ModuleRec, *AF_Module;
 
 
-- 
2.4.3

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

Reply via email to