Commit dece9535164696777c1ae0000b399c470c306d6d

Dominik Röttsches 2022-06-29T11:48:53

[truetype] Perform variation store delta computation with 64-bit precision. * include/freetype/internal/ftmmtypes.h (FT_ItemVarDelta): Make type explicitly 32-bit. * include/freetype/internal/services/svmm.h (FT_Var_Get_Item_Delta_Func): Change return type to `FT_ItemVarDelta` * truetype/ttgxvar.h (tt_var_get_item_delta): Change return type to `FT_ItemVarDelta`. * truetype/ttgxvar.c (tt_var_get_item_delta): Store scalars and deltas to intermediate array, perform computation using new method `FT_MulAddFix`.

diff --git a/include/freetype/internal/ftmmtypes.h b/include/freetype/internal/ftmmtypes.h
index 44f7e74..570c7fd 100644
--- a/include/freetype/internal/ftmmtypes.h
+++ b/include/freetype/internal/ftmmtypes.h
@@ -24,7 +24,7 @@
 FT_BEGIN_HEADER
 
 
-  typedef FT_Long FT_ItemVarDelta;
+  typedef FT_Int32  FT_ItemVarDelta;
 
   typedef struct  GX_ItemVarDataRec_
   {
diff --git a/include/freetype/internal/services/svmm.h b/include/freetype/internal/services/svmm.h
index 22ddc8c..b67ea7c 100644
--- a/include/freetype/internal/services/svmm.h
+++ b/include/freetype/internal/services/svmm.h
@@ -109,7 +109,7 @@ FT_BEGIN_HEADER
                                       FT_ULong         offset,
                                       GX_ItemVarStore  itemStore );
 
-  typedef FT_Int
+  typedef FT_ItemVarDelta
   (*FT_Var_Get_Item_Delta_Func)( FT_Face          face,
                                  GX_ItemVarStore  itemStore,
                                  FT_UInt          outerIndex,
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index 1bf9deb..65fc701 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -939,19 +939,23 @@
   }
 
 
-  FT_LOCAL_DEF( FT_Int )
+  FT_LOCAL_DEF( FT_ItemVarDelta )
   tt_var_get_item_delta( TT_Face          face,
                          GX_ItemVarStore  itemStore,
                          FT_UInt          outerIndex,
                          FT_UInt          innerIndex )
   {
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+    FT_Error   error  = FT_Err_Ok;
+
     GX_ItemVarData    varData;
     FT_ItemVarDelta*  deltaSet;
 
-    FT_UInt   master, j;
-    FT_Fixed  netAdjustment = 0;     /* accumulated adjustment */
-    FT_Fixed  scaledDelta;
-    FT_Fixed  delta;
+    FT_UInt           master, j;
+    FT_Fixed*         scalars;
+    FT_ItemVarDelta   returnValue;
+
 
     /* OpenType 1.8.4+: No variation data for this item
      *  as indices have special value 0xFFFF. */
@@ -964,6 +968,9 @@
     varData  = &itemStore->varData[outerIndex];
     deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
 
+    if ( FT_QNEW_ARRAY( scalars, varData->regionIdxCount ) )
+      return 0;
+
     /* outer loop steps through master designs to be blended */
     for ( master = 0; master < varData->regionIdxCount; master++ )
     {
@@ -1013,18 +1020,33 @@
             FT_MulDiv( scalar,
                        axis->endCoord - face->blend->normalizedcoords[j],
                        axis->endCoord - axis->peakCoord );
-      } /* per-axis loop */
 
-      /* get the scaled delta for this region */
-      delta       = FT_intToFixed( deltaSet[master] );
-      scaledDelta = FT_MulFix( scalar, delta );
+      } /* per-axis loop */
 
-      /* accumulate the adjustments from each region */
-      netAdjustment = netAdjustment + scaledDelta;
+      scalars[master] = scalar;
 
     } /* per-region loop */
 
-    return FT_fixedToInt( netAdjustment );
+
+    /* Compute the scaled delta for this region.
+     *
+     * From: https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store-header-and-item-variation-data-subtables:
+     *
+     *   `Fixed` is a 32-bit (16.16) type and, in the general case, requires
+     *   32-bit deltas.  As described above, the `DeltaSet` record can
+     *   accommodate deltas that are, logically, either 16-bit or 32-bit.
+     *   When scaled deltas are applied to `Fixed` values, the `Fixed` value
+     *   is treated like a 32-bit integer.
+     *
+     * `FT_MulAddFix` internally uses 64-bit precision; it thus can handle
+     * deltas ranging from small 8-bit to large 32-bit values that are
+     * applied to 16.16 `FT_Fixed` / OpenType `Fixed` values.
+     */
+    returnValue = FT_MulAddFix( scalars, deltaSet, varData->regionIdxCount );
+
+    FT_FREE( scalars );
+
+    return returnValue;
   }
 
 
diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h
index 15c2637..513c40b 100644
--- a/src/truetype/ttgxvar.h
+++ b/src/truetype/ttgxvar.h
@@ -391,7 +391,7 @@ FT_BEGIN_HEADER
                                        GX_ItemVarStore    itemStore,
                                        FT_ULong           table_len );
 
-  FT_LOCAL( FT_Int )
+  FT_LOCAL( FT_ItemVarDelta )
   tt_var_get_item_delta( TT_Face          face,
                          GX_ItemVarStore  itemStore,
                          FT_UInt          outerIndex,