diff --git a/ChangeLog b/ChangeLog index 871594e46..c4c3b3a85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2020-12-16 Dominik Röttsches + + [sfnt] Add API to retrieve 'COLR' v1 root paint (#59703). + + * src/sfnt/ttcolr.c (BaseGlyphV1Record): New structure. + (tt_face_load_colr): Handle version 1 table header. + (find_base_glyph_v1_record): New auxiliary function. + (tt_face_get_colr_glyph_paint): New function to find the root + `FT_OpaquePaint` object for a given glyph ID. + + * src/sfnt/ttcolr.h: Updated. + 2020-12-16 Dominik Röttsches Add new methods required for 'COLR' v1 to public API (#59703). diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c index 9025e356c..9b05b3a3b 100644 --- a/src/sfnt/ttcolr.c +++ b/src/sfnt/ttcolr.c @@ -5,7 +5,7 @@ * TrueType and OpenType colored glyph layer support (body). * * Copyright (C) 2018-2020 by - * David Turner, Robert Wilhelm, and Werner Lemberg. + * David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. * * Originally written by Shao Yu Zhang . * @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef TT_CONFIG_OPTION_COLOR_LAYERS @@ -39,12 +40,16 @@ /* NOTE: These are the table sizes calculated through the specs. */ -#define BASE_GLYPH_SIZE 6U -#define LAYER_SIZE 4U -#define COLR_HEADER_SIZE 14U +#define BASE_GLYPH_SIZE 6U +#define BASE_GLYPH_V1_RECORD_SIZE 6U +#define LAYER_V1_LIST_PAINT_OFFSET_SIZE 4U +#define LAYER_V1_LIST_NUM_LAYERS_SIZE 4U +#define COLOR_STOP_SIZE 6U +#define LAYER_SIZE 4U +#define COLR_HEADER_SIZE 14U - typedef struct BaseGlyphRecord_ + typedef struct BaseGlyphRecord_ { FT_UShort gid; FT_UShort first_layer_index; @@ -53,7 +58,16 @@ } BaseGlyphRecord; - typedef struct Colr_ + typedef struct BaseGlyphV1Record_ + { + FT_UShort gid; + /* Offset from start of BaseGlyphV1List, i.e., from base_glyphs_v1. */ + FT_ULong paint_offset; + + } BaseGlyphV1Record; + + + typedef struct Colr_ { FT_UShort version; FT_UShort num_base_glyphs; @@ -62,7 +76,14 @@ FT_Byte* base_glyphs; FT_Byte* layers; - /* The memory which backs up the `COLR' table. */ + FT_ULong num_base_glyphs_v1; + /* Points at beginning of BaseGlyphV1List. */ + FT_Byte* base_glyphs_v1; + + FT_ULong num_layers_v1; + FT_Byte* layers_v1; + + /* The memory that backs up the `COLR' table. */ void* table; FT_ULong table_size; @@ -88,10 +109,14 @@ FT_Byte* table = NULL; FT_Byte* p = NULL; + /* Needed for reading array lengths in referenced tables. */ + FT_Byte* p1 = NULL; Colr* colr = NULL; 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 table_size; @@ -115,7 +140,7 @@ goto NoColr; colr->version = FT_NEXT_USHORT( p ); - if ( colr->version != 0 ) + if ( colr->version != 0 && colr->version != 1 ) goto InvalidTable; colr->num_base_glyphs = FT_NEXT_USHORT( p ); @@ -135,6 +160,35 @@ if ( colr->num_layers * LAYER_SIZE > table_size - layer_offset ) goto InvalidTable; + if ( colr->version == 1 ) + { + base_glyphs_offset_v1 = FT_NEXT_ULONG( p ); + + if ( base_glyphs_offset_v1 >= table_size ) + goto InvalidTable; + + p1 = (FT_Byte*)( table + base_glyphs_offset_v1 ); + num_base_glyphs_v1 = FT_PEEK_ULONG( p1 ); + + if ( num_base_glyphs_v1 * BASE_GLYPH_V1_RECORD_SIZE > + table_size - base_glyphs_offset_v1 ) + goto InvalidTable; + + colr->num_base_glyphs_v1 = num_base_glyphs_v1; + colr->base_glyphs_v1 = p1; + + layer_offset_v1 = FT_NEXT_ULONG( p ); + + if ( !layer_offset_v1 || layer_offset_v1 >= table_size ) + goto InvalidTable; + + p1 = (FT_Byte*)( table + layer_offset_v1 ); + num_layers_v1 = FT_PEEK_ULONG( p1 ); + + colr->num_layers_v1 = num_layers_v1; + colr->layers_v1 = p1; + } + colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset ); colr->layers = (FT_Byte*)( table + layer_offset ); colr->table = table; @@ -265,6 +319,85 @@ } + static FT_Bool + find_base_glyph_v1_record ( FT_Byte * base_glyph_begin, + FT_Int num_base_glyph, + FT_UInt glyph_id, + BaseGlyphV1Record *record ) + { + FT_Int min = 0; + FT_Int max = num_base_glyph - 1; + + + while ( min <= max ) + { + FT_Int mid = min + ( max - min ) / 2; + + /* + * `base_glyph_begin` is the beginning of `BaseGlyphV1List`; + * skip `numBaseGlyphV1Records` by adding 4 to start binary search + * in the array of `BaseGlyphV1Record`. + */ + FT_Byte *p = base_glyph_begin + 4 + mid * BASE_GLYPH_V1_RECORD_SIZE; + + FT_UShort gid = FT_NEXT_USHORT( p ); + + + if ( gid < glyph_id ) + min = mid + 1; + else if (gid > glyph_id ) + max = mid - 1; + else + { + record->gid = gid; + record->paint_offset = FT_NEXT_ULONG ( p ); + return 1; + } + } + + return 0; + } + + + FT_LOCAL_DEF ( FT_Bool ) + tt_face_get_colr_glyph_paint( TT_Face face, + FT_UInt base_glyph, + FT_OpaquePaint* opaque_paint ) + { + Colr* colr = (Colr*)face->colr; + + BaseGlyphV1Record base_glyph_v1_record; + FT_Byte* p; + + + if ( colr->version < 1 || !colr->num_base_glyphs_v1 || + !colr->base_glyphs_v1 ) + return 0; + + if ( opaque_paint->p ) + return 0; + + if ( !find_base_glyph_v1_record( colr->base_glyphs_v1, + colr->num_base_glyphs_v1, + base_glyph, + &base_glyph_v1_record ) ) + return 0; + + if ( !base_glyph_v1_record.paint_offset || + base_glyph_v1_record.paint_offset > colr->table_size ) + return 0; + + p = (FT_Byte*)( colr->base_glyphs_v1 + + base_glyph_v1_record.paint_offset ); + if ( p >= ( (FT_Byte*)colr->table + colr->table_size ) ) + return 0; + + opaque_paint->p = p; + + return 1; + } + + FT_LOCAL_DEF( FT_Error ) tt_face_colr_blend_layer( TT_Face face, FT_UInt color_index, diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h index 641216266..6cce50fb8 100644 --- a/src/sfnt/ttcolr.h +++ b/src/sfnt/ttcolr.h @@ -42,6 +42,11 @@ FT_BEGIN_HEADER FT_UInt *acolor_index, FT_LayerIterator* iterator ); + FT_LOCAL( FT_Bool ) + tt_face_get_colr_glyph_paint( TT_Face face, + FT_UInt base_glyph, + FT_OpaquePaint* paint ); + FT_LOCAL( FT_Error ) tt_face_colr_blend_layer( TT_Face face, FT_UInt color_index,