Commit 1178227b39a4eeecbefad226d4a753b345a16eb1

Werner Lemberg 2019-05-11T09:29:19

[truetype] Increase precision of font variation (#54371). This patch make FreeType use font units in 26.6 format internally instead of integers. * src/truetype/ttgxvar.c (FT_fixedToFdot6): New macro. (TT_Vary_Apply_Glyph_Deltas): Add argument to output unrounded font coordinates. * src/truetype/ttgxvar.h: Updated. * src/truetype/ttgload.c (TT_Process_Simple_Glyph): Use `extra_points2' array to temporarily hold unrounded point coordinates; use them to compute scaled coordinates and linear advance width and height. (load_truetype_code): Adjust similarly.

diff --git a/ChangeLog b/ChangeLog
index 31a84ee..52492cd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
 2019-05-09  Werner Lemberg  <wl@gnu.org>
 
+	[truetype] Increase precision of font variation (#54371).
+
+	This patch make FreeType use font units in 26.6 format internally
+	instead of integers.
+
+	* src/truetype/ttgxvar.c (FT_fixedToFdot6): New macro.
+	(TT_Vary_Apply_Glyph_Deltas): Add argument to output unrounded font
+	coordinates.
+	* src/truetype/ttgxvar.h: Updated.
+
+	* src/truetype/ttgload.c (TT_Process_Simple_Glyph): Use
+	`extra_points2' array to temporarily hold unrounded point
+	coordinates; use them to compute scaled coordinates and linear
+	advance width and height.
+	(load_truetype_code): Adjust similarly.
+
+2019-05-09  Werner Lemberg  <wl@gnu.org>
+
 	* src/truetype/ttgload.c (TT_Process_Simple_Glyph): Minor.
 
 2019-05-08  Alexei Podtelezhnikov  <apodtele@gmail.com>
diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h
index f758ea1..2f7863c 100644
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -1766,6 +1766,13 @@ FT_BEGIN_HEADER
    *     transformed, distorted, emboldened, etc.  However, it must not be
    *     freed.
    *
+   *     [Since 2.10.1] If @FT_LOAD_NO_SCALE is set, outline coordinates of
+   *     OpenType variation fonts for a selected instance are internally
+   *     handled as 26.6 fractional font units but returned as (rounded)
+   *     integers, as expected.  To get unrounded font units, don't use
+   *     @FT_LOAD_NO_SCALE but load the glyph with @FT_LOAD_NO_HINTING and
+   *     scale it, using the font's `units_per_EM` value as the ppem.
+   *
    *   num_subglyphs ::
    *     The number of subglyphs in a composite glyph.  This field is only
    *     valid for the composite glyph format that should normally only be
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index d88f4c4..d4a87ef 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -925,6 +925,7 @@
     FT_GlyphLoader  gloader = loader->gloader;
     FT_Error        error   = FT_Err_Ok;
     FT_Outline*     outline;
+    FT_Vector*      unrounded;
     FT_Int          n_points;
 
 
@@ -950,20 +951,26 @@
     if ( FT_IS_NAMED_INSTANCE( FT_FACE( loader->face ) ) ||
          FT_IS_VARIATION( FT_FACE( loader->face ) )      )
     {
-      /* Deltas apply to the unscaled data. */
+      /* Deltas apply to the unscaled data.                           */
+      /* We temporarily use `extra_points2' to hold unrounded values. */
+      unrounded = gloader->current.extra_points2;
       error = TT_Vary_Apply_Glyph_Deltas( loader->face,
                                           loader->glyph_index,
                                           outline,
+                                          unrounded,
                                           (FT_UInt)n_points );
 
       /* recalculate linear horizontal and vertical advances */
       /* if we don't have HVAR and VVAR, respectively        */
+
+      /* XXX: change all FreeType modules to store `linear' and `vadvance' */
+      /*      in 26.6 format before the `base' module scales them to 16.16 */
       if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
-        loader->linear = outline->points[n_points - 3].x -
-                         outline->points[n_points - 4].x;
+        loader->linear = FT_PIX_ROUND( unrounded[n_points - 3].x -
+                                       unrounded[n_points - 4].x ) / 64;
       if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
-        loader->vadvance = outline->points[n_points - 1].x -
-                           outline->points[n_points - 2].x;
+        loader->vadvance = FT_PIX_ROUND( unrounded[n_points - 1].x -
+                                         unrounded[n_points - 2].x ) / 64;
 
       if ( error )
         return error;
@@ -1013,10 +1020,17 @@
           /* compensate for any scaling by de/emboldening; */
           /* the amount was determined via experimentation */
           if ( x_scale_factor != 1000 && ppem > 11 )
+          {
+            FT_Vector*  orig_points = outline->points;
+
+
+            outline->points = unrounded;
             FT_Outline_EmboldenXY( outline,
                                    FT_MulFix( 1280 * ppem,
                                               1000 - x_scale_factor ),
                                    0 );
+            outline->points = orig_points;
+          }
           do_scale = TRUE;
         }
       }
@@ -1037,10 +1051,10 @@
 
       if ( do_scale )
       {
-        for ( ; vec < limit; vec++ )
+        for ( ; vec < limit; vec++, unrounded++ )
         {
-          vec->x = FT_MulFix( vec->x, x_scale );
-          vec->y = FT_MulFix( vec->y, y_scale );
+          vec->x = FT_MulDiv( unrounded->x, x_scale, 0x10000L * 64 );
+          vec->y = FT_MulDiv( unrounded->y, y_scale, 0x10000L * 64 );
         }
       }
 
@@ -1678,6 +1692,9 @@
         short       contours[4] = { 0, 1, 2, 3 };
         FT_Outline  outline;
 
+        /* unrounded values */
+        FT_Vector  unrounded[4];
+
 
         points[0].x = loader->pp1.x;
         points[0].y = loader->pp1.y;
@@ -1699,6 +1716,7 @@
         error = TT_Vary_Apply_Glyph_Deltas( loader->face,
                                             glyph_index,
                                             &outline,
+                                            unrounded,
                                             (FT_UInt)outline.n_points );
         if ( error )
           goto Exit;
@@ -1716,9 +1734,11 @@
         /* recalculate linear horizontal and vertical advances */
         /* if we don't have HVAR and VVAR, respectively        */
         if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
-          loader->linear = loader->pp2.x - loader->pp1.x;
+          loader->linear = FT_PIX_ROUND( unrounded[1].x -
+                                         unrounded[0].x ) / 64;
         if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
-          loader->vadvance = loader->pp4.x - loader->pp3.x;
+          loader->vadvance = FT_PIX_ROUND( unrounded[3].x -
+                                           unrounded[2].x ) / 64;
       }
 
 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
@@ -1858,9 +1878,10 @@
         FT_SubGlyph  subglyph;
 
         FT_Outline  outline;
-        FT_Vector*  points   = NULL;
-        char*       tags     = NULL;
-        short*      contours = NULL;
+        FT_Vector*  points    = NULL;
+        char*       tags      = NULL;
+        short*      contours  = NULL;
+        FT_Vector*  unrounded = NULL;
 
 
         limit = (short)gloader->current.num_subglyphs;
@@ -1874,9 +1895,10 @@
         outline.tags     = NULL;
         outline.contours = NULL;
 
-        if ( FT_NEW_ARRAY( points, outline.n_points )   ||
-             FT_NEW_ARRAY( tags, outline.n_points )     ||
-             FT_NEW_ARRAY( contours, outline.n_points ) )
+        if ( FT_NEW_ARRAY( points, outline.n_points )    ||
+             FT_NEW_ARRAY( tags, outline.n_points )      ||
+             FT_NEW_ARRAY( contours, outline.n_points )  ||
+             FT_NEW_ARRAY( unrounded, outline.n_points ) )
           goto Exit1;
 
         subglyph = gloader->current.subglyphs;
@@ -1925,6 +1947,7 @@
                              face,
                              glyph_index,
                              &outline,
+                             unrounded,
                              (FT_UInt)outline.n_points ) ) )
           goto Exit1;
 
@@ -1952,14 +1975,17 @@
         /* recalculate linear horizontal and vertical advances */
         /* if we don't have HVAR and VVAR, respectively        */
         if ( !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
-          loader->linear = loader->pp2.x - loader->pp1.x;
+          loader->linear = FT_PIX_ROUND( unrounded[1].x -
+                                         unrounded[0].x ) / 64;
         if ( !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
-          loader->vadvance = loader->pp4.x - loader->pp3.x;
+          loader->vadvance = FT_PIX_ROUND( unrounded[3].x -
+                                           unrounded[2].x ) / 64;
 
       Exit1:
         FT_FREE( outline.points );
         FT_FREE( outline.tags );
         FT_FREE( outline.contours );
+        FT_FREE( unrounded );
 
         if ( error )
           goto Exit;
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index 56b5b6d..317340a 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -68,12 +68,14 @@
 
 
   /* some macros we need */
-#define FT_fdot14ToFixed( x )                \
-        ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
-#define FT_intToFixed( i )                    \
-        ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
-#define FT_fixedToInt( x )                                   \
-        ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define FT_fdot14ToFixed( x )                  \
+          ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
+#define FT_intToFixed( i )                      \
+          ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
+#define FT_fixedToInt( x )                                     \
+          ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define FT_fixedToFdot6( x )                              \
+          ( (FT_Pos)( ( (FT_UInt32)(x) + 0x20 ) >> 10 ) )
 
 
   /**************************************************************************
@@ -3674,6 +3676,11 @@
    *   outline ::
    *     The outline to change.
    *
+   * @Output:
+   *   unrounded ::
+   *     An array with `n_points' elements that is filled with unrounded
+   *     point coordinates (in 26.6 format).
+   *
    * @Return:
    *   FreeType error code.  0 means success.
    */
@@ -3681,6 +3688,7 @@
   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
                               FT_UInt      glyph_index,
                               FT_Outline*  outline,
+                              FT_Vector*   unrounded,
                               FT_UInt      n_points )
   {
     FT_Error   error;
@@ -4068,6 +4076,11 @@
 
     for ( i = 0; i < n_points; i++ )
     {
+      unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x ) +
+                         FT_fixedToFdot6( point_deltas_x[i] );
+      unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y ) +
+                         FT_fixedToFdot6( point_deltas_y[i] );
+
       outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
       outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
     }
diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h
index 7e8d976..07c99b6 100644
--- a/src/truetype/ttgxvar.h
+++ b/src/truetype/ttgxvar.h
@@ -416,6 +416,7 @@ FT_BEGIN_HEADER
   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
                               FT_UInt      glyph_index,
                               FT_Outline*  outline,
+                              FT_Vector*   unrounded,
                               FT_UInt      n_points );
 
   FT_LOCAL( FT_Error )