[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.
This commit is contained in:
Dominik Röttsches 2021-01-20 13:04:50 +02:00 committed by Werner Lemberg
parent cc90307d71
commit 64f01bfedd
6 changed files with 211 additions and 41 deletions

View File

@ -1,3 +1,21 @@
2021-02-09 Dominik Röttsches <drott@chromium.org>
[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 <xavier.claessens@collabora.com>
* meson.build: s/freetype2_dep/freetype_dep/.

View File

@ -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 );
/**************************************************************************

View File

@ -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 );
/**************************************************************************

View File

@ -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;
}

View File

@ -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 );
}

View File

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