[vlc-commits] codec: ttml: fix font size computation

Francois Cartegnie git at videolan.org
Sat Sep 30 20:50:44 CEST 2017


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Sat Sep 30 11:23:36 2017 +0200| [de3d3898554d644feb9312a4a263a4d31525c189] | committer: Francois Cartegnie

codec: ttml: fix font size computation

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=de3d3898554d644feb9312a4a263a4d31525c189
---

 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 );
 



More information about the vlc-commits mailing list