Skip to content

Commit

Permalink
[sfnt] Add API for retrieving a 'COLR' v1 'ClipBox' table.
Browse files Browse the repository at this point in the history
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

  googlefonts/colr-gradients-spec#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.
  • Loading branch information
drott authored and lemzwerg committed Aug 7, 2021
1 parent 6be8bfe commit 47cf8eb
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 16 deletions.
86 changes: 86 additions & 0 deletions include/freetype/ftcolor.h
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
63 changes: 52 additions & 11 deletions include/freetype/internal/sfnt.h
Expand Up @@ -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:
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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_, \
Expand Down Expand Up @@ -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_, \
Expand Down
29 changes: 29 additions & 0 deletions src/base/ftobjs.c
Expand Up @@ -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 )
Expand Down
10 changes: 6 additions & 4 deletions src/sfnt/sfdriver.c
Expand Up @@ -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 */
Expand Down
125 changes: 124 additions & 1 deletion src/sfnt/ttcolr.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;


Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 47cf8eb

Please sign in to comment.