shithub: freetype+ttf2subf

Download patch

ref: 42206ad86aac3c3c344fcd9730d80165514584c9
parent: 0409ef32686188c1947298e0fca9fbeadd23c1c3
author: Werner Lemberg <[email protected]>
date: Tue Jun 16 11:14:21 EDT 2009

Improve scan conversion rules 4 and 6.

Two new constraints are introduced to better identify a `stub' -- a
concept which is only vaguely described in the OpenType
specification.  The old code was too rigorous and suppressed more
pixel than it should.

  . The intersection of the two profiles with the scanline is less
    than a half pixel.  Code related to this was already present in
    the sources but has been commented out.

  . The endpoint of the original contour forming a profile has a
    distance (`overshoot') less than half a pixel to the scanline.

Note that the two additional conditions fix almost all differences
to the Windows rasterizer, but some problematic cases remain.

* src/raster/ftraster.c (Overshoot_Top, Overshoot_Bottom): New
macros for the `flags' field in the `TProfile' structure.
(IS_BOTTOM_OVERSHOOT, IS_TOP_OVERSHOOT): New macros.
(New_Profile, End_Profile): Pass overshoot flag as an argument and
set it accordingly.
Update callers.
(Vertical_Sweep_Drop, Horizontal_Sweep_Drop): Implement the two new
constraints.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2009-06-16  Werner Lemberg  <[email protected]>
+
+	Improve scan conversion rules 4 and 6.
+
+	Two new constraints are introduced to better identify a `stub' -- a
+	concept which is only vaguely described in the OpenType
+	specification.  The old code was too rigorous and suppressed more
+	pixel than it should.
+
+	  . The intersection of the two profiles with the scanline is less
+	    than a half pixel.  Code related to this was already present in
+	    the sources but has been commented out.
+
+	  . The endpoint of the original contour forming a profile has a
+	    distance (`overshoot') less than half a pixel to the scanline.
+
+	Note that the two additional conditions fix almost all differences
+	to the Windows rasterizer, but some problematic cases remain.
+
+	* src/raster/ftraster.c (Overshoot_Top, Overshoot_Bottom): New
+	macros for the `flags' field in the `TProfile' structure.
+	(IS_BOTTOM_OVERSHOOT, IS_TOP_OVERSHOOT): New macros.
+	(New_Profile, End_Profile): Pass overshoot flag as an argument and
+	set it accordingly.
+	Update callers.
+	(Vertical_Sweep_Drop, Horizontal_Sweep_Drop): Implement the two new
+	constraints.
+
 2009-06-11  Werner Lemberg  <[email protected]>
 
 	Increase precision for B/W rasterizer.
--- a/src/raster/ftraster.c
+++ b/src/raster/ftraster.c
@@ -305,7 +305,10 @@
   } TPoint;
 
 
-#define Flow_Up  0x1
+  /* values for the `flags' bit field */
+#define Flow_Up           0x1
+#define Overshoot_Top     0x2
+#define Overshoot_Bottom  0x4
 
 
   /* States of each line, arc, and profile */
@@ -324,18 +327,19 @@
 
   struct  TProfile_
   {
-    FT_F26Dot6  X;           /* current coordinate during sweep         */
-    PProfile    link;        /* link to next profile (various purposes) */
-    PLong       offset;      /* start of profile's data in render pool  */
-    unsigned    flags;       /* Bit 0: profile orientation: up/down     */
-    long        height;      /* profile's height in scanlines           */
-    long        start;       /* profile's starting scanline             */
+    FT_F26Dot6  X;           /* current coordinate during sweep          */
+    PProfile    link;        /* link to next profile (various purposes)  */
+    PLong       offset;      /* start of profile's data in render pool   */
+    unsigned    flags;       /* Bit 0: profile orientation (up/down)     */
+                             /* Bit 1, 2: profile overshoot (top/bottom) */
+    long        height;      /* profile's height in scanlines            */
+    long        start;       /* profile's starting scanline              */
 
-    unsigned    countL;      /* number of lines to step before this     */
-                             /* profile becomes drawable                */
+    unsigned    countL;      /* number of lines to step before this      */
+                             /* profile becomes drawable                 */
 
-    PProfile    next;        /* next profile in same contour, used      */
-                             /* during drop-out control                 */
+    PProfile    next;        /* next profile in same contour, used       */
+                             /* during drop-out control                  */
   };
 
   typedef PProfile   TProfileList;
@@ -410,6 +414,9 @@
 #define FRAC( x )     ( (x) & ( ras.precision - 1 ) )
 #define SCALED( x )   ( ( (x) << ras.scale_shift ) - ras.precision_half )
 
+#define IS_BOTTOM_OVERSHOOT( x )  ( CEILING( x ) - x >= ras.precision_half )
+#define IS_TOP_OVERSHOOT( x )     ( x - FLOOR( x ) >= ras.precision_half )
+
   /* Note that I have moved the location of some fields in the */
   /* structure to ensure that the most used variables are used */
   /* at the top.  Thus, their offset can be coded with less    */
@@ -617,14 +624,18 @@
   /*    Create a new profile in the render pool.                           */
   /*                                                                       */
   /* <Input>                                                               */
-  /*    aState :: The state/orientation of the new profile.                */
+  /*    aState    :: The state/orientation of the new profile.             */
   /*                                                                       */
+  /*    overshoot :: Whether the profile's unrounded start position        */
+  /*                 differs by at least a half pixel.                     */
+  /*                                                                       */
   /* <Return>                                                              */
   /*   SUCCESS on success.  FAILURE in case of overflow or of incoherent   */
   /*   profile.                                                            */
   /*                                                                       */
   static Bool
-  New_Profile( RAS_ARGS TStates  aState )
+  New_Profile( RAS_ARGS TStates  aState,
+                        Bool     overshoot )
   {
     if ( !ras.fProfile )
     {
@@ -639,15 +650,26 @@
       return FAILURE;
     }
 
+    ras.cProfile->flags  = 0;
+    ras.cProfile->start  = 0;
+    ras.cProfile->height = 0;
+    ras.cProfile->offset = ras.top;
+    ras.cProfile->link   = (PProfile)0;
+    ras.cProfile->next   = (PProfile)0;
+
     switch ( aState )
     {
     case Ascending_State:
       ras.cProfile->flags |= Flow_Up;
+      if ( overshoot )
+        ras.cProfile->flags |= Overshoot_Bottom;
+
       FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));
       break;
 
     case Descending_State:
-      ras.cProfile->flags &= ~Flow_Up;
+      if ( overshoot )
+        ras.cProfile->flags |= Overshoot_Top;
       FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));
       break;
 
@@ -657,12 +679,6 @@
       return FAILURE;
     }
 
-    ras.cProfile->start  = 0;
-    ras.cProfile->height = 0;
-    ras.cProfile->offset = ras.top;
-    ras.cProfile->link   = (PProfile)0;
-    ras.cProfile->next   = (PProfile)0;
-
     if ( !ras.gProfile )
       ras.gProfile = ras.cProfile;
 
@@ -682,11 +698,15 @@
   /* <Description>                                                         */
   /*    Finalize the current profile.                                      */
   /*                                                                       */
+  /* <Input>                                                               */
+  /*    overshoot :: Whether the profile's unrounded end position differs  */
+  /*                 by at least a half pixel.                             */
+  /*                                                                       */
   /* <Return>                                                              */
   /*    SUCCESS on success.  FAILURE in case of overflow or incoherency.   */
   /*                                                                       */
   static Bool
-  End_Profile( RAS_ARG )
+  End_Profile( RAS_ARGS Bool  overshoot )
   {
     Long      h;
     PProfile  oldProfile;
@@ -706,15 +726,24 @@
       FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
                   (long)ras.cProfile, ras.cProfile->start, h ));
 
-      oldProfile           = ras.cProfile;
       ras.cProfile->height = h;
-      ras.cProfile         = (PProfile)ras.top;
+      if ( overshoot )
+      {
+        if ( ras.cProfile->flags & Flow_Up )
+          ras.cProfile->flags |= Overshoot_Top;
+        else
+          ras.cProfile->flags |= Overshoot_Bottom;
+      }
 
-      ras.top             += AlignProfileSize;
+      oldProfile   = ras.cProfile;
+      ras.cProfile = (PProfile)ras.top;
 
+      ras.top += AlignProfileSize;
+
       ras.cProfile->height = 0;
       ras.cProfile->offset = ras.top;
-      oldProfile->next     = ras.cProfile;
+
+      oldProfile->next = ras.cProfile;
       ras.num_Profs++;
     }
 
@@ -1328,13 +1357,15 @@
     case Unknown_State:
       if ( y > ras.lastY )
       {
-        if ( New_Profile( RAS_VARS Ascending_State ) )
+        if ( New_Profile( RAS_VARS Ascending_State,
+                                   IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
           return FAILURE;
       }
       else
       {
         if ( y < ras.lastY )
-          if ( New_Profile( RAS_VARS Descending_State ) )
+          if ( New_Profile( RAS_VARS Descending_State,
+                                     IS_TOP_OVERSHOOT( ras.lastY ) ) )
             return FAILURE;
       }
       break;
@@ -1342,8 +1373,9 @@
     case Ascending_State:
       if ( y < ras.lastY )
       {
-        if ( End_Profile( RAS_VAR )                   ||
-             New_Profile( RAS_VARS Descending_State ) )
+        if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
+             New_Profile( RAS_VARS Descending_State,
+                                   IS_TOP_OVERSHOOT( ras.lastY ) ) )
           return FAILURE;
       }
       break;
@@ -1351,8 +1383,9 @@
     case Descending_State:
       if ( y > ras.lastY )
       {
-        if ( End_Profile( RAS_VAR )                  ||
-             New_Profile( RAS_VARS Ascending_State ) )
+        if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
+             New_Profile( RAS_VARS Ascending_State,
+                                   IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
           return FAILURE;
       }
       break;
@@ -1367,13 +1400,13 @@
     {
     case Ascending_State:
       if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
-                    x, y, ras.minY, ras.maxY ) )
+                             x, y, ras.minY, ras.maxY ) )
         return FAILURE;
       break;
 
     case Descending_State:
       if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
-                      x, y, ras.minY, ras.maxY ) )
+                               x, y, ras.minY, ras.maxY ) )
         return FAILURE;
       break;
 
@@ -1467,13 +1500,17 @@
         state_bez = y1 < y3 ? Ascending_State : Descending_State;
         if ( ras.state != state_bez )
         {
+          Bool  o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
+                                                 : IS_TOP_OVERSHOOT( y1 );
+
+
           /* finalize current profile if any */
           if ( ras.state != Unknown_State &&
-               End_Profile( RAS_VAR )     )
+               End_Profile( RAS_VARS o )  )
             goto Fail;
 
           /* create a new profile */
-          if ( New_Profile( RAS_VARS state_bez ) )
+          if ( New_Profile( RAS_VARS state_bez, o ) )
             goto Fail;
         }
 
@@ -1599,11 +1636,16 @@
         /* detect a change of direction */
         if ( ras.state != state_bez )
         {
+          Bool  o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
+                                                 : IS_TOP_OVERSHOOT( y1 );
+
+
+          /* finalize current profile if any */
           if ( ras.state != Unknown_State &&
-               End_Profile( RAS_VAR )     )
+               End_Profile( RAS_VARS o )  )
             goto Fail;
 
-          if ( New_Profile( RAS_VARS state_bez ) )
+          if ( New_Profile( RAS_VARS state_bez, o ) )
             goto Fail;
         }
 
@@ -1903,12 +1945,15 @@
 
     for ( i = 0; i < ras.outline.n_contours; i++ )
     {
+      Bool  o;
+
+
       ras.state    = Unknown_State;
       ras.gProfile = NULL;
 
       if ( Decompose_Curve( RAS_VARS (unsigned short)start,
-                            ras.outline.contours[i],
-                            flipped ) )
+                                     ras.outline.contours[i],
+                                     flipped ) )
         return FAILURE;
 
       start = ras.outline.contours[i] + 1;
@@ -1917,15 +1962,19 @@
       if ( FRAC( ras.lastY ) == 0 &&
            ras.lastY >= ras.minY  &&
            ras.lastY <= ras.maxY  )
-        if ( ras.gProfile                           &&
-               ( ras.gProfile->flags & Flow_Up ) ==
-               ( ras.cProfile->flags & Flow_Up )    )
+        if ( ras.gProfile                        &&
+             ( ras.gProfile->flags & Flow_Up ) ==
+               ( ras.cProfile->flags & Flow_Up ) )
           ras.top--;
         /* Note that ras.gProfile can be nil if the contour was too small */
         /* to be drawn.                                                   */
 
       lastProfile = ras.cProfile;
-      if ( End_Profile( RAS_VAR ) )
+      if ( ras.cProfile->flags & Flow_Up )
+        o = IS_TOP_OVERSHOOT( ras.lastY );
+      else
+        o = IS_BOTTOM_OVERSHOOT( ras.lastY );
+      if ( End_Profile( RAS_VARS o ) )
         return FAILURE;
 
       /* close the `next profile in contour' linked list */
@@ -2237,11 +2286,10 @@
 
           /* Drop-out Control Rules #4 and #6 */
 
-          /* The spec is not very clear regarding those rules.  It  */
-          /* presents a method that is way too costly to implement  */
-          /* while the general idea seems to get rid of `stubs'.    */
+          /* The specification neither provides an exact definition */
+          /* of a `stub' nor gives exact rules to exclude them.     */
           /*                                                        */
-          /* Here, we only get rid of stubs recognized if:          */
+          /* Here the constraints we use to recognize a stub.       */
           /*                                                        */
           /*  upper stub:                                           */
           /*                                                        */
@@ -2255,22 +2303,26 @@
           /*   - P_Left is the successor of P_Right in that contour */
           /*   - y is the bottom of P_Left                          */
           /*                                                        */
+          /* We draw a stub if the following constraints are met.   */
+          /*                                                        */
+          /*   - for an upper or lower stub, there is top or bottom */
+          /*     overshoot, respectively                            */
+          /*   - the covered interval is greater or equal to a half */
+          /*     pixel                                              */
 
-          /* FIXXXME: uncommenting this line solves the disappearing */
-          /*          bit problem in the `7' of verdana 10pts, but   */
-          /*          makes a new one in the `C' of arial 14pts      */
-#if 0
-          if ( x2 - x1 < ras.precision_half )
-#endif
-          {
-            /* upper stub test */
-            if ( left->next == right && left->height <= 0 )
-              return;
+          /* upper stub test */
+          if ( left->next == right                &&
+               left->height <= 0                  &&
+               !( left->flags & Overshoot_Top   &&
+                  x2 - x1 >= ras.precision_half ) )
+            return;
 
-            /* lower stub test */
-            if ( right->next == left && left->start == y )
-              return;
-          }
+          /* lower stub test */
+          if ( right->next == left                 &&
+               left->start == y                    &&
+               !( left->flags & Overshoot_Bottom &&
+                  x2 - x1 >= ras.precision_half  ) )
+            return;
 
           if ( ras.dropOutControl == 1 )
             pxl = e2;
@@ -2432,11 +2484,17 @@
           /* see Vertical_Sweep_Drop for details */
 
           /* rightmost stub test */
-          if ( left->next == right && left->height <= 0 )
+          if ( left->next == right                &&
+               left->height <= 0                  &&
+               !( left->flags & Overshoot_Top   &&
+                  x2 - x1 >= ras.precision_half ) )
             return;
 
           /* leftmost stub test */
-          if ( right->next == left && left->start == y )
+          if ( right->next == left                 &&
+               left->start == y                    &&
+               !( left->flags & Overshoot_Bottom &&
+                  x2 - x1 >= ras.precision_half  ) )
             return;
 
           if ( ras.dropOutControl == 1 )
@@ -3064,7 +3122,7 @@
 
 
     Set_High_Precision( RAS_VARS ras.outline.flags &
-                        FT_OUTLINE_HIGH_PRECISION );
+                                 FT_OUTLINE_HIGH_PRECISION );
     ras.scale_shift = ras.precision_shift;
 
     if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
@@ -3140,7 +3198,7 @@
 
 
     Set_High_Precision( RAS_VARS ras.outline.flags &
-                        FT_OUTLINE_HIGH_PRECISION );
+                                 FT_OUTLINE_HIGH_PRECISION );
     ras.scale_shift = ras.precision_shift + 1;
 
     if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )