I just noticed that the darkening calculation might be off, making stuff excessively bold. Here's a new patch. Sorry :(
diff --git a/include/freetype/ftautoh.h b/include/freetype/ftautoh.h
index cf7b76f..68d59e7 100644
--- a/include/freetype/ftautoh.h
+++ b/include/freetype/ftautoh.h
@@ -439,6 +439,36 @@ FT_BEGIN_HEADER
    */
 
 
+  /**************************************************************************
+   *
+   * @property:
+   *   no-stem-darkening
+   *
+   * @description:
+   *   *Experimental* *only*
+   *
+   *   See the description of the CFF driver for details. Stem darkening is a
+   *   side effect of using the auto-hinter unless setting this property to
+   *   true.
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @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.
+   *
+   */
+
+
   /* */
 
 
diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c
index 905408b..bfbe95d 100644
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -247,6 +247,25 @@
 
     FT_TRACE5(( "\n" ));
 
+    /* Grab the standard_width for the X-axis (i.e. vertical features) and
+     * use as standard_width of stems to compute darkening amount with,
+     * analogous to invoking cf2_computeDarkening() with stdVW.
+     *
+     * XXX: Since standard_width varies depending on what glyph is being
+     * displayed and therefore which script analyzer is invoked, always use the
+     * biggest value for the stem darkening computation. In my limited testing,
+     * this was usually near the "true" value.
+     *
+     * XXX: Values can be significantly bigger than what the stdVW PS dict
+     * entry says on well hinted OpenType/CFF fonts, resulting in less
+     * darkening and therefore less contrast. */
+    if ( metrics->root.globals->standard_vertical_width < metrics->axis[AF_DIMENSION_HORZ].standard_width )
+    {
+      metrics->root.globals->standard_vertical_width_old = metrics->root.globals->standard_vertical_width;
+      metrics->root.globals->standard_vertical_width =
+        metrics->axis[AF_DIMENSION_HORZ].standard_width;
+    }
+
     af_glyph_hints_done( hints );
   }
 
diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c
index 64b9293..69314e1 100644
--- a/src/autofit/afglobal.c
+++ b/src/autofit/afglobal.c
@@ -323,6 +323,10 @@
     globals->glyph_count  = face->num_glyphs;
     globals->glyph_styles = (FT_Byte*)( globals + 1 );
     globals->module       = module;
+    globals->stem_darkening_for_ppem = 0;
+    globals->darken_x     = 0;
+    globals->standard_vertical_width = 0;
+    globals->standard_vertical_width_old = 0;
 
 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
     globals->hb_font = hb_ft_font_create( face, NULL );
@@ -375,6 +379,10 @@
 #endif
 
       globals->glyph_count  = 0;
+      globals->stem_darkening_for_ppem = 0;
+      globals->darken_x = 0;
+      globals->standard_vertical_width = 0;
+      globals->standard_vertical_width_old = 0;
       globals->glyph_styles = NULL;  /* no need to free this one! */
       globals->face         = NULL;
 
@@ -450,7 +458,6 @@
     return error;
   }
 
-
   FT_LOCAL_DEF( FT_Bool )
   af_face_globals_is_digit( AF_FaceGlobals  globals,
                             FT_UInt         gindex )
diff --git a/src/autofit/afglobal.h b/src/autofit/afglobal.h
index 9bbb687..4589433 100644
--- a/src/autofit/afglobal.h
+++ b/src/autofit/afglobal.h
@@ -111,6 +111,17 @@ 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 needs to be
+                                                 recomputed. */
+    FT_Pos           darken_x;
+    FT_Pos           standard_vertical_width; /* Copy from e.g.
+                                                 AF_LatinMetrics.axis[AF_DIMENSION_HORZ],
+                                                 to compute the darkening
+                                                 amount. */
+    FT_Pos           standard_vertical_width_old; /* Hack for less robust way of finding "true" width. */
+
     AF_Module        module;         /* to access global properties */
 
   } AF_FaceGlobalsRec;
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index 3065895..e8b13a5 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -251,6 +251,25 @@
 
     FT_TRACE5(( "\n" ));
 
+    /* Grab the standard_width for the X-axis (i.e. vertical features) and
+     * use as standard_width of stems to compute darkening amount with,
+     * analogous to invoking cf2_computeDarkening() with stdVW.
+     *
+     * XXX: Since standard_width varies depending on what glyph is being
+     * displayed and therefore which script analyzer is invoked, always use the
+     * biggest value for the stem darkening computation. In my limited testing,
+     * this was usually near the "true" value.
+     *
+     * XXX: Values can be significantly bigger than what the stdVW PS dict
+     * entry says on well hinted OpenType/CFF fonts, resulting in less
+     * darkening and therefore less contrast. */
+    if ( metrics->root.globals->standard_vertical_width < metrics->axis[AF_DIMENSION_HORZ].standard_width )
+    {
+      metrics->root.globals->standard_vertical_width_old = metrics->root.globals->standard_vertical_width;
+      metrics->root.globals->standard_vertical_width =
+        metrics->axis[AF_DIMENSION_HORZ].standard_width;
+    }
+
     af_glyph_hints_done( hints );
   }
 
diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c
index 7c2fa7c..8475c23 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 )
@@ -320,6 +321,37 @@
       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
     }
 
+    /* Apply stem darkening here. Darken only on the X axis, as the autohinter
+     * snaps to the Y axis. This matches the behavior of Adobe's proprietary
+     * TrueType engine.
+     *
+     * Note: The outline must be emboldened after it's loaded by
+     * FT_Load_Glyph(), but only in this function! Other occurences of
+     * FT_Load_Glyph in the writing system class implementations compute their
+     * hints using the Y axis, so emboldening does nothing there.
+     *
+     * First check if we already computed the darkening amount for the size we
+     * are given so the computation is done only once per size. Then check if
+     * standard_vertical_width has changed -- this can happen when for
+     * different glyphs, different writing class style analyzers run over the
+     * face testing different characters and coming up with different
+     * standard_vertical_widths. This means the computation may happen multiple
+     * times per size. In my limited testing, the biggest value was usually not
+     * too far off the mark except when it was. */
+    if ( !module->no_stem_darkening )
+    {
+      if ( face->size->metrics.x_ppem != loader->globals->stem_darkening_for_ppem
+          || loader->globals->standard_vertical_width
+             > loader->globals->standard_vertical_width_old)
+      {
+        af_loader_compute_darkening( loader, module );
+        loader->globals->standard_vertical_width_old =
+          loader->globals->standard_vertical_width;
+      }
+      if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+        FT_Outline_EmboldenXY(&slot->outline, loader->globals->darken_x, 0);
+    }
+
   Exit:
     return error;
   }
@@ -396,7 +428,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:
@@ -404,4 +436,138 @@
   }
 
 
+  /* Compute amount of pixels 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?
+   * XXX: what about the emboldening effect in cf2_computeDarkening()? */
+  FT_LOCAL_DEF( void )
+  af_loader_compute_darkening( AF_Loader loader,
+                               AF_Module module )
+  {
+    #define cf2_intToFixed( i )   ( (FT_Int32)( (FT_UInt32)(i) << 16 ) )
+    #define cf2_fixedToInt( x )   ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+    #define cf2_floatToFixed( f ) ( (FT_Int32)( (f) * 65536.0 + 0.5 ) )
+    FT_UShort units_per_EM;
+    FT_Int32  ppem, emRatio;
+    FT_Int32  stemWidth, stemWidthPer1000, scaledStem, darkenAmount;
+    FT_Int    logBase2;
+    FT_Int    x1, y1, x2, y2, x3, y3, x4, y4;
+    FT_Pos    stdVW;
+
+    ppem         = cf2_intToFixed(loader->face->size->metrics.x_ppem);
+    ppem         = FT_MAX( cf2_intToFixed( 4 ), ppem );
+    units_per_EM = loader->face->units_per_EM;
+
+    if ( units_per_EM == 0 )
+      units_per_EM = 1000;
+
+    emRatio = cf2_intToFixed( 1000 ) / units_per_EM;
+    if ( emRatio < cf2_floatToFixed( .01 ) )
+    {
+      /* If something goes wrong, silently disable darkening for the size. */
+      loader->globals->darken_x = 0;
+      loader->globals->stem_darkening_for_ppem = loader->globals->face->size->metrics.x_ppem;
+      return;
+    }
+
+    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];
+
+    stdVW = loader->globals->standard_vertical_width;
+    if ( stdVW <= 0)
+    {
+      stemWidth = cf2_intToFixed( 75 ); /* Taken from cf2font.c */
+      stemWidthPer1000 = stemWidth;
+    }
+    else
+    {
+      stemWidth = cf2_intToFixed( stdVW );
+      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 back to true character space. */
+    darkenAmount = FT_DivFix( darkenAmount, emRatio * 2 );
+
+    loader->globals->darken_x = cf2_fixedToInt(darkenAmount);
+    loader->globals->stem_darkening_for_ppem = loader->globals->face->size->metrics.x_ppem;
+    return;
+  }
+
 /* END */
diff --git a/src/autofit/afloader.h b/src/autofit/afloader.h
index 37cfd14..3c952de 100644
--- a/src/autofit/afloader.h
+++ b/src/autofit/afloader.h
@@ -75,6 +75,10 @@ FT_BEGIN_HEADER
                         FT_UInt    gindex,
                         FT_Int32   load_flags );
 
+  FT_LOCAL_DEF( void )
+  af_loader_compute_darkening( AF_Loader loader,
+                               AF_Module module );
+
 /* */
 
 
diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c
index 8ae425c..614b118 100644
--- a/src/autofit/afmodule.c
+++ b/src/autofit/afmodule.c
@@ -169,6 +169,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 ));
@@ -245,6 +284,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 ));
@@ -296,6 +362,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;
 
 
_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to