Commit 286da6c528ace3443d993370c798cdc09011200b

Dominik Röttsches 2021-06-22T15:01:19

[sfnt] Support PaintScale in 'COLR' v1 parsing. * include/freetype/ftcolor.h (FT_PaintFormat): Renumber values, add `FT_COLR_PAINTFORMAT_SCALE`. (FT_PaintScale): New structure to represent 'PaintScale*' tables. (FT_COLR_Paint): Updated. * src/sfnt/ttcolr.c (FT_PaintFormat_Internal): New enumeration. (read_paint): Parse 'PaintScale' and friends.

diff --git a/ChangeLog b/ChangeLog
index eccf4a1..9209ea8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2021-06-30  Dominik Röttsches  <drott@chromium.org>
 
+	[sfnt] Support PaintScale in 'COLR' v1 parsing.
+
+	* include/freetype/ftcolor.h (FT_PaintFormat): Renumber values, add
+	`FT_COLR_PAINTFORMAT_SCALE`.
+	(FT_PaintScale): New structure to represent 'PaintScale*' tables.
+	(FT_COLR_Paint): Updated.
+
+	* src/sfnt/ttcolr.c (FT_PaintFormat_Internal): New enumeration.
+	(read_paint): Parse 'PaintScale' and friends.
+
+2021-06-30  Dominik Röttsches  <drott@chromium.org>
+
 	[sfnt] Handle fonts without layer list in 'COLR' v1.
 
 	'COLR' v1 fonts do not necessarily need to have a layer list; for
diff --git a/include/freetype/ftcolor.h b/include/freetype/ftcolor.h
index 63b3565..59e6f15 100644
--- a/include/freetype/ftcolor.h
+++ b/include/freetype/ftcolor.h
@@ -475,12 +475,18 @@ FT_BEGIN_HEADER
    *   extensions to the 'COLR' table, see
    *   'https://github.com/googlefonts/colr-gradients-spec'.
    *
-   *   Only non-variable format identifiers are listed in this enumeration;
-   *   as soon as support for variable 'COLR' v1 fonts is implemented,
-   *   interpolation is performed dependent on axis coordinates, which are
-   *   configured on the @FT_Face through @FT_Set_Var_Design_Coordinates.
-   *   This implies that always static (interpolated) values are returned
-   *   for both variable and non-variable formats.
+   *   The enumeration values losely correspond with the format numbers of
+   *   the specification: FreeType always returns a fully specified 'Paint'
+   *   structure for the 'Transform', 'Translate', 'Scale', 'Rotate', and
+   *   'Skew' table types even though the specification has different formats
+   *   depending on whether or not a center is specified, whether the scale
+   *   is uniform in x and y~direction or not, etc.  Also, only non-variable
+   *   format identifiers are listed in this enumeration; as soon as support
+   *   for variable 'COLR' v1 fonts is implemented, interpolation is
+   *   performed dependent on axis coordinates, which are configured on the
+   *   @FT_Face through @FT_Set_Var_Design_Coordinates.  This implies that
+   *   always static, readily interpolated values are returned in the 'Paint'
+   *   structures.
    *
    * @since:
    *   2.11 -- **currently experimental only!**  There might be changes
@@ -498,10 +504,11 @@ FT_BEGIN_HEADER
     FT_COLR_PAINTFORMAT_COLR_GLYPH      = 11,
     FT_COLR_PAINTFORMAT_TRANSFORM       = 12,
     FT_COLR_PAINTFORMAT_TRANSLATE       = 14,
-    FT_COLR_PAINTFORMAT_ROTATE          = 16,
-    FT_COLR_PAINTFORMAT_SKEW            = 18,
-    FT_COLR_PAINTFORMAT_COMPOSITE       = 20,
-    FT_COLR_PAINT_FORMAT_MAX            = 21,
+    FT_COLR_PAINTFORMAT_SCALE           = 16,
+    FT_COLR_PAINTFORMAT_ROTATE          = 24,
+    FT_COLR_PAINTFORMAT_SKEW            = 28,
+    FT_COLR_PAINTFORMAT_COMPOSITE       = 32,
+    FT_COLR_PAINT_FORMAT_MAX            = 33,
     FT_COLR_PAINTFORMAT_UNSUPPORTED     = 255
 
   } FT_PaintFormat;
@@ -1104,6 +1111,56 @@ FT_BEGIN_HEADER
   /**************************************************************************
    *
    * @struct:
+   *   FT_PaintScale
+   *
+   * @description:
+   *   A structure representing all of the 'COLR' v1 'PaintScale*' paint
+   *   tables.  Used for scaling downstream paints by a given x and y~scale,
+   *   with a given center.  This structure is used for all 'PaintScale*'
+   *   types that are part of specification; fields of this structure are
+   *   filled accordingly.  If there is a center, the center values are set,
+   *   otherwise they are set to the zero coordinate.  If the source font
+   *   file has 'PaintScaleUniform*' set, the scale values are set
+   *   accordingly to the same value.
+   *
+   * @fields:
+   *   paint ::
+   *     An @FT_OpaquePaint object referencing the paint that is to be
+   *     scaled.
+   *
+   *   scale_x ::
+   *     Scale factor in x~direction.
+   *
+   *   scale_y ::
+   *     Scale factor in y~direction.
+   *
+   *   center_x ::
+   *     x~coordinate of center point to scale from.
+   *
+   *   center_y ::
+   *     y~coordinate of center point to scale from.
+   *
+   * @since:
+   *   2.11 -- **currently experimental only!**  There might be changes
+   *   without retaining backward-compatibility of both the API and ABI.
+   *
+   */
+  typedef struct  FT_PaintScale_
+  {
+    FT_OpaquePaint  paint;
+
+    FT_Fixed  scale_x;
+    FT_Fixed  scale_y;
+
+    FT_Fixed  center_x;
+    FT_Fixed  center_y;
+
+  } FT_PaintScale;
+
+
+  /**************************************************************************
+   *
+   * @struct:
    *   FT_PaintRotate
    *
    * @description:
@@ -1277,6 +1334,7 @@ FT_BEGIN_HEADER
       FT_PaintSweepGradient   sweep_gradient;
       FT_PaintTransform       transform;
       FT_PaintTranslate       translate;
+      FT_PaintScale           scale;
       FT_PaintRotate          rotate;
       FT_PaintSkew            skew;
       FT_PaintComposite       composite;
diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c
index 34accdd..bd478ab 100644
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -49,6 +49,17 @@
 #define COLR_HEADER_SIZE                 14U
 
 
+  typedef enum  FT_PaintFormat_Internal_
+  {
+    FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER         = 18,
+    FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM        = 20,
+    FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM_CENTER = 22,
+    FT_COLR_PAINTFORMAT_INTERNAL_ROTATE_CENTER        = 26,
+    FT_COLR_PAINTFORMAT_INTERNAL_SKEW_CENTER          = 30
+
+  } FT_PaintFormat_Internal;
+
+
   typedef struct  BaseGlyphRecord_
   {
     FT_UShort  gid;
@@ -561,6 +572,53 @@
       return 1;
     }
 
+    else if ( apaint->format ==
+                FT_COLR_PAINTFORMAT_SCALE                         ||
+              (FT_PaintFormat_Internal)apaint->format ==
+                FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER         ||
+              (FT_PaintFormat_Internal)apaint->format ==
+                FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM        ||
+              (FT_PaintFormat_Internal)apaint->format ==
+                FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM_CENTER )
+    {
+      apaint->u.scale.paint.p                     = child_table_p;
+      apaint->u.scale.paint.insert_root_transform = 0;
+
+      /* All scale paints get at least one scale value. */
+      apaint->u.scale.scale_x = FT_NEXT_LONG( p );
+
+      /* Non-uniform ones read an extra y value. */
+      if ( apaint->format ==
+             FT_COLR_PAINTFORMAT_SCALE                 ||
+           (FT_PaintFormat_Internal)apaint->format ==
+             FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER )
+        apaint->u.scale.scale_y = FT_NEXT_LONG( p );
+      else
+        apaint->u.scale.scale_y = apaint->u.scale.scale_x;
+
+      /* Scale paints that have a center read center coordinates, */
+      /* otherwise the center is (0,0).                           */
+      if ( (FT_PaintFormat_Internal)apaint->format ==
+             FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER         ||
+           (FT_PaintFormat_Internal)apaint->format ==
+             FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM_CENTER )
+      {
+        apaint->u.scale.center_x = FT_NEXT_LONG ( p );
+        apaint->u.scale.center_y = FT_NEXT_LONG ( p );
+      }
+      else
+      {
+        apaint->u.scale.center_x = 0;
+        apaint->u.scale.center_y = 0;
+      }
+
+      /* FT 'COLR' v1 API output format always returns fully defined */
+      /* structs; we thus set the format to the public API value.    */
+      apaint->format = FT_COLR_PAINTFORMAT_SCALE;
+
+      return 1;
+    }
+
     else if ( apaint->format == FT_COLR_PAINTFORMAT_ROTATE )
     {
       apaint->u.rotate.paint.p                     = child_table_p;