shithub: freetype+ttf2subf

Download patch

ref: 64f01bfeddea30b8b1f1c375df9b72d88bd5a3c5
parent: cc90307d7140867479428bc21861240cfe66cfec
author: Dominik Röttsches <[email protected]>
date: Wed Jan 20 08:04:50 EST 2021

[sfnt] Provide optional root transform for 'COLR' v1 glyph graph.

* include/freetype/freetype.h (FT_Get_Color_Glyph_Paint):
Additional function argument root_transform to control whether
root transform should be returned.
(FT_OpaquePaint): Additional tracking field to denote whether
root transform is to be returned.
* include/freetype/internal/sfnt.h
(TT_Get_Color_Glyph_Paint_Func): Propagate additional argument.
* src/base/ftobjs.c (FT_Get_Color_Glyph_Paint): Ditto.
* src/sfnt/ttcolr.c (tt_face_get_colr_glyph_paint): Return root
transform reflecting the size and tranform configured on
FT_Face.
(read_paint): Initialize and track status of insert_root_transform
flag.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2021-02-09  Dominik Röttsches  <[email protected]>
+
+	[sfnt] Provide optional root transform for 'COLR' v1 glyph graph.
+
+        * include/freetype/freetype.h (FT_Get_Color_Glyph_Paint):
+	Additional function argument `root_transform` to control whether
+	root transform should be returned.
+	(FT_OpaquePaint): Additional tracking field to denote whether
+	root transform is to be returned.
+	* include/freetype/internal/sfnt.h
+	(TT_Get_Color_Glyph_Paint_Func): Propagate additional argument.
+	* src/base/ftobjs.c (FT_Get_Color_Glyph_Paint): Ditto.
+	* src/sfnt/ttcolr.c (tt_face_get_colr_glyph_paint): Return root
+	transform reflecting the size and tranform configured on
+	`FT_Face`.
+	(read_paint): Initialize and track status of insert_root_transform
+	flag.
+
 2021-02-09  Xavier Claessens  <[email protected]>
 
 	* meson.build: s/freetype2_dep/freetype_dep/.
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -4479,11 +4479,15 @@
    *   p ::
    *     An internal offset to a Paint table, needs to be set to NULL before
    *     passing this struct as an argument to @FT_Get_Paint.
+   *
+   *   insert_root_transform ::
+   *     An internal boolean to track whether an initial root transform is
+   *     to be provided.  Do not set this value.
    */
   typedef struct  FT_Opaque_Paint_
   {
     FT_Byte*  p;
-
+    FT_Bool   insert_root_transform;
   } FT_OpaquePaint;
 
 
@@ -4881,6 +4885,33 @@
 
   /**************************************************************************
    *
+   * @enum:
+   *   FT_Color_Root_Transform
+   *
+   * @description:
+   *   An enumeration to specify whether @FT_Get_Color_Glyph_Paint is to
+   *   return a root transform to configure the client's graphics context
+   *   matrix.
+   *
+   * @values:
+   *   FT_COLOR_INCLUDE_ROOT_TRANSFORM ::
+   *     Do include the root transform as the initial @FT_COLR_Paint object.
+   *
+   *   FT_COLOR_NO_ROOT_TRANSFORM ::
+   *     Do not output an initial root transform.
+   */
+  typedef enum  FT_Color_Root_Transform_
+  {
+    FT_COLOR_INCLUDE_ROOT_TRANSFORM,
+    FT_COLOR_NO_ROOT_TRANSFORM,
+
+    FT_COLOR_ROOT_TRANSFORM_MAX
+
+  } FT_Color_Root_Transform;
+
+
+  /**************************************************************************
+   *
    * @function:
    *   FT_Get_Color_Glyph_Paint
    *
@@ -4898,6 +4929,26 @@
    *   function and specifying a glyph ID, one retrieves the root paint
    *   table for this glyph ID.
    *
+   *   This function allows control whether an initial root transform is
+   *   returned to configure scaling, transform, and translation correctly
+   *   on the client's graphics context.  The initial root transform is
+   *   computed and returned according to the values configured for @FT_Size
+   *   and @FT_Set_Transform on the @FT_Face object, see below for details
+   *   of the `root_transform` parameter.  This has implications for a
+   *   client 'COLR' v1 implementation: When this function returns an
+   *   initially computed root transform, at the time of executing the
+   *   @FT_Paint_Glyph operation, the contours should be retrieved using
+   *   @FT_Load_Glyph at unscaled, untransformed size.  This is because the
+   *   root transform applied to the graphics context will take care of
+   *   correct scaling.
+   *
+   *   Alternatively, to allow hinting of contours, at the time of executing
+   *   @FT_Load_Glyph, the current graphics context transformation matrix
+   *   can be decomposed into a scaling matrix and a remainder, and
+   *   @FT_Load_Glyph can be used to retrieve the contours at scaled size.
+   *   Care must then be taken to blit or clip to the graphics context with
+   *   taking this remainder transformation into account.
+   *
    * @input:
    *   face ::
    *     A handle to the parent face object.
@@ -4905,6 +4956,28 @@
    *   base_glyph ::
    *     The glyph index for which to retrieve the root paint table.
    *
+   *   root_transform ::
+   *     Specifies whether an initially computed root is returned by
+   *     @FT_Paint_Transformed to account for the activated size (see
+   *     @FT_Activate_Size) and the configured transform and translate (see
+   *     @FT_Set_Translate).
+   *
+   *     This root transform is returned before nodes of the glyph graph of
+   *     the font are returned.  Subsequent @FT_COLR_Paint structures
+   *     contain unscaled and untransformed values.  The inserted root
+   *     transform enables the client application to apply an initial
+   *     transform to its graphics context.  When executing subsequent
+   *     FT_COLR_Paint operations, values from @FT_COLR_Paint operations
+   *     will ultimately be correctly scaled because of the root transform
+   *     applied to the graphics context.  Use
+   *     @FT_COLOR_INCLUDE_ROOT_TRANSFORM to include the root transform, use
+   *     @FT_COLOR_NO_ROOT_TRANSFORM to not include it.  The latter may be
+   *     useful when traversing the 'COLR' v1 glyph graph and reaching a
+   *     @FT_PaintColrGlyph.  When recursing into @FT_PaintColrGlyph and
+   *     painting that inline, no additional root transform is needed as it
+   *     has already been applied to the graphics context at the beginning
+   *     of drawing this glyph.
+   *
    * @output:
    *   paint ::
    *     The @FT_OpaquePaint object that references the actual paint table.
@@ -4918,9 +4991,10 @@
    *   error, value~0 is returned also.
    */
   FT_EXPORT( FT_Bool )
-  FT_Get_Color_Glyph_Paint( FT_Face          face,
-                            FT_UInt          base_glyph,
-                            FT_OpaquePaint*  paint );
+  FT_Get_Color_Glyph_Paint( FT_Face                  face,
+                            FT_UInt                  base_glyph,
+                            FT_Color_Root_Transform  root_transform,
+                            FT_OpaquePaint*          paint );
 
 
   /**************************************************************************
--- a/include/freetype/internal/sfnt.h
+++ b/include/freetype/internal/sfnt.h
@@ -549,9 +549,10 @@
    *   error, value~0 is returned also.
    */
   typedef FT_Bool
-  ( *TT_Get_Color_Glyph_Paint_Func )( TT_Face          face,
-                                      FT_UInt          base_glyph,
-                                      FT_OpaquePaint  *paint );
+  ( *TT_Get_Color_Glyph_Paint_Func )( TT_Face                   face,
+                                      FT_UInt                   base_glyph,
+                                      FT_Color_Root_Transform   root_transform,
+                                      FT_OpaquePaint           *paint );
 
 
   /**************************************************************************
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -5571,9 +5571,10 @@
   /* documentation is in freetype.h */
 
   FT_EXPORT_DEF ( FT_Bool )
-  FT_Get_Color_Glyph_Paint( FT_Face          face,
-                            FT_UInt          base_glyph,
-                            FT_OpaquePaint*  paint )
+  FT_Get_Color_Glyph_Paint( FT_Face                  face,
+                            FT_UInt                  base_glyph,
+                            FT_Color_Root_Transform  root_transform,
+                            FT_OpaquePaint*          paint )
   {
     TT_Face       ttface;
     SFNT_Service  sfnt;
@@ -5589,7 +5590,10 @@
     sfnt   = (SFNT_Service)ttface->sfnt;
 
     if ( sfnt->get_colr_layer )
-      return sfnt->get_colr_glyph_paint( ttface, base_glyph, paint );
+      return sfnt->get_colr_glyph_paint( ttface,
+                                         base_glyph,
+                                         root_transform,
+                                         paint );
     else
       return 0;
   }
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -396,8 +396,9 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.glyph.paint.p = paint_p;
-      apaint->u.glyph.glyphID = FT_NEXT_USHORT( p );
+      apaint->u.glyph.paint.p                     = paint_p;
+      apaint->u.glyph.paint.insert_root_transform = 0;
+      apaint->u.glyph.glyphID                     = FT_NEXT_USHORT( p );
     }
 
     else if ( apaint->format == FT_COLR_PAINTFORMAT_SOLID )
@@ -475,7 +476,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.transformed.paint.p = paint_p;
+      apaint->u.transformed.paint.p                     = paint_p;
+      apaint->u.transformed.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.transformed.affine.xx = FT_NEXT_LONG( p );
@@ -506,7 +508,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.translate.paint.p = paint_p;
+      apaint->u.translate.paint.p                     = paint_p;
+      apaint->u.translate.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.translate.dx = FT_NEXT_LONG( p );
@@ -529,7 +532,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.rotate.paint.p = paint_p;
+      apaint->u.rotate.paint.p                     = paint_p;
+      apaint->u.rotate.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.rotate.angle = FT_NEXT_LONG( p );
@@ -555,7 +559,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.skew.paint.p = paint_p;
+      apaint->u.skew.paint.p                     = paint_p;
+      apaint->u.skew.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.skew.x_skew_angle = FT_NEXT_LONG( p );
@@ -588,7 +593,10 @@
       if ( source_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.composite.source_paint.p = source_paint_p;
+      apaint->u.composite.source_paint.p =
+        source_paint_p;
+      apaint->u.composite.source_paint.insert_root_transform =
+        0;
 
       composite_mode = FT_NEXT_BYTE( p );
       if ( composite_mode >= FT_COLR_COMPOSITE_MAX )
@@ -604,7 +612,10 @@
       if ( backdrop_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.composite.backdrop_paint.p = backdrop_paint_p;
+      apaint->u.composite.backdrop_paint.p =
+        backdrop_paint_p;
+      apaint->u.composite.backdrop_paint.insert_root_transform =
+        0;
     }
 
     else if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_GLYPH )
@@ -655,17 +666,16 @@
 
 
   FT_LOCAL_DEF ( FT_Bool )
-  tt_face_get_colr_glyph_paint( TT_Face          face,
-                                FT_UInt          base_glyph,
-                                FT_OpaquePaint*  opaque_paint )
+  tt_face_get_colr_glyph_paint( TT_Face                  face,
+                                FT_UInt                  base_glyph,
+                                FT_Color_Root_Transform  root_transform,
+                                FT_OpaquePaint*          opaque_paint )
   {
-    Colr*  colr = (Colr*)face->colr;
-
+    Colr*              colr = (Colr*)face->colr;
     BaseGlyphV1Record  base_glyph_v1_record;
     FT_Byte*           p;
 
-
-    if ( !colr )
+    if ( !colr || !colr->table )
       return 0;
 
     if ( colr->version < 1 || !colr->num_base_glyphs_v1 ||
@@ -692,6 +702,11 @@
 
     opaque_paint->p = p;
 
+    if ( root_transform == FT_COLOR_INCLUDE_ROOT_TRANSFORM )
+      opaque_paint->insert_root_transform = 1;
+    else
+      opaque_paint->insert_root_transform = 0;
+
     return 1;
   }
 
@@ -737,8 +752,12 @@
            colr->num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE ) )
       return 0;
 
-    paint_offset    = FT_NEXT_ULONG( p );
-    opaque_paint->p = (FT_Byte*)( colr->layers_v1 + paint_offset );
+    paint_offset =
+      FT_NEXT_ULONG( p );
+    opaque_paint->insert_root_transform =
+      0;
+    opaque_paint->p =
+      (FT_Byte*)( colr->layers_v1 + paint_offset );
 
     iterator->p = p;
 
@@ -794,21 +813,74 @@
                      FT_OpaquePaint  opaque_paint,
                      FT_COLR_Paint*  paint )
   {
-    Colr*  colr = (Colr*)face->colr;
+    Colr*           colr = (Colr*)face->colr;
+    FT_OpaquePaint  next_paint;
+    FT_Matrix       ft_root_scale;
 
-    FT_Byte*  p;
+    if ( !colr || !colr->base_glyphs_v1 || !colr->table )
+      return 0;
 
+    if ( opaque_paint.insert_root_transform )
+    {
+      /* 'COLR' v1 glyph information is returned in unscaled coordinates,
+       * i.e., `FT_Size` is not applied or multiplied into the values.  When
+       * client applications draw color glyphs, they can request to include
+       * a top-level transform, which includes the active `x_scale` and
+       * `y_scale` information for scaling the glyph, as well the additional
+       * transform and translate configured through `FT_Set_Transform`.
+       * This allows client applications to apply this top-level transform
+       * to the graphics context first and only once, then have gradient and
+       * contour scaling applied correctly when performing the additional
+       * drawing operations for subsequenct paints.  Prepare this initial
+       * transform here.
+       */
+      paint->format = FT_COLR_PAINTFORMAT_TRANSFORMED;
 
-    if ( !colr )
-      return 0;
+      next_paint.p                     = opaque_paint.p;
+      next_paint.insert_root_transform = 0;
+      paint->u.transformed.paint       = next_paint;
 
-    if ( opaque_paint.p < (FT_Byte*)colr->table                         ||
-         opaque_paint.p >= ( (FT_Byte*)colr->table + colr->table_size ) )
-      return 0;
+      /* `x_scale` and `y_scale` are in 26.6 format, representing the scale
+       * factor to get from font units to requested size.  However, expected
+       * return values are in 16.16, so we shift accordingly with rounding. 
+       */
+      ft_root_scale.xx = ( face->root.size->metrics.x_scale + 32 ) >> 6;
+      ft_root_scale.xy = 0;
+      ft_root_scale.yx = 0;
+      ft_root_scale.yy = ( face->root.size->metrics.y_scale + 32 ) >> 6;
 
-    p = opaque_paint.p;
+      if ( face->root.internal->transform_flags & 1 )
+        FT_Matrix_Multiply( &face->root.internal->transform_matrix,
+                            &ft_root_scale );
 
-    return read_paint( colr, p, paint );
+      paint->u.transformed.affine.xx = ft_root_scale.xx;
+      paint->u.transformed.affine.xy = ft_root_scale.xy;
+      paint->u.transformed.affine.yx = ft_root_scale.yx;
+      paint->u.transformed.affine.yy = ft_root_scale.yy;
+
+      /* The translation is specified in 26.6 format and, according to the
+       * documentation of `FT_Set_Translate`, is performed on the character
+       * size given in the last call to `FT_Set_Char_Size`.  The
+       * 'PaintTransformed' paint table's `FT_Affine23` format expects
+       * values in 16.16 format, thus we need to shift by 10 bits.
+       */
+      if ( face->root.internal->transform_flags & 2 )
+      {
+        paint->u.transformed.affine.dx =
+          face->root.internal->transform_delta.x << 10;
+        paint->u.transformed.affine.dy =
+          face->root.internal->transform_delta.y << 10;
+      }
+      else
+      {
+        paint->u.transformed.affine.dx = 0;
+        paint->u.transformed.affine.dy = 0;
+      }
+
+      return 1;
+    }
+
+    return read_paint( colr, opaque_paint.p, paint );
   }
 
 
--- a/src/sfnt/ttcolr.h
+++ b/src/sfnt/ttcolr.h
@@ -43,9 +43,10 @@
                           FT_LayerIterator*  iterator );
 
   FT_LOCAL( FT_Bool )
-  tt_face_get_colr_glyph_paint( TT_Face          face,
-                                FT_UInt          base_glyph,
-                                FT_OpaquePaint*  paint );
+  tt_face_get_colr_glyph_paint( TT_Face                  face,
+                                FT_UInt                  base_glyph,
+                                FT_Color_Root_Transform  root_transform,
+                                FT_OpaquePaint*          paint );
 
   FT_LOCAL ( FT_Bool )
   tt_face_get_paint_layers( TT_Face            face,