Commit 2c4500e981aa4fae26af045e259d34e55a3aa7e5

David Turner 2007-02-14T15:08:47

improving TrueType bytecode interpreter to better match Windows behaviour on tricky cases... FIX_BYTECODE is now the default to allow large testing

diff --git a/ChangeLog b/ChangeLog
index e6d58d9..83d9eb6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-02-14  David Turner  <david@freetype.org>
+
+	* src/truetype/ttinterp.c: improved the FIX_BYTECODE code which is now
+	the default, it seems to get rid of most known problems with my fonts,
+	but more testing is needed though.
+
 2007-02-12  Werner Lemberg  <wl@gnu.org>
 
 	* src/truetype/ttinterp.c (Project_x, Project_y): Remove compiler
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
index 1e89923..ace0290 100644
--- a/src/truetype/ttinterp.c
+++ b/src/truetype/ttinterp.c
@@ -18,7 +18,7 @@
 
   /* define FIX_BYTECODE to implement the bytecode interpreter fixes */
   /* needed to match Windows behaviour more accurately               */
-/* #define  FIX_BYTECODE */
+#define  FIX_BYTECODE
 
 
 #include <ft2build.h>
@@ -1143,6 +1143,35 @@
 #define NULL_Vector  (FT_Vector*)&Null_Vector
 
 
+#if 1
+  static FT_Int32
+  TT_MulFix14( FT_Int32  a,
+               FT_Int    b )
+{
+  FT_Int32   sign;
+  FT_UInt32  ah, al, mid, lo, hi;
+
+  sign = a^b;
+
+  if (a < 0) a = -a;
+  if (b < 0) b = -b;
+
+  ah = (FT_UInt32)((a >> 16) & 0xFFFFU);
+  al = (FT_UInt32)( a & 0xFFFFU );
+
+  lo    = al*b;
+  mid   = ah*b;
+  hi    = (mid >> 16);
+  mid   = (mid << 16) + (1 << 13); /* rounding */
+  lo   += mid;
+  if (lo < mid)
+    hi += 1;
+
+  mid = (lo >> 14) | (hi << 18);
+
+  return  sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
+}
+#else
   /* compute (a*b)/2^14 with maximal accuracy and rounding */
   static FT_Int32
   TT_MulFix14( FT_Int32  a,
@@ -1170,6 +1199,7 @@
 
     return ( hi << 18 ) | ( l >> 14 );
   }
+#endif
 
 
   /* compute (ax*bx+ay*by)/2^14 with maximal accuracy and rounding */
@@ -4808,15 +4838,10 @@
                          CUR.twilight.n_points );
 
         /* get scaled orus coordinates */
-        vec1 = CUR.zp0.orus[L];
-        vec2 = CUR.zp1.orus[K];
-
-        vec1.x = TT_MULFIX( vec1.x, CUR.metrics.x_scale );
-        vec1.y = TT_MULFIX( vec1.y, CUR.metrics.y_scale );
-        vec2.x = TT_MULFIX( vec2.x, CUR.metrics.x_scale );
-        vec2.y = TT_MULFIX( vec2.y, CUR.metrics.y_scale );
+        vec1.x = TT_MULFIX( CUR.zp0.orus[L].x - CUR.zp1.orus[K].x, CUR.metrics.x_scale );
+        vec1.y = TT_MULFIX( CUR.zp0.orus[L].y - CUR.zp1.orus[L].y, CUR.metrics.y_scale );
 
-        D = CUR_Func_dualproj( &vec1, &vec2 );
+        D = CUR_fast_dualproj( &vec1 );
 
 #else
 
@@ -5748,7 +5773,8 @@
 #ifdef FIX_BYTECODE
 
     {
-      FT_Vector  vec1, vec2;
+      FT_Vector*  vec1 = &CUR.zp1.orus[point];
+      FT_Vector*  vec2 = &CUR.zp0.orus[CUR.GS.rp0];
 
 
       if ( CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 )
@@ -5756,16 +5782,21 @@
                        CUR.twilight.org,
                        CUR.twilight.n_points );
 
-      vec1 = CUR.zp1.orus[point];
-      vec2 = CUR.zp0.orus[CUR.GS.rp0];
-
-      vec1.x = TT_MULFIX( vec1.x, CUR.metrics.x_scale );
-      vec1.y = TT_MULFIX( vec1.y, CUR.metrics.y_scale );
+      if ( CUR.metrics.x_scale == CUR.metrics.y_scale ) 
+      {
+        /* this should be faster */
+        org_dist = CUR_Func_dualproj( vec1, vec2 );
+        org_dist = TT_MULFIX( org_dist, CUR.metrics.x_scale );
+      }
+      else 
+      {
+        FT_Vector  vec;
 
-      vec2.x = TT_MULFIX( vec2.x, CUR.metrics.x_scale );
-      vec2.y = TT_MULFIX( vec2.y, CUR.metrics.y_scale );
+        vec.x = TT_MULFIX( vec1->x - vec2->x, CUR.metrics.x_scale );
+        vec.y = TT_MULFIX( vec1->y - vec2->y, CUR.metrics.y_scale );
 
-      org_dist = CUR_Func_dualproj( &vec1, &vec2 );
+        org_dist = CUR_fast_dualproj( &vec );
+      }
     }
 
 #else
@@ -5886,14 +5917,11 @@
       CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y +
                              TT_MulFix14( cvt_dist, CUR.GS.freeVector.y );
 
-      CUR.zp1.cur[point] = CUR.zp1.org[point];
+      CUR.zp1.cur[point] = CUR.zp0.cur[point];
     }
 
-    org_dist = CUR_Func_dualproj( CUR.zp1.org + point,
-                                  CUR.zp0.org + CUR.GS.rp0 );
-
-    cur_dist = CUR_Func_project( CUR.zp1.cur + point,
-                                 CUR.zp0.cur + CUR.GS.rp0 );
+    org_dist = CUR_Func_dualproj( &CUR.zp1.org[point], &CUR.zp0.org[CUR.GS.rp0] );
+    cur_dist = CUR_Func_project ( &CUR.zp1.cur[point], &CUR.zp0.cur[CUR.GS.rp0] );
 
     /* auto-flip test */
 
@@ -5947,7 +5975,6 @@
       CUR.GS.rp0 = point;
 
     /* XXX: UNDOCUMENTED! */
-
     CUR.GS.rp2 = point;
   }
 
@@ -6125,13 +6152,15 @@
   /* Opcode range: 0x39                                                    */
   /* Stack:        uint32... -->                                           */
   /*                                                                       */
+
+  /* SOMETIMES, DUMBER CODE IS BETTER CODE */
+#ifdef FIX_BYTECODE
   static void
   Ins_IP( INS_ARG )
   {
-    FT_F26Dot6  org_a, org_b, org_x,
-                cur_a, cur_b, cur_x,
-                distance = 0;
-    FT_UShort   point;
+    FT_F26Dot6  old_range, cur_range;
+    FT_Vector*  orus_base;
+    FT_Vector*  cur_base;
 
     FT_UNUSED_ARG;
 
@@ -6142,61 +6171,100 @@
       return;
     }
 
-#ifdef FIX_BYTECODE
-
-    /* We need to deal in a special way with the twilight zone.  The easiest
-     * solution is simply to copy the coordinates from `org' to `orus'
-     * whenever someone tries to perform intersections based on some of its
-     * points.
-     *
-     * Otherwise, by definition, value of CUR.twilight[n] is (0,0),
-     * whatever value of `n'.
-     */
+   /* We need to deal in a special way with the twilight zone.  The easiest
+    * solution is simply to copy the coordinates from `org' to `orus'
+    * whenever someone tries to perform intersections based on some of its
+    * points.
+    *
+    * Otherwise, by definition, value of CUR.twilight.orus[n] is (0,0),
+    * whatever value of `n'.
+    */
     if ( CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 || CUR.GS.gep2 == 0 )
     {
-      FT_ARRAY_COPY( CUR.twilight.orus,
-                     CUR.twilight.org,
-                     CUR.twilight.n_points );
+        FT_ARRAY_COPY( CUR.twilight.orus,
+                       CUR.twilight.org,
+                       CUR.twilight.n_points );
     }
 
-#endif /* FIX_BYTECODE */
+    orus_base = &CUR.zp0.orus[CUR.GS.rp1];
+    cur_base  = &CUR.zp0.cur[CUR.GS.rp1];
 
     /* XXX: There are some glyphs in some braindead but popular  */
     /*      fonts out there (e.g. [aeu]grave in monotype.ttf)    */
     /*      calling IP[] with bad values of rp[12].              */
     /*      Do something sane when this odd thing happens.       */
-
     if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) ||
          BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) )
     {
-      org_a = cur_a = 0;
-      org_b = cur_b = 0;
+      old_range = 0;
+      cur_range = 0;
     }
     else
     {
+      old_range = CUR_Func_dualproj( &CUR.zp1.orus[CUR.GS.rp2], orus_base );
+      cur_range = CUR_Func_project ( &CUR.zp1.cur[CUR.GS.rp2],  cur_base );
+    }
+
+    for ( ; CUR.GS.loop > 0; --CUR.GS.loop )
+    {
+      FT_UInt     point = (FT_UInt) CUR.stack[--CUR.args];
+      FT_F26Dot6  org_dist, cur_dist, new_dist;
 
-#ifdef FIX_BYTECODE
+      /* check point bounds */
+      if ( BOUNDS( point, CUR.zp2.n_points ) )
+      {
+        if ( CUR.pedantic_hinting )
+        {
+          CUR.error = TT_Err_Invalid_Reference;
+          return;
+        }
+        continue;
+      }
 
-      FT_Vector  vec1, vec2;
+      org_dist = CUR_Func_dualproj( &CUR.zp2.orus[point], orus_base );
+      cur_dist = CUR_Func_project ( &CUR.zp2.cur[point], cur_base );
+      new_dist = (old_range != 0) ? TT_MULDIV( org_dist, cur_range, old_range )
+                                  : cur_dist;
 
+      CUR_Func_move( &CUR.zp2, point, new_dist - cur_dist );
+    }
+    CUR.GS.loop = 1;
+    CUR.new_top = CUR.args;
+  }
+#else /* OLD CODE */
+  static void
+  Ins_IP( INS_ARG )
+  {
+    FT_F26Dot6  org_a, org_b, org_x,
+                cur_a, cur_b, cur_x,
+                distance = 0;
+    FT_UShort   point;
 
-      vec1   = CUR.zp0.orus[CUR.GS.rp1];
-      vec2   = CUR.zp1.orus[CUR.GS.rp2];
-      vec1.x = TT_MULFIX( vec1.x, CUR.metrics.x_scale );
-      vec1.y = TT_MULFIX( vec1.y, CUR.metrics.y_scale );
-      vec2.x = TT_MULFIX( vec2.x, CUR.metrics.x_scale );
-      vec2.y = TT_MULFIX( vec2.y, CUR.metrics.y_scale );
+    FT_UNUSED_ARG;
 
-      org_a = CUR_fast_dualproj( &vec1 );
-      org_b = CUR_fast_dualproj( &vec2 );
 
-#else
+    if ( CUR.top < CUR.GS.loop )
+    {
+      CUR.error = TT_Err_Invalid_Reference;
+      return;
+    }
+
+    /* XXX: There are some glyphs in some braindead but popular  */
+    /*      fonts out there (e.g. [aeu]grave in monotype.ttf)    */
+    /*      calling IP[] with bad values of rp[12].              */
+    /*      Do something sane when this odd thing happens.       */
 
+    if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) ||
+         BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) )
+    {
+      org_a = cur_a = 0;
+      org_b = cur_b = 0;
+    }
+    else
+    {
       org_a = CUR_fast_dualproj( &CUR.zp0.org[CUR.GS.rp1] );
       org_b = CUR_fast_dualproj( &CUR.zp1.org[CUR.GS.rp2] );
 
-#endif /* FIX_BYTECODE */
-
       cur_a = CUR_fast_project( &CUR.zp0.cur[CUR.GS.rp1] );
       cur_b = CUR_fast_project( &CUR.zp1.cur[CUR.GS.rp2] );
     }
@@ -6216,24 +6284,7 @@
       }
       else
       {
-
-#ifdef FIX_BYTECODE
-
-        FT_Vector  vec;
-
-
-        vec   = CUR.zp2.orus[point];
-        vec.x = TT_MULFIX( vec.x, CUR.metrics.x_scale );
-        vec.y = TT_MULFIX( vec.y, CUR.metrics.y_scale );
-
-        org_x = CUR_fast_dualproj( &vec );
-
-#else
-
         org_x = CUR_fast_dualproj( &CUR.zp2.org[point] );
-
-#endif /* FIX_BYTECODE */
-
         cur_x = CUR_fast_project ( &CUR.zp2.cur[point] );
 
         if ( ( org_a <= org_b && org_x <= org_a ) ||
@@ -6263,7 +6314,7 @@
     CUR.GS.loop = 1;
     CUR.new_top = CUR.args;
   }
-
+#endif
 
   /*************************************************************************/
   /*                                                                       */