[truetype] Add support for `avar` table 2.0 format.

See

  https://github.com/harfbuzz/boring-expansion-spec/blob/main/avar2.md

for the specification.

Currently, this is implemented only in most recent OS versions on Apple
platforms and in the HarfBuzz library, but it is expected to be added to the
OpenType standard soon.

* src/truetype/ttgxvar.h (GX_AVarTableRec): New structure.
(GX_BlendRec): Use it to replace `avar_segment` with `avar_table`.

* src/truetype/ttgxvar.c (ft_var_load_avar): Load new table version.
(ft_var_to_normalized, tt_done_blend): Extend for new format.
(ft_var_load_hvvar, ft_var_to_design): Updated.
This commit is contained in:
Behdad Esfahbod 2022-07-23 13:49:27 -06:00 committed by Werner Lemberg
parent dea2e6358b
commit ae4eb996ab
2 changed files with 178 additions and 42 deletions

View File

@ -353,15 +353,21 @@
static void
ft_var_load_avar( TT_Face face )
{
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
FT_Error error;
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
FT_Int i, j;
GX_Blend blend = face->blend;
GX_AVarSegment segment;
FT_Error error;
FT_Long version;
FT_Long axisCount;
FT_Int i, j;
FT_ULong table_len;
GX_AVarTable table;
FT_Long version;
FT_Long axisCount;
FT_ULong table_offset;
FT_ULong table_len;
FT_ULong store_offset;
FT_ULong axisMap_offset;
FT_TRACE2(( "AVAR " ));
@ -374,13 +380,15 @@
return;
}
table_offset = FT_STREAM_POS();
if ( FT_FRAME_ENTER( table_len ) )
return;
version = FT_GET_LONG();
axisCount = FT_GET_LONG();
if ( version != 0x00010000L )
if ( version != 0x00010000L && version != 0x00020000L )
{
FT_TRACE2(( "bad table version\n" ));
goto Exit;
@ -396,10 +404,14 @@
goto Exit;
}
if ( FT_QNEW_ARRAY( blend->avar_segment, axisCount ) )
if ( FT_NEW( blend->avar_table ) )
goto Exit;
table = blend->avar_table;
if ( FT_QNEW_ARRAY( table->avar_segment, axisCount ) )
goto Exit;
segment = &blend->avar_segment[0];
segment = &table->avar_segment[0];
for ( i = 0; i < axisCount; i++, segment++ )
{
FT_TRACE5(( " axis %d:\n", i ));
@ -412,9 +424,9 @@
/* it right now since loading the `avar' table is optional. */
for ( j = i - 1; j >= 0; j-- )
FT_FREE( blend->avar_segment[j].correspondence );
FT_FREE( table->avar_segment[j].correspondence );
FT_FREE( blend->avar_segment );
FT_FREE( table->avar_segment );
goto Exit;
}
@ -433,6 +445,35 @@
FT_TRACE5(( "\n" ));
}
if ( version < 0x00020000L )
goto Exit;
axisMap_offset = FT_GET_ULONG();
store_offset = FT_GET_ULONG();
if ( store_offset )
{
error = tt_var_load_item_variation_store(
face,
table_offset + store_offset,
&table->itemStore );
if ( error )
goto Exit;
}
if ( axisMap_offset )
{
error = tt_var_load_delta_set_index_mapping(
face,
table_offset + axisMap_offset,
&table->axisMap,
&table->itemStore,
table_len );
if ( error )
goto Exit;
}
Exit:
FT_FRAME_EXIT();
}
@ -888,12 +929,15 @@
table = blend->hvar_table;
}
error = tt_var_load_item_variation_store(
face,
table_offset + store_offset,
&table->itemStore );
if ( error )
goto Exit;
if ( store_offset )
{
error = tt_var_load_item_variation_store(
face,
table_offset + store_offset,
&table->itemStore );
if ( error )
goto Exit;
}
if ( widthMap_offset )
{
@ -1924,12 +1968,18 @@
FT_Fixed* coords,
FT_Fixed* normalized )
{
FT_Error error = FT_Err_Ok;
FT_Memory memory = face->root.memory;
FT_UInt i, j;
GX_Blend blend;
FT_MM_Var* mmvar;
FT_UInt i, j;
FT_Var_Axis* a;
GX_AVarSegment av;
FT_Fixed* new_normalized;
FT_Fixed* old_normalized;
blend = face->blend;
mmvar = blend->mmvar;
@ -1980,31 +2030,92 @@
for ( ; i < mmvar->num_axis; i++ )
normalized[i] = 0;
if ( blend->avar_segment )
if ( blend->avar_table )
{
GX_AVarTable table = blend->avar_table;
FT_TRACE5(( "normalized design coordinates"
" before applying `avar' data:\n" ));
av = blend->avar_segment;
for ( i = 0; i < mmvar->num_axis; i++, av++ )
if ( table->avar_segment )
{
for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
{
if ( normalized[i] < av->correspondence[j].fromCoord )
{
FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 ));
av = table->avar_segment;
normalized[i] =
FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
av->correspondence[j].toCoord -
av->correspondence[j - 1].toCoord,
av->correspondence[j].fromCoord -
av->correspondence[j - 1].fromCoord ) +
av->correspondence[j - 1].toCoord;
break;
for ( i = 0; i < mmvar->num_axis; i++, av++ )
{
for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
{
if ( normalized[i] < av->correspondence[j].fromCoord )
{
FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 ));
normalized[i] =
FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
av->correspondence[j].toCoord -
av->correspondence[j - 1].toCoord,
av->correspondence[j].fromCoord -
av->correspondence[j - 1].fromCoord ) +
av->correspondence[j - 1].toCoord;
break;
}
}
}
}
if ( table->itemStore.varData )
{
if ( FT_QNEW_ARRAY( new_normalized, mmvar->num_axis ) )
return;
/* Install our half-normalized coordinates for the next */
/* Item Variation Store to work with. */
old_normalized = face->blend->normalizedcoords;
face->blend->normalizedcoords = normalized;
for ( i = 0; i < mmvar->num_axis; i++ )
{
FT_Fixed v = normalized[i];
FT_UInt innerIndex = i;
FT_UInt outerIndex = 0;
FT_Int delta;
if ( table->axisMap.innerIndex )
{
FT_UInt idx = i;
if ( idx >= table->axisMap.mapCount )
idx = table->axisMap.mapCount - 1;
outerIndex = table->axisMap.outerIndex[idx];
innerIndex = table->axisMap.innerIndex[idx];
}
delta = tt_var_get_item_delta( face,
&table->itemStore,
outerIndex,
innerIndex );
v += delta << 2;
/* Clamp value range. */
v = v >= 0x10000L ? 0x10000 : v;
v = v <= -0x10000L ? -0x10000 : v;
new_normalized[i] = v;
}
for ( i = 0; i < mmvar->num_axis; i++ )
{
normalized[i] = new_normalized[i];
}
face->blend->normalizedcoords = old_normalized;
FT_FREE( new_normalized );
}
}
}
@ -2041,9 +2152,9 @@
for ( ; i < num_coords; i++ )
design[i] = 0;
if ( blend->avar_segment )
if ( blend->avar_table && blend->avar_table->avar_segment )
{
GX_AVarSegment av = blend->avar_segment;
GX_AVarSegment av = blend->avar_table->avar_segment;
FT_TRACE5(( "design coordinates"
@ -4390,11 +4501,19 @@
FT_FREE( blend->normalized_stylecoords );
FT_FREE( blend->mmvar );
if ( blend->avar_segment )
if ( blend->avar_table )
{
for ( i = 0; i < num_axes; i++ )
FT_FREE( blend->avar_segment[i].correspondence );
FT_FREE( blend->avar_segment );
FT_FREE( blend->avar_table->avar_segment[i].correspondence );
FT_FREE( blend->avar_table->avar_segment );
tt_var_done_item_variation_store( face,
&blend->avar_table->itemStore );
tt_var_done_delta_set_index_map( face,
&blend->avar_table->axisMap );
FT_FREE( blend->avar_table );
}
if ( blend->hvar_table )

View File

@ -62,6 +62,23 @@ FT_BEGIN_HEADER
} GX_AVarSegmentRec, *GX_AVarSegment;
/**************************************************************************
*
* @Struct:
* GX_AVarTableRec
*
* @Description:
* Data from the `avar' table.
*/
typedef struct GX_AVarTableRec_
{
GX_AVarSegment avar_segment; /* avar_segment[num_axis] */
GX_ItemVarStoreRec itemStore; /* Item Variation Store */
GX_DeltaSetIdxMapRec axisMap; /* Axis Mapping */
} GX_AVarTableRec, *GX_AVarTable;
/**************************************************************************
*
* @Struct:
@ -194,7 +211,7 @@ FT_BEGIN_HEADER
* A Boolean; if set, FreeType tried to load (and parse) the `avar'
* table.
*
* avar_segment ::
* avar_table ::
* Data from the `avar' table.
*
* hvar_loaded ::
@ -259,7 +276,7 @@ FT_BEGIN_HEADER
/* normalized_stylecoords[num_namedstyles][num_axis] */
FT_Bool avar_loaded;
GX_AVarSegment avar_segment; /* avar_segment[num_axis] */
GX_AVarTable avar_table;
FT_Bool hvar_loaded;
FT_Bool hvar_checked;