Commit cc25e3ae12216380179506b9d5d9c25293966708

Werner Lemberg 2013-08-05T08:46:15

[autofit] Improve handling of `near' points. Points which are very near to each other are now marked as such. The `weak' flag is then computed by using the `in' vector of the first and the `out' vector of the last point of a group of near points. For example, this fixes the rendering of glyph `Oslash' in `Roboto-Thin.ttf'. * src/autofit/afhints.h (AF_Flags): New value `AF_FLAGS_NEAR'. * src/autofit/afhints.c (af_glyph_hints_reload): Introduce the heuristic value `near_limit' to decide whether the current point is near to the previous one, then set `AF_FLAG_NEAR' accordingly. Store good `in' vector (of last non-near point) in `last_good_in_{x,y}' and use it as an argument to `ft_corner_is_flat' if necessary.

diff --git a/ChangeLog b/ChangeLog
index d90f504..d9a95f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2013-08-05  Werner Lemberg  <wl@gnu.org>
+
+	[autofit] Improve handling of `near' points.
+
+	Points which are very near to each other are now marked as such.
+	The `weak' flag is then computed by using the `in' vector of the
+	first and the `out' vector of the last point of a group of near
+	points.
+
+	For example, this fixes the rendering of glyph `Oslash' in
+	`Roboto-Thin.ttf'.
+
+	* src/autofit/afhints.h (AF_Flags): New value `AF_FLAGS_NEAR'.
+
+	* src/autofit/afhints.c (af_glyph_hints_reload): Introduce
+	the heuristic value `near_limit' to decide whether the current point
+	is near to the previous one, then set `AF_FLAG_NEAR' accordingly.
+	Store good `in' vector (of last non-near point) in
+	`last_good_in_{x,y}' and use it as an argument to
+	`ft_corner_is_flat' if necessary.
+
 2013-08-02  Werner Lemberg  <wl@gnu.org>
 
 	* include/freetype/ftcffdrv.h: Improve documentation.
diff --git a/src/autofit/afhints.c b/src/autofit/afhints.c
index e8defaa..58d0735 100644
--- a/src/autofit/afhints.c
+++ b/src/autofit/afhints.c
@@ -740,6 +740,12 @@
         FT_Pos        in_y   = 0;
         AF_Direction  in_dir = AF_DIR_NONE;
 
+        FT_Pos  last_good_in_x = 0;
+        FT_Pos  last_good_in_y = 0;
+
+        FT_UInt  units_per_em = hints->metrics->scaler.face->units_per_EM;
+        FT_Int   near_limit   = 20 * units_per_em / 2048;
+
 
         for ( point = points; point < point_limit; point++ )
         {
@@ -749,15 +755,59 @@
 
           if ( point == first )
           {
-            prev   = first->prev;
-            in_x   = first->fx - prev->fx;
-            in_y   = first->fy - prev->fy;
+            prev = first->prev;
+
+            in_x = first->fx - prev->fx;
+            in_y = first->fy - prev->fy;
+
+            last_good_in_x = in_x;
+            last_good_in_y = in_y;
+
+            if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
+            {
+              /* search first non-near point to get a good `in_dir' value */
+
+              AF_Point  point_ = prev;
+
+
+              while ( point_ != first )
+              {
+                AF_Point  prev_  = point_->prev;
+
+                FT_Pos  in_x_ = point_->fx - prev_->fx;
+                FT_Pos  in_y_ = point_->fy - prev_->fy;
+
+
+                if ( FT_ABS( in_x_ ) + FT_ABS( in_y_) >= near_limit )
+                {
+                  last_good_in_x = in_x_;
+                  last_good_in_y = in_y_;
+
+                  break;
+                }
+
+                point_ = prev_;
+              }
+            }
+
             in_dir = af_direction_compute( in_x, in_y );
             first  = prev + 1;
           }
 
           point->in_dir = (FT_Char)in_dir;
 
+          /* check whether the current point is near to the previous one */
+          /* (value 20 in `near_limit' is heuristic; we use Taxicab      */
+          /* metrics for the test)                                       */
+
+          if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
+            point->flags |= AF_FLAG_NEAR;
+          else
+          {
+            last_good_in_x = in_x;
+            last_good_in_y = in_y;
+          }
+
           next  = point->next;
           out_x = next->fx - point->fx;
           out_y = next->fy - point->fy;
@@ -765,23 +815,43 @@
           in_dir         = af_direction_compute( out_x, out_y );
           point->out_dir = (FT_Char)in_dir;
 
-          /* check for weak points */
+          /* Check for weak points.  The remaining points not collected */
+          /* in edges are then implicitly classified as strong points.  */
 
           if ( point->flags & AF_FLAG_CONTROL )
           {
+            /* control points are always weak */
           Is_Weak_Point:
             point->flags |= AF_FLAG_WEAK_INTERPOLATION;
           }
           else if ( point->out_dir == point->in_dir )
           {
             if ( point->out_dir != AF_DIR_NONE )
+            {
+              /* current point lies on a horizontal or   */
+              /* vertical segment (but doesn't start it) */
               goto Is_Weak_Point;
+            }
 
-            if ( ft_corner_is_flat( in_x, in_y, out_x, out_y ) )
+            /* test whether `in' and `out' direction is approximately */
+            /* the same (and use the last good `in' vector in case    */
+            /* the current point is near to the previous one)         */
+            if ( ft_corner_is_flat(
+                   point->flags & AF_FLAG_NEAR ? last_good_in_x : in_x,
+                   point->flags & AF_FLAG_NEAR ? last_good_in_y : in_y,
+                   out_x,
+                   out_y ) )
+            {
+              /* current point lies on a straight, diagonal line */
+              /* (more or less)                                  */
               goto Is_Weak_Point;
+            }
           }
           else if ( point->in_dir == -point->out_dir )
+          {
+            /* current point forms a spike */
             goto Is_Weak_Point;
+          }
 
           in_x = out_x;
           in_y = out_y;
diff --git a/src/autofit/afhints.h b/src/autofit/afhints.h
index 789ad98..ce52325 100644
--- a/src/autofit/afhints.h
+++ b/src/autofit/afhints.h
@@ -236,7 +236,10 @@ FT_BEGIN_HEADER
     AF_FLAG_WEAK_INTERPOLATION = 1 << 8,
 
     /* all inflection points in the outline have this flag set */
-    AF_FLAG_INFLECTION = 1 << 9
+    AF_FLAG_INFLECTION = 1 << 9,
+
+    /* the current point is very near to another one */
+    AF_FLAG_NEAR = 1 << 10
 
   } AF_Flags;