Commit 63bef9a588c3410007ead16e4121830cc1b1b2e2

Werner Lemberg 2014-05-01T07:16:05

[autofit] Fix handling of neutral blue zones in stems. * src/autofit/afhints.h (AF_Edge_Flags): New value `AF_EDGE_NEUTRAL'. * src/autofit/aflatin.c (af_latin_hints_compute_blue_edges): Trace neutral blue zones with AF_EDGE_NEUTRAL. (af_latin_hint_edges): Skip neutral blue zones if necessary.

diff --git a/ChangeLog b/ChangeLog
index ddc68ce..11dc4c2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2014-05-01  Werner Lemberg  <wl@gnu.org>
+
+	[autofit] Fix handling of neutral blue zones in stems.
+
+	* src/autofit/afhints.h (AF_Edge_Flags): New value
+	`AF_EDGE_NEUTRAL'.
+
+	* src/autofit/aflatin.c (af_latin_hints_compute_blue_edges): Trace
+	neutral blue zones with AF_EDGE_NEUTRAL.
+	(af_latin_hint_edges): Skip neutral blue zones if necessary.
+
 2014-04-28  Werner Lemberg  <wl@gnu.org>
 
 	[autofit] Introduce neutral blue zones to the latin module.
diff --git a/src/autofit/afhints.h b/src/autofit/afhints.h
index 6e1b1ff..c0ebd0d 100644
--- a/src/autofit/afhints.h
+++ b/src/autofit/afhints.h
@@ -244,10 +244,11 @@ FT_BEGIN_HEADER
   /* edge hint flags */
   typedef enum  AF_Edge_Flags_
   {
-    AF_EDGE_NORMAL = 0,
-    AF_EDGE_ROUND  = 1 << 0,
-    AF_EDGE_SERIF  = 1 << 1,
-    AF_EDGE_DONE   = 1 << 2
+    AF_EDGE_NORMAL  = 0,
+    AF_EDGE_ROUND   = 1 << 0,
+    AF_EDGE_SERIF   = 1 << 1,
+    AF_EDGE_DONE    = 1 << 2,
+    AF_EDGE_NEUTRAL = 1 << 3  /* set if edge aligns to a neutral blue zone */
 
   } AF_Edge_Flags;
 
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index 825c361..a0d9ebc 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -1841,8 +1841,9 @@
     for ( ; edge < edge_limit; edge++ )
     {
       FT_UInt   bb;
-      AF_Width  best_blue = NULL;
-      FT_Pos    best_dist;  /* initial threshold */
+      AF_Width  best_blue            = NULL;
+      FT_Bool   best_blue_is_neutral = 0;
+      FT_Pos    best_dist;                 /* initial threshold */
 
 
       /* compute the initial threshold as a fraction of the EM size */
@@ -1856,7 +1857,7 @@
       for ( bb = 0; bb < latin->blue_count; bb++ )
       {
         AF_LatinBlue  blue = latin->blues + bb;
-        FT_Bool       is_top_blue, is_major_dir;
+        FT_Bool       is_top_blue, is_neutral_blue, is_major_dir;
 
 
         /* skip inactive blue zones (i.e., those that are too large) */
@@ -1867,12 +1868,15 @@
         /* direction); if it is a bottom zone, check for left edges (in  */
         /* the major direction) -- this assumes the TrueType convention  */
         /* for the orientation of contours                               */
-        is_top_blue  = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
-        is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
+        is_top_blue =
+          (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
+        is_neutral_blue =
+          (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0);
+        is_major_dir =
+          FT_BOOL( edge->dir == axis->major_dir );
 
         /* neutral blue zones are handled for both directions */
-        if ( is_top_blue ^ is_major_dir          ||
-             blue->flags & AF_LATIN_BLUE_NEUTRAL )
+        if ( is_top_blue ^ is_major_dir || is_neutral_blue )
         {
           FT_Pos  dist;
 
@@ -1885,8 +1889,9 @@
           dist = FT_MulFix( dist, scale );
           if ( dist < best_dist )
           {
-            best_dist = dist;
-            best_blue = &blue->ref;
+            best_dist            = dist;
+            best_blue            = &blue->ref;
+            best_blue_is_neutral = is_neutral_blue;
           }
 
           /* now compare it to the overshoot position and check whether */
@@ -1894,9 +1899,9 @@
           /* reference position of a top zone, or under the reference   */
           /* position of a bottom zone (provided we don't have a        */
           /* neutral blue zone)                                         */
-          if ( edge->flags & AF_EDGE_ROUND              &&
-               dist != 0                                &&
-               !( blue->flags & AF_LATIN_BLUE_NEUTRAL ) )
+          if ( edge->flags & AF_EDGE_ROUND &&
+               dist != 0                   &&
+               !is_neutral_blue            )
           {
             FT_Bool  is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
 
@@ -1910,8 +1915,9 @@
               dist = FT_MulFix( dist, scale );
               if ( dist < best_dist )
               {
-                best_dist = dist;
-                best_blue = &blue->shoot;
+                best_dist            = dist;
+                best_blue            = &blue->shoot;
+                best_blue_is_neutral = is_neutral_blue;
               }
             }
           }
@@ -1919,7 +1925,11 @@
       }
 
       if ( best_blue )
+      {
         edge->blue_edge = best_blue;
+        if ( best_blue_is_neutral )
+          edge->flags |= AF_EDGE_NEUTRAL;
+      }
     }
   }
 
@@ -2304,14 +2314,41 @@
         if ( edge->flags & AF_EDGE_DONE )
           continue;
 
-        blue  = edge->blue_edge;
         edge1 = NULL;
         edge2 = edge->link;
 
+        /*
+         *  If a stem contains both a neutral and a non-neutral blue zone,
+         *  skip the neutral one.  Otherwise, outlines with different
+         *  directions might be incorrectly aligned at the same vertical
+         *  position.
+         *
+         *  If we have two neutral blue zones, skip one of them.
+         *
+         */
+        if ( edge->blue_edge && edge2 && edge2->blue_edge )
+        {
+          FT_Byte  neutral  = edge->flags  & AF_EDGE_NEUTRAL;
+          FT_Byte  neutral2 = edge2->flags & AF_EDGE_NEUTRAL;
+
+
+          if ( ( neutral && neutral2 ) || neutral2 )
+          {
+            edge2->blue_edge = NULL;
+            edge2->flags    &= ~AF_EDGE_NEUTRAL;
+          }
+          else if ( neutral )
+          {
+            edge->blue_edge = NULL;
+            edge->flags    &= ~AF_EDGE_NEUTRAL;
+          }
+        }
+
+        blue = edge->blue_edge;
         if ( blue )
           edge1 = edge;
 
-        /* flip edges if the other stem is aligned to a blue zone */
+        /* flip edges if the other edge is aligned to a blue zone */
         else if ( edge2 && edge2->blue_edge )
         {
           blue  = edge2->blue_edge;
@@ -2378,7 +2415,7 @@
       /* this should not happen, but it's better to be safe */
       if ( edge2->blue_edge )
       {
-        FT_TRACE5(( "  ASSERTION FAILED for edge %d\n", edge2-edges ));
+        FT_TRACE5(( "  ASSERTION FAILED for edge %d\n", edge2 - edges ));
 
         af_latin_align_linked_edge( hints, dim, edge2, edge );
         edge->flags |= AF_EDGE_DONE;