Commit 15e2a4f790d2c1ef53bc96bd4415eeda50e03e43

Werner Lemberg 2015-08-05T21:53:50

[autofit] Improve recognition of flat vs. rounded segments. Lower the flatness threshold from upem/8 to upem/14, making the auto-hinter accept shorter elements. Synchronize flat/round stem selection algorithm with blue zone code. * src/autofit/aflatin.c (FLAT_THRESHOLD): New macro. (af_latin_metrics_init_blues): Use it. (af_latin_hints_compute_segments): Collect information on maximum and minimum coordinates of `on' points; use this to add a constraint for the flat/round decision similar to `af_latin_metrics_init_blues'.

diff --git a/ChangeLog b/ChangeLog
index c8ddab7..f30791f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2015-08-05  Werner Lemberg  <wl@gnu.org>
+
+	[autofit] Improve recognition of flat vs. rounded segments.
+
+	Lower the flatness threshold from upem/8 to upem/14, making the
+	auto-hinter accept shorter elements.
+
+	Synchronize flat/round stem selection algorithm with blue zone code.
+
+	* src/autofit/aflatin.c (FLAT_THRESHOLD): New macro.
+	(af_latin_metrics_init_blues): Use it.
+	(af_latin_hints_compute_segments): Collect information on maximum
+	and minimum coordinates of `on' points; use this to add a constraint
+	for the flat/round decision similar to
+	`af_latin_metrics_init_blues'.
+
 2015-08-04  Werner Lemberg  <wl@gnu.org>
 
 	Another left-shift bug (#45681).
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index 893e986..3065895 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -41,6 +41,10 @@
 #define FT_COMPONENT  trace_aflatin
 
 
+  /* needed for computation of round vs. flat segments */
+#define FLAT_THRESHOLD( x )  ( x / 14 )
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -274,6 +278,8 @@
     AF_Blue_Stringset         bss = sc->blue_stringset;
     const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
 
+    FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
+
 
     /* we walk over the blue character strings as specified in the */
     /* style's entry in the `af_blue_stringset' array              */
@@ -693,16 +699,16 @@
           /* now set the `round' flag depending on the segment's kind: */
           /*                                                           */
           /* - if the horizontal distance between the first and last   */
-          /*   `on' point is larger than upem/8 (value 8 is heuristic) */
+          /*   `on' point is larger than a heuristic threshold         */
           /*   we have a flat segment                                  */
           /* - if either the first or the last point of the segment is */
           /*   an `off' point, the segment is round, otherwise it is   */
           /*   flat                                                    */
           if ( best_on_point_first >= 0                               &&
                best_on_point_last >= 0                                &&
-               (FT_UInt)( FT_ABS( points[best_on_point_last].x -
-                                  points[best_on_point_first].x ) ) >
-                 metrics->units_per_em / 8                            )
+               ( FT_ABS( points[best_on_point_last].x -
+                         points[best_on_point_first].x ) ) >
+                 flat_threshold                                       )
             round = 0;
           else
             round = FT_BOOL(
@@ -1155,14 +1161,17 @@
   af_latin_hints_compute_segments( AF_GlyphHints  hints,
                                    AF_Dimension   dim )
   {
-    AF_AxisHints   axis          = &hints->axis[dim];
-    FT_Memory      memory        = hints->memory;
-    FT_Error       error         = FT_Err_Ok;
-    AF_Segment     segment       = NULL;
-    AF_SegmentRec  seg0;
-    AF_Point*      contour       = hints->contours;
-    AF_Point*      contour_limit = contour + hints->num_contours;
-    AF_Direction   major_dir, segment_dir;
+    AF_LatinMetrics  metrics       = (AF_LatinMetrics)hints->metrics;
+    AF_AxisHints     axis          = &hints->axis[dim];
+    FT_Memory        memory        = hints->memory;
+    FT_Error         error         = FT_Err_Ok;
+    AF_Segment       segment       = NULL;
+    AF_SegmentRec    seg0;
+    AF_Point*        contour       = hints->contours;
+    AF_Point*        contour_limit = contour + hints->num_contours;
+    AF_Direction     major_dir, segment_dir;
+
+    FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
 
 
     FT_ZERO( &seg0 );
@@ -1203,11 +1212,13 @@
     /* do each contour separately */
     for ( ; contour < contour_limit; contour++ )
     {
-      AF_Point  point   =  contour[0];
-      AF_Point  last    =  point->prev;
-      int       on_edge =  0;
-      FT_Pos    min_pos =  32000;  /* minimum segment pos != min_coord */
-      FT_Pos    max_pos = -32000;  /* maximum segment pos != max_coord */
+      AF_Point  point      =  contour[0];
+      AF_Point  last       =  point->prev;
+      int       on_edge    =  0;
+      FT_Pos    min_pos    =  32000;  /* minimum segment pos != min_coord */
+      FT_Pos    max_pos    = -32000;  /* maximum segment pos != max_coord */
+      FT_Pos    min_on_pos =  32000;
+      FT_Pos    max_on_pos = -32000;
       FT_Bool   passed;
 
 
@@ -1249,6 +1260,16 @@
           if ( u > max_pos )
             max_pos = u;
 
+          /* get minimum and maximum coordinate of on points */
+          if ( !( point->flags & AF_FLAG_CONTROL ) )
+          {
+            v = point->v;
+            if ( v < min_on_pos )
+              min_on_pos = v;
+            if ( v > max_on_pos )
+              max_on_pos = v;
+          }
+
           if ( point->out_dir != segment_dir || point == last )
           {
             /* we are just leaving an edge; record a new segment! */
@@ -1256,9 +1277,10 @@
             segment->pos  = (FT_Short)( ( min_pos + max_pos ) >> 1 );
 
             /* a segment is round if either its first or last point */
-            /* is a control point                                   */
-            if ( ( segment->first->flags | point->flags ) &
-                 AF_FLAG_CONTROL                          )
+            /* is a control point, and the length of the on points  */
+            /* inbetween doesn't exceed a heuristic limit           */
+            if ( ( segment->first->flags | point->flags ) & AF_FLAG_CONTROL &&
+                 ( max_on_pos - min_on_pos ) < flat_threshold               )
               segment->flags |= AF_EDGE_ROUND;
 
             /* compute segment size */
@@ -1301,10 +1323,19 @@
           /* clear all segment fields */
           segment[0] = seg0;
 
-          segment->dir      = (FT_Char)segment_dir;
+          segment->dir   = (FT_Char)segment_dir;
+          segment->first = point;
+          segment->last  = point;
+
           min_pos = max_pos = point->u;
-          segment->first    = point;
-          segment->last     = point;
+
+          if ( point->flags & AF_FLAG_CONTROL )
+          {
+            min_on_pos =  32000;
+            max_on_pos = -32000;
+          }
+          else
+            min_on_pos = max_on_pos = point->v;
 
           on_edge = 1;
         }