From 47cf8ebf4a78ed42da455a98d77a92ce6a180d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20R=C3=B6ttsches?= Date: Wed, 28 Jul 2021 17:36:57 +0300 Subject: [PATCH] [sfnt] Add API for retrieving a 'COLR' v1 'ClipBox' table. The optional 'COLR' v1 glyph-specific clip box helps upstream graphics libraries allocate a sufficiently large bitmap for a glyph without having to traverse the glyph graph for that. See https://github.com/googlefonts/colr-gradients-spec/issues/251 for background on the introduction of this specification change. * include/freetype/ftcolor.h (FT_ClipBox): New structure. (FT_Get_Color_Glyph_ClipBox): New function declaration. * include/freetype/internal/sfnt.h (TT_Get_Color_Glyph_ClipBox_Func): New function type. (SFNT_Interface, FT_DEFINE_SFNT_INTERFACE): Use it. * src/base/ftobjs.c (FT_Get_Color_Glyph_ClipBox): New function to link API with SFNT implementation. * src/sfnt/sfdriver.c (sfnt_interface): Updated. * src/sfnt/ttcolr.c (Colr): New field `clip_list`. (tt_face_load_colr): Parse global clip list offset. (tt_face_get_color_glyph_clipbox): New function to find the clip box for a glyph id from the clip list array. * src/sfnt/ttcolr.h: Updated. --- include/freetype/ftcolor.h | 86 +++++++++++++++++++++ include/freetype/internal/sfnt.h | 63 +++++++++++++--- src/base/ftobjs.c | 29 +++++++ src/sfnt/sfdriver.c | 10 ++- src/sfnt/ttcolr.c | 125 ++++++++++++++++++++++++++++++- src/sfnt/ttcolr.h | 5 ++ 6 files changed, 302 insertions(+), 16 deletions(-) diff --git a/include/freetype/ftcolor.h b/include/freetype/ftcolor.h index a4bd62a26..b98289917 100644 --- a/include/freetype/ftcolor.h +++ b/include/freetype/ftcolor.h @@ -1383,6 +1383,49 @@ FT_BEGIN_HEADER } FT_Color_Root_Transform; + /************************************************************************** + * + * @struct: + * FT_ClipBox + * + * @description: + * A structure representing a 'COLR' v1 'ClipBox' table. 'COLR' v1 + * glyphs may optionally define a clip box for aiding allocation or + * defining a maximum drawable region. Use @FT_Get_Color_Glyph_ClipBox + * to retrieve it. + * + * @fields: + * bottom_left :: + * The bottom left corner of the clip box as an @FT_Vector with + * fixed-point coordinates in 26.6 format. + * + * top_left :: + * The top left corner of the clip box as an @FT_Vector with + * fixed-point coordinates in 26.6 format. + * + * top_right :: + * The top right corner of the clip box as an @FT_Vector with + * fixed-point coordinates in 26.6 format. + * + * bottom_right :: + * The bottom right corner of the clip box as an @FT_Vector with + * fixed-point coordinates in 26.6 format. + * + * @since: + * 2.12 -- **currently experimental only!** There might be changes + * without retaining backward compatibility of both the API and ABI. + * + */ + typedef struct FT_ClipBox_ + { + FT_Vector bottom_left; + FT_Vector top_left; + FT_Vector top_right; + FT_Vector bottom_right; + + } FT_ClipBox; + + /************************************************************************** * * @function: @@ -1475,6 +1518,49 @@ FT_BEGIN_HEADER FT_OpaquePaint* paint ); + /************************************************************************** + * + * @function: + * FT_Get_Color_Glyph_ClipBox + * + * @description: + * Search for a 'COLR' v1 clip box for the specified `base_glyph` and + * fill the `clip_box` parameter with the 'COLR' v1 'ClipBox' information + * if one is found. + * + * @input: + * face :: + * A handle to the parent face object. + * + * base_glyph :: + * The glyph index for which to retrieve the clip box. + * + * @output: + * clip_box :: + * The clip box for the requested `base_glyph` if one is found. The + * clip box is computed taking scale and transformations configured on + * the @FT_Face into account. @FT_ClipBox contains @FT_Vector values + * in 26.6 format. + * + * @return: + * Value~1 if a clip box is found. If no clip box is found or an error + * occured, value~0 is returned. + * + * @note: + * To retrieve the clip box in font units, reset scale to units-per-em + * and remove transforms configured using @FT_Set_Transform. + * + * @since: + * 2.12 -- **currently experimental only!** There might be changes + * without retaining backward compatibility of both the API and ABI. + * + */ + FT_EXPORT( FT_Bool ) + FT_Get_Color_Glyph_ClipBox( FT_Face face, + FT_UInt base_glyph, + FT_ClipBox* clip_box ); + + /************************************************************************** * * @function: diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index 438ec897e..e8d77e232 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -555,6 +555,44 @@ FT_BEGIN_HEADER FT_OpaquePaint *paint ); + /************************************************************************** + * + * @functype: + * TT_Get_Color_Glyph_ClipBox_Func + * + * @description: + * Search for a 'COLR' v1 clip box for the specified `base_glyph` and + * fill the `clip_box` parameter with the 'COLR' v1 'ClipBox' information + * if one is found. + * + * @input: + * face :: + * A handle to the parent face object. + * + * base_glyph :: + * The glyph index for which to retrieve the clip box. + * + * @output: + * clip_box :: + * The clip box for the requested base_glyph if one is found. The clip + * box is computed taking scale and transformations configured on the + * @FT_Face into account. @FT_ClipBox contains @FT_Vector values in + * 26.6 format. + * + * @note: + * To retrieve the clip box in font units, reset scale to units-per-em + * and remove transforms configured using @FT_Set_Transform. + * + * @return: + * Value~1 if a ClipBox is found. If no clip box is found or an + * error occured, value~0 is returned. + */ + typedef FT_Bool + ( *TT_Get_Color_Glyph_ClipBox_Func )( TT_Face face, + FT_UInt base_glyph, + FT_ClipBox* clip_box ); + + /************************************************************************** * * @functype: @@ -890,17 +928,18 @@ FT_BEGIN_HEADER TT_Set_SBit_Strike_Func set_sbit_strike; TT_Load_Strike_Metrics_Func load_strike_metrics; - TT_Load_Table_Func load_cpal; - TT_Load_Table_Func load_colr; - TT_Free_Table_Func free_cpal; - TT_Free_Table_Func free_colr; - TT_Set_Palette_Func set_palette; - TT_Get_Colr_Layer_Func get_colr_layer; - TT_Get_Color_Glyph_Paint_Func get_colr_glyph_paint; - TT_Get_Paint_Layers_Func get_paint_layers; - TT_Get_Colorline_Stops_Func get_colorline_stops; - TT_Get_Paint_Func get_paint; - TT_Blend_Colr_Func colr_blend; + TT_Load_Table_Func load_cpal; + TT_Load_Table_Func load_colr; + TT_Free_Table_Func free_cpal; + TT_Free_Table_Func free_colr; + TT_Set_Palette_Func set_palette; + TT_Get_Colr_Layer_Func get_colr_layer; + TT_Get_Color_Glyph_Paint_Func get_colr_glyph_paint; + TT_Get_Color_Glyph_ClipBox_Func get_color_glyph_clipbox; + TT_Get_Paint_Layers_Func get_paint_layers; + TT_Get_Colorline_Stops_Func get_colorline_stops; + TT_Get_Paint_Func get_paint; + TT_Blend_Colr_Func colr_blend; TT_Get_Metrics_Func get_metrics; @@ -951,6 +990,7 @@ FT_BEGIN_HEADER set_palette_, \ get_colr_layer_, \ get_colr_glyph_paint_, \ + get_color_glyph_clipbox, \ get_paint_layers_, \ get_colorline_stops_, \ get_paint_, \ @@ -995,6 +1035,7 @@ FT_BEGIN_HEADER set_palette_, \ get_colr_layer_, \ get_colr_glyph_paint_, \ + get_color_glyph_clipbox, \ get_paint_layers_, \ get_colorline_stops_, \ get_paint_, \ diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index 0ded2440f..5c1a4d034 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -5639,6 +5639,35 @@ } + /* documentation is in ftcolor.h */ + + FT_EXPORT_DEF( FT_Bool ) + FT_Get_Color_Glyph_ClipBox( FT_Face face, + FT_UInt base_glyph, + FT_ClipBox* clip_box ) + { + TT_Face ttface; + SFNT_Service sfnt; + + + if ( !face || !clip_box ) + return 0; + + if ( !FT_IS_SFNT( face ) ) + return 0; + + ttface = (TT_Face)face; + sfnt = (SFNT_Service)ttface->sfnt; + + if ( sfnt->get_color_glyph_clipbox ) + return sfnt->get_color_glyph_clipbox( ttface, + base_glyph, + clip_box ); + else + return 0; + } + + /* documentation is in freetype.h */ FT_EXPORT_DEF( FT_Bool ) diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index 824e709fa..d1d01c99e 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -1292,13 +1292,15 @@ /* TT_Get_Colr_Layer_Func get_colr_layer */ PUT_COLOR_LAYERS_V1( tt_face_get_colr_glyph_paint ), - /* TT_Get_Colr_Glyph_Paint_Func get_colr_glyph_paint */ + /* TT_Get_Color_Glyph_Paint_Func get_colr_glyph_paint */ + PUT_COLOR_LAYERS_V1( tt_face_get_color_glyph_clipbox ), + /* TT_Get_Color_Glyph_ClipBox_Func get_clipbox */ PUT_COLOR_LAYERS_V1( tt_face_get_paint_layers ), - /* TT_Get_Paint_Layers_Func get_paint_layers */ + /* TT_Get_Paint_Layers_Func get_paint_layers */ PUT_COLOR_LAYERS_V1( tt_face_get_colorline_stops ), - /* TT_Get_Paint get_paint */ + /* TT_Get_Paint get_paint */ PUT_COLOR_LAYERS_V1( tt_face_get_paint ), - /* TT_Get_Colorline_Stops_Func get_colorline_stops */ + /* TT_Get_Colorline_Stops_Func get_colorline_stops */ PUT_COLOR_LAYERS( tt_face_colr_blend_layer ), /* TT_Blend_Colr_Func colr_blend */ diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c index b8f4777ed..2f3e8846d 100644 --- a/src/sfnt/ttcolr.c +++ b/src/sfnt/ttcolr.c @@ -94,6 +94,8 @@ FT_ULong num_layers_v1; FT_Byte* layers_v1; + FT_Byte* clip_list; + /* * Paint tables start at the minimum of the end of the LayerList and the * end of the BaseGlyphList. Record this location in a field here for @@ -134,7 +136,7 @@ FT_ULong base_glyph_offset, layer_offset; FT_ULong base_glyphs_offset_v1, num_base_glyphs_v1; - FT_ULong layer_offset_v1, num_layers_v1; + FT_ULong layer_offset_v1, num_layers_v1, clip_list_offset; FT_ULong table_size; @@ -226,6 +228,16 @@ colr->base_glyphs_v1 + colr->num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE; } + + clip_list_offset = FT_NEXT_ULONG( p ); + + if ( clip_list_offset >= table_size ) + goto InvalidTable; + + if ( clip_list_offset ) + colr->clip_list = (FT_Byte*)( table + clip_list_offset ); + else + colr->clip_list = 0; } colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset ); @@ -796,6 +808,117 @@ } + FT_LOCAL_DEF( FT_Bool ) + tt_face_get_color_glyph_clipbox( TT_Face face, + FT_UInt base_glyph, + FT_ClipBox* clip_box ) + { + Colr* colr; + + FT_Byte *p, *p1, *clip_base; + + FT_Byte clip_list_format; + FT_ULong num_clip_boxes, i; + FT_UShort gid_start, gid_end; + FT_UInt32 clip_box_offset; + FT_Byte format; + + const FT_Byte num_corners = 4; + FT_Vector corners[4]; + FT_Byte j; + FT_BBox font_clip_box; + + + colr = (Colr*)face->colr; + if ( !colr ) + return 0; + + if ( !colr->clip_list ) + return 0; + + p = colr->clip_list; + + clip_base = p; + clip_list_format = FT_NEXT_BYTE ( p ); + + /* Format byte used here to be able to upgrade ClipList for >16bit */ + /* glyph ids; for now we can expect it to be 0. */ + if ( !( clip_list_format == 0 ) ) + return 0; + + num_clip_boxes = FT_NEXT_ULONG( p ); + + for ( i = 0; i < num_clip_boxes; ++i ) + { + gid_start = FT_NEXT_USHORT( p ); + gid_end = FT_NEXT_USHORT( p ); + clip_box_offset = FT_NEXT_UOFF3( p ); + + if ( base_glyph >= gid_start && base_glyph <= gid_end ) + { + p1 = (FT_Byte*)( clip_base + clip_box_offset ); + + if ( p1 >= ( (FT_Byte*)colr->table + colr->table_size ) ) + return 0; + + format = FT_NEXT_BYTE( p1 ); + + if ( format < 0 || format > 1 ) + return 0; + + /* `face->root.size->metrics.x_scale` and `y_scale` are factors */ + /* that scale a font unit value in integers to a 26.6 fixed value */ + /* according to the requested size, see for example */ + /* `ft_recompute_scaled_metrics`. */ + font_clip_box.xMin = FT_MulFix( FT_NEXT_SHORT( p1 ), + face->root.size->metrics.x_scale ); + font_clip_box.yMin = FT_MulFix( FT_NEXT_SHORT( p1 ), + face->root.size->metrics.x_scale ); + font_clip_box.xMax = FT_MulFix( FT_NEXT_SHORT( p1 ), + face->root.size->metrics.x_scale ); + font_clip_box.yMax = FT_MulFix( FT_NEXT_SHORT( p1 ), + face->root.size->metrics.x_scale ); + + /* Make 4 corner points (xMin, yMin), (xMax, yMax) and transform */ + /* them. If we we would only transform two corner points and */ + /* span a rectangle based on those, the rectangle may become too */ + /* small to cover the glyph. */ + corners[0].x = font_clip_box.xMin; + corners[1].x = font_clip_box.xMin; + corners[2].x = font_clip_box.xMax; + corners[3].x = font_clip_box.xMax; + + corners[0].y = font_clip_box.yMin; + corners[1].y = font_clip_box.yMax; + corners[2].y = font_clip_box.yMax; + corners[3].y = font_clip_box.yMin; + + for ( j = 0; j < num_corners; ++j ) + { + if ( face->root.internal->transform_flags & 1 ) + FT_Vector_Transform( &corners[j], + &face->root.internal->transform_matrix ); + + if ( face->root.internal->transform_flags & 2 ) + { + corners[j].x += face->root.internal->transform_delta.x; + corners[j].y += face->root.internal->transform_delta.y; + } + } + + clip_box->bottom_left = corners[0]; + clip_box->top_left = corners[1]; + clip_box->top_right = corners[2]; + clip_box->bottom_right = corners[3]; + + return 1; + } + } + + return 0; + } + + FT_LOCAL_DEF( FT_Bool ) tt_face_get_paint_layers( TT_Face face, FT_LayerIterator* iterator, diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h index c91d2b172..b81e4cb95 100644 --- a/src/sfnt/ttcolr.h +++ b/src/sfnt/ttcolr.h @@ -48,6 +48,11 @@ FT_BEGIN_HEADER FT_Color_Root_Transform root_transform, FT_OpaquePaint* paint ); + FT_LOCAL( FT_Bool ) + tt_face_get_color_glyph_clipbox( TT_Face face, + FT_UInt base_glyph, + FT_ClipBox* clip_box ); + FT_LOCAL( FT_Bool ) tt_face_get_paint_layers( TT_Face face, FT_LayerIterator* iterator,