Commit caa48b0db1a4a79f08670d45027988799b2e4c3c

Werner Lemberg 2016-03-05T19:47:07

[autofit] Avoid excessive stem length rounding (#25392). * src/autofit/aflatin.c (af_latin_compute_stem_width): Add argument to pass difference between hinted and unhinted position of base point; use this to adjust the stem width depending on the PPEM so that it doesn't become too large under certain circumstances. Update all callers using value 0 for this argument except... (af_latin_align_linked_edge): Pass position delta of base point to `af_latin_compute_stem_width'.

diff --git a/ChangeLog b/ChangeLog
index 4c3cab8..68baf19 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2016-03-05  Werner Lemberg  <wl@gnu.org>
+
+	[autofit] Avoid excessive stem length rounding (#25392).
+
+	* src/autofit/aflatin.c (af_latin_compute_stem_width): Add argument
+	to pass difference between hinted and unhinted position of base
+	point; use this to adjust the stem width depending on the PPEM so
+	that it doesn't become too large under certain circumstances.
+	Update all callers using value 0 for this argument except...
+	(af_latin_align_linked_edge): Pass position delta of base point to
+	`af_latin_compute_stem_width'.
+
 2016-03-05  J Raynor <jxraynor@gmail.com>
 
 	Make FreeType compile on AIX out of the box.
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index e9fb9df..dddfada 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -2504,12 +2504,14 @@
   af_latin_compute_stem_width( AF_GlyphHints  hints,
                                AF_Dimension   dim,
                                FT_Pos         width,
+                               FT_Pos         base_delta,
                                FT_UInt        base_flags,
                                FT_UInt        stem_flags )
   {
     AF_LatinMetrics  metrics  = (AF_LatinMetrics)hints->metrics;
     AF_LatinAxis     axis     = &metrics->axis[dim];
     FT_Pos           dist     = width;
+    FT_Pos           bdelta   = 0;
     FT_Int           sign     = 0;
     FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
 
@@ -2524,6 +2526,32 @@
       sign = 1;
     }
 
+    /* A stem's end position depends on two values: the start position    */
+    /* and the stem length.  The former gets usually rounded to the grid, */
+    /* while the latter gets rounded also if it exceeds a certain length  */
+    /* (see below in this function).  This `double rounding' can lead to  */
+    /* a great difference to the original, unhinted position; this        */
+    /* normally doesn't matter for large PPEM values, but for small sizes */
+    /* it can easily make outlines collide.  For this reason, we adjust   */
+    /* the stem length by a small amount depending on the PPEM value in   */
+    /* case the former and latter rounding both point into the same       */
+    /* direction.                                                         */
+
+    if ( ( ( width > 0 ) && ( base_delta > 0 ) ) ||
+         ( ( width < 0 ) && ( base_delta < 0 ) ) )
+    {
+      FT_UInt  ppem = metrics->root.scaler.face->size->metrics.x_ppem;
+
+
+      if ( ppem < 10 )
+        bdelta = base_delta;
+      else if ( ppem < 30 )
+        bdelta = ( base_delta * (FT_Pos)( 30 - ppem ) ) / 20;
+
+      if ( bdelta < 0 )
+        bdelta = -bdelta;
+    }
+
     if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
          ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
     {
@@ -2581,7 +2609,7 @@
             dist += delta;
         }
         else
-          dist = ( dist + 32 ) & ~63;
+          dist = ( dist - bdelta + 32 ) & ~63;
       }
     }
     else
@@ -2670,11 +2698,17 @@
                               AF_Edge        base_edge,
                               AF_Edge        stem_edge )
   {
-    FT_Pos  dist = stem_edge->opos - base_edge->opos;
+    FT_Pos  dist, base_delta;
+    FT_Pos  fitted_width;
+
+
+    dist       = stem_edge->opos - base_edge->opos;
+    base_delta = base_edge->pos - base_edge->opos;
 
-    FT_Pos  fitted_width = af_latin_compute_stem_width( hints, dim, dist,
-                                                        base_edge->flags,
-                                                        stem_edge->flags );
+    fitted_width = af_latin_compute_stem_width( hints, dim,
+                                                dist, base_delta,
+                                                base_edge->flags,
+                                                stem_edge->flags );
 
 
     stem_edge->pos = base_edge->pos + fitted_width;
@@ -2878,7 +2912,8 @@
 
 
         org_len = edge2->opos - edge->opos;
-        cur_len = af_latin_compute_stem_width( hints, dim, org_len,
+        cur_len = af_latin_compute_stem_width( hints, dim,
+                                               org_len, 0,
                                                edge->flags,
                                                edge2->flags );
 
@@ -2947,7 +2982,8 @@
         org_len    = edge2->opos - edge->opos;
         org_center = org_pos + ( org_len >> 1 );
 
-        cur_len = af_latin_compute_stem_width( hints, dim, org_len,
+        cur_len = af_latin_compute_stem_width( hints, dim,
+                                               org_len, 0,
                                                edge->flags,
                                                edge2->flags );
 
@@ -3007,7 +3043,8 @@
           org_len    = edge2->opos - edge->opos;
           org_center = org_pos + ( org_len >> 1 );
 
-          cur_len    = af_latin_compute_stem_width( hints, dim, org_len,
+          cur_len    = af_latin_compute_stem_width( hints, dim,
+                                                    org_len, 0,
                                                     edge->flags,
                                                     edge2->flags );