diff --git a/ChangeLog b/ChangeLog index c92b98cf7..d310facff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2021-02-09 Dominik Röttsches + + [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 * meson.build: s/freetype2_dep/freetype_dep/. diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index dba1f9a53..5d8e8dcb9 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -4479,11 +4479,15 @@ FT_BEGIN_HEADER * 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; @@ -4879,6 +4883,33 @@ FT_BEGIN_HEADER } FT_COLR_Paint; + /************************************************************************** + * + * @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: @@ -4898,6 +4929,26 @@ FT_BEGIN_HEADER * 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 @@ FT_BEGIN_HEADER * 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 @@ FT_BEGIN_HEADER * 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 ); /************************************************************************** diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index bfb2d7122..438ec897e 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -549,9 +549,10 @@ FT_BEGIN_HEADER * 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 ); /************************************************************************** diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index dd478e03a..829f4ab84 100644 --- 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; } diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c index 8a6aef230..6ba3a6350 100644 --- 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 ) + if ( !colr || !colr->base_glyphs_v1 || !colr->table ) return 0; - if ( opaque_paint.p < (FT_Byte*)colr->table || - opaque_paint.p >= ( (FT_Byte*)colr->table + colr->table_size ) ) - 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; - p = opaque_paint.p; + next_paint.p = opaque_paint.p; + next_paint.insert_root_transform = 0; + paint->u.transformed.paint = next_paint; - return read_paint( colr, p, paint ); + /* `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; + + if ( face->root.internal->transform_flags & 1 ) + FT_Matrix_Multiply( &face->root.internal->transform_matrix, + &ft_root_scale ); + + 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 ); } diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h index d9ba9acf5..4a7ed4bf4 100644 --- a/src/sfnt/ttcolr.h +++ b/src/sfnt/ttcolr.h @@ -43,9 +43,10 @@ FT_BEGIN_HEADER 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,