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.
--- 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,