From de3d3898554d644feb9312a4a263a4d31525c189 Mon Sep 17 00:00:00 2001 From: Francois Cartegnie Date: Sat, 30 Sep 2017 11:23:36 +0200 Subject: [PATCH] codec: ttml: fix font size computation --- modules/codec/ttml/substtml.c | 179 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 32 deletions(-) diff --git a/modules/codec/ttml/substtml.c b/modules/codec/ttml/substtml.c index 8c964f5443..66f9ea063d 100644 --- a/modules/codec/ttml/substtml.c +++ b/modules/codec/ttml/substtml.c @@ -42,11 +42,27 @@ /***************************************************************************** * Local prototypes *****************************************************************************/ +typedef struct +{ + float i_value; + enum + { + TTML_UNIT_UNKNOWN = 0, + TTML_UNIT_PERCENT, + TTML_UNIT_CELL, + TTML_UNIT_PIXELS, + } unit; +} ttml_length_t; + +#define TTML_DEFAULT_CELL_RESOLUTION_H 32 +#define TTML_DEFAULT_CELL_RESOLUTION_V 15 +#define TTML_LINE_TO_HEIGHT_RATIO 1.06 typedef struct { text_style_t* font_style; - unsigned i_cell_height; + ttml_length_t font_size; + ttml_length_t extent_h, extent_v; int i_text_align; int i_direction; bool b_direction_set; @@ -63,6 +79,8 @@ typedef struct { vlc_dictionary_t regions; tt_node_t * p_rootnode; /* for now. FIXME: split header */ + ttml_length_t root_extent_h, root_extent_v; + unsigned i_cell_resolution_v; } ttml_context_t; typedef struct @@ -105,7 +123,12 @@ static ttml_style_t * ttml_style_New( ) if( unlikely( !p_ttml_style ) ) return NULL; - p_ttml_style->i_cell_height = 15; + p_ttml_style->extent_h.i_value = 100; + p_ttml_style->extent_h.unit = TTML_UNIT_PERCENT; + p_ttml_style->extent_v.i_value = 100; + p_ttml_style->extent_v.unit = TTML_UNIT_PERCENT; + p_ttml_style->font_size.i_value = 1.0; + p_ttml_style->font_size.unit = TTML_UNIT_CELL; p_ttml_style->font_style = text_style_Create( STYLE_NO_DEFAULTS ); if( unlikely( !p_ttml_style->font_style ) ) { @@ -169,6 +192,43 @@ static ttml_region_t *ttml_region_New( ) return p_ttml_region; } +static ttml_length_t ttml_read_length( const char *psz ) +{ + ttml_length_t len = { 0.0, TTML_UNIT_UNKNOWN }; + + char* psz_end = NULL; + float size = us_strtof( psz, &psz_end ); + len.i_value = size; + if( psz_end ) + { + if( *psz_end == 'c' || *psz_end == 'r' ) + len.unit = TTML_UNIT_CELL; + else if( *psz_end == '%' ) + len.unit = TTML_UNIT_PERCENT; + else if( *psz_end == 'p' && *(psz_end + 1) == 'x' ) + len.unit = TTML_UNIT_PIXELS; + } + return len; +} + +static ttml_length_t ttml_rebase_length( ttml_length_t value, + ttml_length_t reference, + unsigned i_cell_resolution ) +{ + if( value.unit == TTML_UNIT_PERCENT ) + { + value.i_value *= reference.i_value / 100.0; + value.unit = reference.unit; + } + else if( value.unit == TTML_UNIT_CELL ) + { + value.i_value *= reference.i_value / i_cell_resolution; + value.unit = reference.unit; + } + // pixels as-is + return value; +} + static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename, size_t i_maxdepth, const char *psz_id ) { @@ -201,7 +261,7 @@ static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename, } static void FillTextStyle( const char *psz_attr, const char *psz_val, - const ttml_style_t *p_ttml_style, text_style_t *p_text_style ) + text_style_t *p_text_style ) { if( !strcasecmp ( "tts:fontFamily", psz_attr ) ) { @@ -214,20 +274,6 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val, p_text_style->i_font_alpha = atoi( psz_val ); p_text_style->i_features |= STYLE_HAS_BACKGROUND_ALPHA | STYLE_HAS_FONT_ALPHA; } - else if( !strcasecmp( "tts:fontSize", psz_attr ) ) - { - char* psz_end = NULL; - float size = us_strtof( psz_val, &psz_end ); - if( size > 0.0 ) - { - if( *psz_end == 'c' ) - p_text_style->f_font_relsize = 100.0 * size / p_ttml_style->i_cell_height; - else if( *psz_end == '%' ) - p_text_style->f_font_relsize = size / p_ttml_style->i_cell_height; - else - p_text_style->i_font_size = (int)( size + 0.5 ); - } - } else if( !strcasecmp( "tts:color", psz_attr ) ) { unsigned int i_color = vlc_html_color( psz_val, NULL ); @@ -348,23 +394,57 @@ static void FillRegionStyle( const char *psz_attr, const char *psz_val, } } -static void FillTTMLStylePrio( const vlc_dictionary_t *p_dict, - ttml_style_t *p_ttml_style ) +static void ReadTTMLExtent( const char *value, ttml_length_t *h, ttml_length_t *v ) { - void *value = vlc_dictionary_value_for_key( p_dict, "ttp:cellResolution" ); - if( value != kVLCDictionaryNotFound ) + ttml_length_t vals[2] = { { 0.0, TTML_UNIT_UNKNOWN }, + { 0.0, TTML_UNIT_UNKNOWN } }; + char *dup = strdup( value ); + char* psz_saveptr = NULL; + char* token = (dup) ? strtok_r( dup, " ", &psz_saveptr ) : NULL; + for(int i=0; i<2 && token != NULL; i++) { - const char *psz_val = value; - unsigned w, h; - if( sscanf( psz_val, "%u %u", &w, &h) == 2 && w && h ) - p_ttml_style->i_cell_height = h; + token = strtok_r( NULL, " ", &psz_saveptr ); + if( token != NULL ) + vals[i] = ttml_read_length( token ); + } + free( dup ); + + if( vals[0].unit != TTML_UNIT_UNKNOWN && + vals[1].unit != TTML_UNIT_UNKNOWN ) + { + *h = vals[0]; + *v = vals[1]; } } +static void ComputeTTMLStyles( ttml_context_t *p_ctx, const vlc_dictionary_t *p_dict, + ttml_style_t *p_ttml_style ) +{ + VLC_UNUSED(p_dict); + /* Values depending on multiple others are converted last + * Default value conversion must also not depend on attribute presence */ + text_style_t *p_text_style = p_ttml_style->font_style; + ttml_length_t len = p_ttml_style->font_size; + len = ttml_rebase_length( len, p_ctx->root_extent_h, + p_ctx->i_cell_resolution_v ); + if( len.unit == TTML_UNIT_CELL ) + p_text_style->f_font_relsize = 100.0 * len.i_value / + (p_ctx->i_cell_resolution_v / TTML_LINE_TO_HEIGHT_RATIO); + else if( len.unit == TTML_UNIT_PERCENT ) + p_text_style->f_font_relsize = len.i_value; + else if( len.unit == TTML_UNIT_PIXELS ) + p_text_style->i_font_size = (int)( len.i_value + 0.5 ); +} + static void FillTTMLStyle( const char *psz_attr, const char *psz_val, ttml_style_t *p_ttml_style ) { - if( !strcasecmp( "tts:textAlign", psz_attr ) ) + if( !strcasecmp( "tts:extent", psz_attr ) ) + { + ReadTTMLExtent( psz_attr, &p_ttml_style->extent_h, + &p_ttml_style->extent_v ); + } + else if( !strcasecmp( "tts:textAlign", psz_attr ) ) { if( !strcasecmp ( "left", psz_val ) ) p_ttml_style->i_text_align = SUBPICTURE_ALIGN_LEFT; @@ -377,6 +457,12 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val, else if( !strcasecmp ( "end", psz_val ) ) /* FIXME: should be BIDI based */ p_ttml_style->i_text_align = SUBPICTURE_ALIGN_RIGHT; } + else if( !strcasecmp( "tts:fontSize", psz_attr ) ) + { + ttml_length_t len = ttml_read_length( psz_val ); + if( len.unit != TTML_UNIT_UNKNOWN && len.i_value > 0.0 ) + p_ttml_style->font_size = len; + } else if( !strcasecmp( "tts:direction", psz_attr ) ) { if( !strcasecmp( "rtl", psz_val ) ) @@ -423,7 +509,7 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val, { p_ttml_style->b_preserve_space = !strcmp( "preserve", psz_val ); } - else FillTextStyle( psz_attr, psz_val, p_ttml_style, p_ttml_style->font_style ); + else FillTextStyle( psz_attr, psz_val, p_ttml_style->font_style ); } static void DictionaryMerge( const vlc_dictionary_t *p_src, vlc_dictionary_t *p_dst ) @@ -489,10 +575,9 @@ static void DictMergeWithRegionID( ttml_context_t *p_ctx, const char *psz_id, } } -static void DictToTTMLStyle( const vlc_dictionary_t *p_dict, ttml_style_t *p_ttml_style ) +static void DictToTTMLStyle( ttml_context_t *p_ctx, const vlc_dictionary_t *p_dict, + ttml_style_t *p_ttml_style ) { - /* Units, defaults, that must be set first to compute styles */ - FillTTMLStylePrio( p_dict, p_ttml_style ); for( int i = 0; i < p_dict->i_size; ++i ) { for ( vlc_dictionary_entry_t* p_entry = p_dict->p_entries[i]; @@ -501,6 +586,7 @@ static void DictToTTMLStyle( const vlc_dictionary_t *p_dict, ttml_style_t *p_ttm FillTTMLStyle( p_entry->psz_key, p_entry->p_value, p_ttml_style ); } } + ComputeTTMLStyles( p_ctx, p_dict, p_ttml_style ); } static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_node ) @@ -528,7 +614,7 @@ static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_nod if( !vlc_dictionary_is_empty( &merged ) && (p_ttml_style = ttml_style_New()) ) { - DictToTTMLStyle( &merged, p_ttml_style ); + DictToTTMLStyle( p_ctx, &merged, p_ttml_style ); } vlc_dictionary_clear( &merged, NULL, NULL ); @@ -756,7 +842,7 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t if( p_set_styles != NULL || (p_set_styles = ttml_style_New()) ) { /* Merge with or create a local set of styles to apply to following childs */ - DictToTTMLStyle( &p_set->attr_dict, p_set_styles ); + DictToTTMLStyle( p_ctx, &p_set->attr_dict, p_set_styles ); } } } @@ -805,6 +891,33 @@ static tt_node_t *ParseTTML( decoder_t *p_dec, const uint8_t *p_buffer, size_t i return p_rootnode; } +static void InitTTMLContext( tt_node_t *p_rootnode, ttml_context_t *p_ctx ) +{ + p_ctx->p_rootnode = p_rootnode; + /* set defaults required for size/cells computation */ + p_ctx->root_extent_h.i_value = 100; + p_ctx->root_extent_h.unit = TTML_UNIT_PERCENT; + p_ctx->root_extent_v.i_value = 100; + p_ctx->root_extent_v.unit = TTML_UNIT_PERCENT; + p_ctx->i_cell_resolution_v = TTML_DEFAULT_CELL_RESOLUTION_V; + /* and override them */ + const char *value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict, + "tts:extent" ); + if( value != kVLCDictionaryNotFound ) + { + ReadTTMLExtent( value, &p_ctx->root_extent_h, + &p_ctx->root_extent_v ); + } + value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict, + "ttp:cellResolution" ); + if( value != kVLCDictionaryNotFound ) + { + unsigned w, h; + if( sscanf( value, "%u %u", &w, &h) == 2 && w && h ) + p_ctx->i_cell_resolution_v = h; + } +} + static ttml_region_t *GenerateRegions( tt_node_t *p_rootnode, tt_time_t playbacktime ) { ttml_region_t* p_regions = NULL; @@ -816,7 +929,9 @@ static ttml_region_t *GenerateRegions( tt_node_t *p_rootnode, tt_time_t playback if( p_bodynode ) { ttml_context_t context; + InitTTMLContext( p_rootnode, &context ); context.p_rootnode = p_rootnode; + vlc_dictionary_init( &context.regions, 1 ); ConvertNodesToRegionContent( &context, p_bodynode, NULL, NULL, playbacktime ); -- 2.11.4.GIT