diff --git a/ChangeLog b/ChangeLog index 4bd8e5ed2..3ccfdf5d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,57 @@ +2018-05-13 Shao Yu Zhang + Werner Lemberg + + [sfnt] Preliminary support of coloured layer outlines. + + This commit enables OpenType's COLR/CPAL table handling; a typical + application are color emojis that can be scaled to any size. + + If the color palette does not exist or is invalid, the rendering + step rasterizes the outline instead. The current implementation + assumes that the foreground is black. + + Enable this by defining option TT_CONFIG_OPTION_COLOR_LAYERS. + + There are still some issues with metrics; additionally, an API to + fetch color layers is missing. + + * devel/ftoption.h, include/freetype/config/ftoption.h + (TT_CONFIG_OPTION_COLOR_LAYERS): New macro. + + * include/freetype/internal/ftobjs.h (FT_Glyph_LayerRec, + FT_Colr_InternalRec): New structures. + (FT_Slot_InternalRec): Add `color_layers' field. + + * include/freetype/internal/sfnt.h (TT_Load_Colr_Layer_Func, + TT_Blend_Colr_Func): New function types. + (SFNT_Interface): Add `load_colr', `free_colr', `load_colr_layer', + and `colr_blend' fields. + + * include/freetype/internal/tttypes.h (TT_FaceRec): Add + `colr_and_cpal' field. + + * include/freetype/internal/tttags. (TTAG_COLR, TTAG_CPAL): New + macros. + + * src/sfnt/ttcolr.c, src/sfnt/ttcolr.h: New files. + + * src/base/ftobjs.c (ft_glyphslot_done, FT_Render_Glyph_Internal): + Handle glyph color layers. + + * src/sfnt/Jamfile (_sources), src/sfnt/rules.mk (SFNT_DRV_SRC): Add + `ttcolr.c'. + + * src/sfnt/sfdriver.c: Include `ttcolr.h'. + (PUT_COLOR_LAYERS): New macro. + Update call to `FT_DEFINE_SFNT_INTERFACE'. + + * src/sfnt/sfnt.c: Include `ttcolr.c'. + + * src/sfnt/sfobjs.c (sfnt_load_face): Load `COLR' and `CPAL' tables. + (sfnt_done_face): Updated. + + * src/truetype/ttgload.c (TT_Load_Glyph): Handle color layers. + 2018-05-12 Arkady Shapkin Use MS VC++'s _BitScanReverse to calculate MSB (patch #9636). diff --git a/devel/ftoption.h b/devel/ftoption.h index 84933a0c7..2bc2a76eb 100644 --- a/devel/ftoption.h +++ b/devel/ftoption.h @@ -485,6 +485,15 @@ FT_BEGIN_HEADER #define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COLOR_LAYERS if you want to support coloured */ + /* outlines (from the COLR/CPAL tables) in all formats using the SFNT */ + /* module (namely TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_COLOR_LAYERS + + /*************************************************************************/ /* */ /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index 1cf694668..3b6603b39 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -504,6 +504,15 @@ FT_BEGIN_HEADER #define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COLOR_LAYERS if you want to support coloured */ + /* outlines (from the COLR/CPAL tables) in all formats using the SFNT */ + /* module (namely TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_COLOR_LAYERS + + /*************************************************************************/ /* */ /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index af1e17163..301388337 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -384,6 +384,23 @@ FT_BEGIN_HEADER } FT_Face_InternalRec; + typedef struct FT_Glyph_LayerRec_ + { + FT_UShort glyph_index; + FT_UShort color_index; + + } FT_Glyph_LayerRec; + + + typedef struct FT_Colr_InternalRec_ + { + FT_Glyph_LayerRec* layers; + FT_UShort num_layers; + FT_Int load_flags; + + } FT_Colr_InternalRec, *FT_Colr_Internal; + + /*************************************************************************/ /* */ /* */ @@ -417,6 +434,8 @@ FT_BEGIN_HEADER /* */ /* glyph_hints :: Format-specific glyph hints management. */ /* */ + /* color_layers :: Data from (SFNT) COLR/CPAL tables. */ + /* */ #define FT_GLYPH_OWN_BITMAP 0x1U @@ -429,6 +448,8 @@ FT_BEGIN_HEADER FT_Vector glyph_delta; void* glyph_hints; + FT_Colr_Internal color_layers; + } FT_GlyphSlot_InternalRec; diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index 73f16ba4f..f543535f0 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -428,6 +428,68 @@ FT_BEGIN_HEADER FT_UShort* aadvance ); + /*************************************************************************/ + /* */ + /* */ + /* TT_Load_Colr_Layer_Func */ + /* */ + /* */ + /* Load the color layer data given a glyph index. */ + /* */ + /* */ + /* face :: The target face object. */ + /* */ + /* idx :: The glyph index. */ + /* */ + /* */ + /* layers :: The layer info with color index and glyph index. */ + /* Deallocate with `FT_FREE'. */ + /* */ + /* num_layers :: Number of layers. */ + /* */ + /* */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* color layer information exists for `idx'. */ + /* */ + typedef FT_Error + (*TT_Load_Colr_Layer_Func)( TT_Face face, + FT_Int idx, + FT_Glyph_LayerRec* *layers, + FT_UShort* num_layers ); + + + /*************************************************************************/ + /* */ + /* */ + /* TT_Blend_Colr_Func */ + /* */ + /* */ + /* Blend the bitmap in `new_glyph' into `base_glyph' using the color */ + /* specified by `color_index'. */ + /* */ + /* XXX: Handle foregound color */ + /* */ + /* */ + /* face :: The target face object. */ + /* */ + /* color_index :: Color index from the COLR table. */ + /* */ + /* base_glyph :: Slot for bitmap to be merged into. The underlying */ + /* bitmap may get reallocated. */ + /* */ + /* new_glyph :: Slot to be incooperated into `base_glyph'. */ + /* */ + /* */ + /* FreeType error code. 0 means success. Returns an error if */ + /* color_index is invalid or reallocation fails. */ + /* */ + typedef FT_Error + (*TT_Blend_Colr_Func)( TT_Face face, + FT_Int color_index, + FT_GlyphSlot base_glyph, + FT_GlyphSlot new_glyph ); + + /*************************************************************************/ /* */ /* */ @@ -616,6 +678,11 @@ FT_BEGIN_HEADER TT_Set_SBit_Strike_Func set_sbit_strike; TT_Load_Strike_Metrics_Func load_strike_metrics; + TT_Load_Table_Func load_colr; + TT_Free_Table_Func free_colr; + TT_Load_Colr_Layer_Func load_colr_layer; + TT_Blend_Colr_Func colr_blend; + TT_Get_Metrics_Func get_metrics; TT_Get_Name_Func get_name; @@ -658,6 +725,10 @@ FT_BEGIN_HEADER free_eblc_, \ set_sbit_strike_, \ load_strike_metrics_, \ + load_colr_, \ + free_colr_, \ + load_colr_layer_, \ + colr_blend_, \ get_metrics_, \ get_name_, \ get_name_id_ ) \ @@ -691,6 +762,10 @@ FT_BEGIN_HEADER free_eblc_, \ set_sbit_strike_, \ load_strike_metrics_, \ + load_colr_, \ + free_colr_, \ + load_colr_layer_, \ + colr_blend_, \ get_metrics_, \ get_name_, \ get_name_id_ \ diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 10dd336a8..072636eca 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -1352,6 +1352,10 @@ FT_BEGIN_HEADER /* exposed by the API and the indices used in */ /* the font's sbit table. */ /* */ + /* colr_and_cpal :: A pointer to data related to `COLR' and */ + /* `CPAL' tables. NULL if tables are not */ + /* available. */ + /* */ /* kern_table :: A pointer to the `kern' table. */ /* */ /* kern_table_size :: The size of the `kern' table. */ @@ -1535,6 +1539,7 @@ FT_BEGIN_HEADER FT_UInt sbit_num_strikes; FT_UInt* sbit_strike_map; + void* colr_and_cpal; FT_Byte* kern_table; FT_ULong kern_table_size; FT_UInt num_kern_tables; diff --git a/include/freetype/tttags.h b/include/freetype/tttags.h index e5cee68a1..36b02057a 100644 --- a/include/freetype/tttags.h +++ b/include/freetype/tttags.h @@ -46,6 +46,8 @@ FT_BEGIN_HEADER #define TTAG_CFF2 FT_MAKE_TAG( 'C', 'F', 'F', '2' ) #define TTAG_CID FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) #define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) +#define TTAG_COLR FT_MAKE_TAG( 'C', 'O', 'L', 'R' ) +#define TTAG_CPAL FT_MAKE_TAG( 'C', 'P', 'A', 'L' ) #define TTAG_cvar FT_MAKE_TAG( 'c', 'v', 'a', 'r' ) #define TTAG_cvt FT_MAKE_TAG( 'c', 'v', 't', ' ' ) #define TTAG_DSIG FT_MAKE_TAG( 'D', 'S', 'I', 'G' ) diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index 6629cbd0c..3696c8175 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -538,6 +538,16 @@ /* free bitmap buffer if needed */ ft_glyphslot_free_bitmap( slot ); + /* free glyph color layers if needed */ + if ( slot->internal->color_layers ) + { + FT_Colr_InternalRec* color_layers = slot->internal->color_layers; + + + FT_FREE( color_layers->layers ); + FT_FREE( slot->internal->color_layers ); + } + /* slot->internal might be NULL in out-of-memory situations */ if ( slot->internal ) { @@ -4497,6 +4507,61 @@ break; default: + if ( slot->internal->color_layers != NULL ) + { + FT_Face face = slot->face; + + + error = FT_New_GlyphSlot( face, NULL ); + if ( !error ) + { + TT_Face ttface = (TT_Face)face; + SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; + + FT_Glyph_LayerRec* glyph_layers = + slot->internal->color_layers->layers; + + FT_Int idx; + + + for ( idx = 0; + idx < slot->internal->color_layers->num_layers; + idx++ ) + { + FT_Int load_flags; + + + load_flags = slot->internal->color_layers->load_flags + & ~FT_LOAD_COLOR; + load_flags |= FT_LOAD_RENDER; + + error = FT_Load_Glyph( face, + glyph_layers[idx].glyph_index, + load_flags ); + if ( error ) + break; + + error = sfnt->colr_blend( ttface, + glyph_layers[idx].color_index, + slot, + face->glyph ); + if ( error ) + break; + } + + if ( !error ) + slot->format = FT_GLYPH_FORMAT_BITMAP; + + FT_Done_GlyphSlot( face->glyph ); + } + + if ( !error ) + return error; + + /* Failed to do the colored layer. Draw outline instead. */ + slot->format = FT_GLYPH_FORMAT_OUTLINE; + } + { FT_ListNode node = NULL; diff --git a/src/sfnt/Jamfile b/src/sfnt/Jamfile index 57977fc96..5e8e78902 100644 --- a/src/sfnt/Jamfile +++ b/src/sfnt/Jamfile @@ -27,6 +27,7 @@ SubDir FT2_TOP $(FT2_SRC_DIR) sfnt ; ttmtx ttpost ttsbit + ttcolr ; } else diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk index e93c37e2a..f6e062b07 100644 --- a/src/sfnt/rules.mk +++ b/src/sfnt/rules.mk @@ -32,6 +32,7 @@ SFNT_DRV_SRC := $(SFNT_DIR)/ttload.c \ $(SFNT_DIR)/ttmtx.c \ $(SFNT_DIR)/ttcmap.c \ $(SFNT_DIR)/ttsbit.c \ + $(SFNT_DIR)/ttcolr.c \ $(SFNT_DIR)/ttpost.c \ $(SFNT_DIR)/ttkern.c \ $(SFNT_DIR)/ttbdf.c \ diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index 0fcc72cab..40b56d599 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -32,6 +32,10 @@ #include "ttsbit.h" #endif +#ifdef TT_CONFIG_OPTION_COLOR_LAYERS +#include "ttcolr.h" +#endif + #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES #include "ttpost.h" #endif @@ -1185,6 +1189,12 @@ #define PUT_EMBEDDED_BITMAPS( a ) NULL #endif +#ifdef TT_CONFIG_OPTION_COLOR_LAYERS +#define PUT_COLOR_LAYERS( a ) a +#else +#define PUT_COLOR_LAYERS( a ) NULL +#endif + #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES #define PUT_PS_NAMES( a ) a #else @@ -1243,9 +1253,18 @@ /* TT_Free_Table_Func free_eblc */ PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike ), - /* TT_Set_SBit_Strike_Func set_sbit_strike */ + /* TT_Set_SBit_Strike_Func set_sbit_strike */ PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ), - /* TT_Load_Strike_Metrics_Func load_strike_metrics */ + /* TT_Load_Strike_Metrics_Func load_strike_metrics */ + + PUT_COLOR_LAYERS( tt_face_load_colr ), + /* TT_Load_Table_Func load_colr */ + PUT_COLOR_LAYERS( tt_face_free_colr ), + /* TT_Free_Table_Func free_colr */ + PUT_COLOR_LAYERS( tt_face_load_colr_layers ), + /* TT_Load_Colr_Layer_Func load_colr_layer */ + PUT_COLOR_LAYERS( tt_face_colr_blend_layer ), + /* TT_Blend_Colr_Func colr_blend */ tt_face_get_metrics, /* TT_Get_Metrics_Func get_metrics */ diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c index f29839acd..baf87a75f 100644 --- a/src/sfnt/sfnt.c +++ b/src/sfnt/sfnt.c @@ -24,6 +24,8 @@ #include "sfobjs.c" #include "ttbdf.c" #include "ttcmap.c" +#include "ttcolr.c" + #include "ttkern.c" #include "ttload.c" #include "ttmtx.c" diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 6ba8509f5..d8734b042 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -1341,6 +1341,12 @@ if ( sfnt->load_eblc ) LOAD_( eblc ); + if ( sfnt->load_colr ) + { + /* Ignore error. Missing optional colr/cpal is okay. */ + LOAD_( colr ); + } + /* consider the pclt, kerning, and gasp tables as optional */ LOAD_( pclt ); LOAD_( gasp ); @@ -1394,7 +1400,8 @@ /* Compute face flags. */ /* */ if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_CBLC || - face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX ) + face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX || + face->colr_and_cpal ) flags |= FT_FACE_FLAG_COLOR; /* color glyphs */ if ( has_outline == TRUE ) @@ -1737,6 +1744,10 @@ /* destroy the embedded bitmaps table if it is loaded */ if ( sfnt->free_eblc ) sfnt->free_eblc( face ); + + /* destroy color table data if it is loaded */ + if ( sfnt->free_colr ) + sfnt->free_colr( face ); } #ifdef TT_CONFIG_OPTION_BDF diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c new file mode 100644 index 000000000..58499d32e --- /dev/null +++ b/src/sfnt/ttcolr.c @@ -0,0 +1,546 @@ +/***************************************************************************/ +/* */ +/* ttcolr.c */ +/* */ +/* TrueType and OpenType color outline support. */ +/* */ +/* Copyright 2018 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Written by Shao Yu Zhang . */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* `COLR' and `CPAL' table specification: */ + /* */ + /* https://www.microsoft.com/typography/otspec/colr.htm */ + /* https://www.microsoft.com/typography/otspec/cpal.htm */ + /* */ + /*************************************************************************/ + + +#include +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H + + +#ifdef TT_CONFIG_OPTION_COLOR_LAYERS + +#include "ttcolr.h" + + + /* NOTE: These are the table sizes calculated through the specs. */ +#define BASE_GLYPH_SIZE 6 +#define LAYER_SIZE 4 +#define COLR_HEADER_SIZE 14 +#define CPAL_V0_HEADER_BASE_SIZE 12 +#define COLOR_SIZE 4 + + + typedef struct BaseGlyphRecord_ + { + FT_UShort gid; + FT_UShort first_layer_index; + FT_UShort num_layers; + + } BaseGlyphRecord; + + + typedef struct Colr_ + { + FT_UShort version; + FT_UShort num_base_glyphs; + FT_UShort num_layers; + + FT_Byte* base_glyphs; + FT_Byte* layers; + + } Colr; + + + typedef struct Cpal_ + { + FT_UShort version; /* Table version number (0 or 1 supported). */ + FT_UShort num_palettes_entries; /* # of entries in each palette. */ + FT_UShort num_palettes; /* # of palettes in the table. */ + FT_UShort num_colors; /* Total number of color records, */ + /* combined for all palettes. */ + FT_Byte* colors; /* RGBA array of colors */ + FT_Byte* color_indices; /* Index of each palette's first color record */ + /* in the combined color record array. */ + + /* version 1 fields */ + FT_ULong* palette_types; + FT_UShort* palette_labels; + FT_UShort* palette_entry_labels; + + } Cpal; + + + typedef struct ColrCpal_ + { + /* Accessors into the colr/cpal tables. */ + Colr colr; + Cpal cpal; + + /* The memory which backs up colr/cpal tables. */ + void* colr_table; + void* cpal_table; + + } ColrCpal; + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttcolrcpal + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_colr( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = face->root.memory; + + FT_Byte* colr_table = NULL; + FT_Byte* cpal_table = NULL; + FT_Byte* p = NULL; + + Colr colr; + Cpal cpal; + ColrCpal* cc = NULL; + + FT_ULong base_glyph_begin, base_glyph_end, layer_begin, layer_end; + FT_ULong colors_begin, colors_end; + FT_ULong table_size; + + + face->colr_and_cpal = NULL; + + error = face->goto_table( face, TTAG_COLR, stream, &table_size ); + if ( error ) + goto NoColor; + + if ( table_size < sizeof ( COLR_HEADER_SIZE ) ) + goto InvalidTable; + + if ( FT_FRAME_EXTRACT( table_size, colr_table ) ) + goto NoColor; + + p = colr_table; + + FT_ZERO( &colr ); + colr.version = FT_NEXT_USHORT( p ); + colr.num_base_glyphs = FT_NEXT_USHORT( p ); + + base_glyph_begin = FT_NEXT_ULONG( p ); + layer_begin = FT_NEXT_ULONG( p ); + + colr.num_layers = FT_NEXT_USHORT( p ); + colr.base_glyphs = (FT_Byte*)( colr_table + base_glyph_begin ); + colr.layers = (FT_Byte*)( colr_table + layer_begin ); + + if ( colr.version != 0 ) + goto InvalidTable; + + /* Ensure variable length tables lies within the COLR table. */ + /* We wrap around FT_ULong at most once since count is FT_UShort. */ + + base_glyph_end = base_glyph_begin + + colr.num_base_glyphs * BASE_GLYPH_SIZE; + layer_end = layer_begin + + colr.num_layers * LAYER_SIZE; + if ( base_glyph_end < base_glyph_begin || base_glyph_end > table_size || + layer_end < layer_begin || layer_end > table_size ) + goto InvalidTable; + + /* Ensure pointers don't wrap. */ + if ( colr.base_glyphs < colr_table || colr.layers < colr_table ) + goto InvalidTable; + + error = face->goto_table( face, TTAG_CPAL, stream, &table_size ); + if ( error ) + goto NoColor; + + if ( table_size < CPAL_V0_HEADER_BASE_SIZE ) + goto InvalidTable; + + if ( FT_FRAME_EXTRACT( table_size, cpal_table ) ) + goto NoColor; + + p = cpal_table; + + FT_ZERO( &cpal ); + cpal.version = FT_NEXT_USHORT( p ); + cpal.num_palettes_entries = FT_NEXT_USHORT( p ); + cpal.num_palettes = FT_NEXT_USHORT( p ); + cpal.num_colors = FT_NEXT_USHORT( p ); + + colors_begin = FT_NEXT_ULONG( p ); + + cpal.color_indices = p; + cpal.colors = (FT_Byte*)cpal_table + colors_begin; + + if ( cpal.version != 0 && cpal.version != 1 ) + goto InvalidTable; + + colors_end = colors_begin + cpal.num_colors * COLOR_SIZE; + + /* Ensure variable length tables lies within the COLR table. */ + /* We wrap around FT_ULong at most once since count is FT_UShort. */ + if ( colors_end < colors_begin || colors_end > table_size ) + goto InvalidTable; + + if ( cpal.colors < cpal_table ) + goto InvalidTable; + + if ( FT_NEW( cc ) ) + goto NoColor; + + cc->colr = colr; + cc->cpal = cpal; + cc->colr_table = colr_table; + cc->cpal_table = cpal_table; + + face->colr_and_cpal = cc; + + return FT_Err_Ok; + + InvalidTable: + error = FT_THROW( Invalid_File_Format ); + + NoColor: + if ( colr_table ) + FT_FRAME_RELEASE( colr_table ); + if ( cpal_table ) + FT_FRAME_RELEASE( cpal_table ); + + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_colr( TT_Face face ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = face->root.memory; + + ColrCpal* colr_and_cpal = (ColrCpal*)face->colr_and_cpal; + + + if ( colr_and_cpal ) + { + FT_FRAME_RELEASE( colr_and_cpal->colr_table ); + FT_FRAME_RELEASE( colr_and_cpal->cpal_table ); + + FT_FREE( face->colr_and_cpal ); + } + } + + + static FT_Bool + find_base_glyph_record( FT_Byte* base_glyph_begin, + FT_Int num_base_glyph, + FT_UShort glyph_id, + BaseGlyphRecord* record ) + { + FT_Int min = 0; + FT_Int max = num_base_glyph - 1; + + + while ( min <= max ) + { + FT_Int mid = min + ( max - min ) / 2; + FT_Byte* p = base_glyph_begin + mid * BASE_GLYPH_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->first_layer_index = FT_NEXT_USHORT( p ); + record->num_layers = FT_NEXT_USHORT( p ); + + return 1; + } + } + + return 0; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_colr_layers( TT_Face face, + FT_Int glyph_id, + FT_Glyph_LayerRec* *ret_layers, + FT_UShort* ret_num_layers ) + { + FT_Error error; + FT_Memory memory = face->root.memory; + + ColrCpal* colr_and_cpal = (ColrCpal *)face->colr_and_cpal; + Colr* colr = &colr_and_cpal->colr; + Cpal* cpal = &colr_and_cpal->cpal; + + BaseGlyphRecord glyph_record; + FT_Glyph_LayerRec* layers; + int layer_idx; + FT_Byte* layer_record_ptr; + + + if ( !ret_layers || !ret_num_layers ) + return FT_THROW( Invalid_Argument ); + + if ( !find_base_glyph_record( colr->base_glyphs, + colr->num_base_glyphs, + glyph_id, + &glyph_record ) ) + return FT_THROW ( Invalid_Table ); + + /* Load all colors for the glyphs; this would be stored in the slot. */ + layer_record_ptr = colr->layers + + glyph_record.first_layer_index * LAYER_SIZE; + + if ( FT_NEW_ARRAY( layers, glyph_record.num_layers ) ) + goto Error; + + for ( layer_idx = 0; layer_idx < glyph_record.num_layers; layer_idx++ ) + { + FT_UShort gid = FT_NEXT_USHORT( layer_record_ptr ); + FT_UShort palette_index = FT_NEXT_USHORT( layer_record_ptr ); + + + if ( palette_index != 0xFFFF && + palette_index >= cpal->num_palettes_entries ) + { + error = FT_THROW( Invalid_File_Format ); + goto Error; + } + + layers[layer_idx].color_index = palette_index; + layers[layer_idx].glyph_index = gid; + } + + *ret_layers = layers; + *ret_num_layers = glyph_record.num_layers; + + return FT_Err_Ok; + + Error: + if ( layers ) + FT_FREE( layers ); + + return error; + } + + + static FT_Bool + tt_face_find_color( TT_Face face, + FT_UShort color_index, + FT_Byte* blue, + FT_Byte* green, + FT_Byte* red, + FT_Byte* alpha ) + { + ColrCpal* colr_and_cpal = (ColrCpal *)face->colr_and_cpal; + Cpal* cpal = &colr_and_cpal->cpal; + + FT_Int palette_index = 0; + FT_Byte* p; + FT_Int color_offset; + + + if ( color_index >= cpal->num_palettes_entries ) + return 0; + + p = cpal->color_indices + palette_index * sizeof ( FT_UShort ); + + color_offset = FT_NEXT_USHORT( p ); + + p = cpal->colors + color_offset + COLOR_SIZE * color_index; + + *red = FT_NEXT_BYTE( p ); + *green = FT_NEXT_BYTE( p ); + *blue = FT_NEXT_BYTE( p ); + *alpha = FT_NEXT_BYTE( p ); + + return 1; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_colr_blend_layer( TT_Face face, + FT_Int color_index, + FT_GlyphSlot dstSlot, + FT_GlyphSlot srcSlot ) + { + FT_Error error; + + FT_UInt x, y; + FT_Byte b, g, r, alpha; + + FT_Long size; + FT_Byte* src; + FT_Byte* dst; + + + if ( !dstSlot->bitmap.buffer ) + { + /* Initialize destination of color bitmap */ + /* with the size of first component. */ + dstSlot->bitmap_left = srcSlot->bitmap_left; + dstSlot->bitmap_top = srcSlot->bitmap_top; + + dstSlot->bitmap.width = srcSlot->bitmap.width; + dstSlot->bitmap.rows = srcSlot->bitmap.rows; + dstSlot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + dstSlot->bitmap.pitch = dstSlot->bitmap.width * 4; + dstSlot->bitmap.num_grays = 256; + + size = dstSlot->bitmap.rows * dstSlot->bitmap.pitch; + + error = ft_glyphslot_alloc_bitmap( dstSlot, size ); + if ( error ) + return error; + + FT_MEM_ZERO( dstSlot->bitmap.buffer, size ); + } + else + { + /* Resize destination if needed such that new component fits. */ + FT_Int x_min, x_max, y_min, y_max; + + + x_min = FT_MIN( dstSlot->bitmap_left, srcSlot->bitmap_left ); + x_max = FT_MAX( dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width, + srcSlot->bitmap_left + (FT_Int)srcSlot->bitmap.width ); + + y_min = FT_MIN( dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows, + srcSlot->bitmap_top - (FT_Int)srcSlot->bitmap.rows ); + y_max = FT_MAX( dstSlot->bitmap_top, srcSlot->bitmap_top ); + + if ( x_min != dstSlot->bitmap_left || + x_max != dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width || + y_min != dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows || + y_max != dstSlot->bitmap_top ) + { + FT_Memory memory = face->root.memory; + + FT_UInt width = x_max - x_min; + FT_UInt rows = y_max - y_min; + FT_UInt pitch = width * 4; + + FT_Byte* buf; + FT_Byte* p; + FT_Byte* q; + + + size = rows * pitch; + if ( FT_ALLOC( buf, size ) ) + return error; + + p = dstSlot->bitmap.buffer; + q = buf + + pitch * ( y_max - dstSlot->bitmap_top ) + + 4 * ( dstSlot->bitmap_left - x_min ); + + for ( y = 0; y < dstSlot->bitmap.rows; y++ ) + { + FT_MEM_COPY( q, p, dstSlot->bitmap.width * 4 ); + + p += dstSlot->bitmap.pitch; + q += pitch; + } + + ft_glyphslot_set_bitmap( dstSlot, buf ); + + dstSlot->bitmap_top = y_max; + dstSlot->bitmap_left = x_min; + + dstSlot->bitmap.width = width; + dstSlot->bitmap.rows = rows; + dstSlot->bitmap.pitch = pitch; + + dstSlot->internal->flags |= FT_GLYPH_OWN_BITMAP; + dstSlot->format = FT_GLYPH_FORMAT_BITMAP; + } + } + + /* Default assignments to pacify compiler. */ + r = g = b = 0; + alpha = 255; + + if ( color_index != 0xFFFF ) + tt_face_find_color( face, color_index, &b, &g, &r, &alpha ); + else + { + /* TODO. foreground color from argument? */ + /* Add public FT_Render_Glyph_Color() with color value? */ + } + + /* XXX Convert if srcSlot.bitmap is not grey? */ + src = srcSlot->bitmap.buffer; + dst = dstSlot->bitmap.buffer + + dstSlot->bitmap.pitch * ( dstSlot->bitmap_top - srcSlot->bitmap_top ) + + 4 * ( srcSlot->bitmap_left - dstSlot->bitmap_left ); + + for ( y = 0; y < srcSlot->bitmap.rows; y++ ) + { + for ( x = 0; x < srcSlot->bitmap.width; x++ ) + { + int aa = src[x]; + int fa = alpha * aa / 255; + + int fb = b * fa / 255; + int fg = g * fa / 255; + int fr = r * fa / 255; + + int ba2 = 255 - fa; + + int bb = dst[4 * x + 0]; + int bg = dst[4 * x + 1]; + int br = dst[4 * x + 2]; + int ba = dst[4 * x + 3]; + + + dst[4 * x + 0] = bb * ba2 / 255 + fb; + dst[4 * x + 1] = bg * ba2 / 255 + fg; + dst[4 * x + 2] = br * ba2 / 255 + fr; + dst[4 * x + 3] = ba * ba2 / 255 + fa; + } + + src += srcSlot->bitmap.pitch; + dst += dstSlot->bitmap.pitch; + } + + return FT_Err_Ok; + } + +#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */ + + /* ANSI C doesn't like empty source files */ + typedef int _tt_colr_dummy; + +#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */ + +/* EOF */ diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h new file mode 100644 index 000000000..84062c04b --- /dev/null +++ b/src/sfnt/ttcolr.h @@ -0,0 +1,57 @@ +/***************************************************************************/ +/* */ +/* ttsbit.h */ +/* */ +/* TrueType and OpenType color outline support (specification). */ +/* */ +/* Copyright 2018 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Written by Shao Yu Zhang . */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTCOLR_H__ +#define __TTCOLR_H__ + + +#include +#include "ttload.h" + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_colr( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_free_colr( TT_Face face ); + + FT_LOCAL( FT_Error ) + tt_face_load_colr_layers( TT_Face face, + FT_Int glyph_id, + FT_Glyph_LayerRec* *ret_layers, + FT_UShort* ret_num_layers ); + + FT_LOCAL( FT_Error ) + tt_face_colr_blend_layer( TT_Face face, + FT_Int color_index, + FT_GlyphSlot dstSlot, + FT_GlyphSlot srcSlot ); + + +FT_END_HEADER + + +#endif /* __TTCOLR_H__ */ + +/* END */ diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index 9434e54d7..ec0d40b31 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -463,7 +463,7 @@ ? &ttsize->metrics : &size->hinted_metrics; - /* now load the glyph outline if necessary */ + /* now fill in the glyph slot with outline/bitmap/layered */ error = TT_Load_Glyph( size, slot, glyph_index, load_flags ); /* force drop-out mode to 2 - irrelevant now */ diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index 39d9c3f73..17d8d95b2 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -2892,6 +2892,37 @@ size->metrics->y_ppem < 24 ) glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + /* The outline based algorithm took care of metrics. */ + /* Read additional color info if requested. */ + if ( ( load_flags & FT_LOAD_COLOR ) && + ( (TT_Face)(glyph->face) )->colr_and_cpal ) + { + TT_Face face = (TT_Face)glyph->face; + FT_Memory memory = face->root.memory; + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + FT_Glyph_LayerRec* glyph_layers; + FT_UShort num_glyph_layers; + FT_Colr_Internal color_layers; + + + error = sfnt->load_colr_layer( face, + glyph_index, + &glyph_layers, + &num_glyph_layers ); + if ( error ) + return error; + + if ( FT_NEW( color_layers ) ) + return error; + + color_layers->layers = glyph_layers; + color_layers->num_layers = num_glyph_layers; + color_layers->load_flags = load_flags; + + glyph->internal->color_layers = color_layers; + } + Exit: #ifdef FT_DEBUG_LEVEL_TRACE if ( error )