[vlc-commits] text_renderer: freetype add support for ruby

Francois Cartegnie git at videolan.org
Fri Feb 16 19:38:17 CET 2018


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Feb  9 00:58:47 2018 +0100| [aa11155a67111520091f70a902b83436396158ee] | committer: Francois Cartegnie

text_renderer: freetype add support for ruby

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

 modules/text_renderer/freetype/freetype.c    |  91 +++++++++++-
 modules/text_renderer/freetype/text_layout.c | 214 ++++++++++++++++++++++++++-
 modules/text_renderer/freetype/text_layout.h |  19 ++-
 3 files changed, 312 insertions(+), 12 deletions(-)

diff --git a/modules/text_renderer/freetype/freetype.c b/modules/text_renderer/freetype/freetype.c
index 1802367349..bf76d49b0c 100644
--- a/modules/text_renderer/freetype/freetype.c
+++ b/modules/text_renderer/freetype/freetype.c
@@ -841,6 +841,17 @@ static void RenderCharAXYZ( filter_t *p_filter,
             break;
         }
 
+        if(ch->p_ruby && ch->p_ruby->p_laid)
+        {
+            RenderCharAXYZ( p_filter,
+                            p_picture,
+                            ch->p_ruby->p_laid,
+                            i_offset_x, i_offset_y,
+                            2,
+                            ExtractComponents,
+                            BlendPixel );
+        }
+
         /* Don't render if invisible or not wanted */
         if( i_a == STYLE_ALPHA_TRANSPARENT ||
            (g == 0 && 0 == (ch->p_style->i_style_flags & STYLE_SHADOW) ) ||
@@ -1001,6 +1012,27 @@ static void FillDefaultStyles( filter_t *p_filter )
     text_style_Merge( p_sys->p_default_style, p_sys->p_forced_style, true );
 }
 
+static void FreeRubyBlockArray( ruby_block_t **pp_array, size_t i_array )
+{
+    ruby_block_t *p_lyt = NULL;
+    for( size_t i = 0; i< i_array; i++ )
+    {
+        if( p_lyt != pp_array[i] )
+        {
+            p_lyt = pp_array[i];
+            if( p_lyt )
+            {
+                free( p_lyt->p_uchars );
+                text_style_Delete( p_lyt->p_style );
+                if( p_lyt->p_laid )
+                    FreeLines( p_lyt->p_laid );
+                free( p_lyt );
+            }
+        }
+    }
+    free( pp_array );
+}
+
 static void FreeStylesArray( text_style_t **pp_styles, size_t i_styles )
 {
     text_style_t *p_style = NULL;
@@ -1016,7 +1048,8 @@ static void FreeStylesArray( text_style_t **pp_styles, size_t i_styles )
 }
 
 static size_t AddTextAndStyles( filter_sys_t *p_sys,
-                                const char *psz_text, const text_style_t *p_style,
+                                const char *psz_text, const char *psz_rt,
+                                const text_style_t *p_style,
                                 layout_text_block_t *p_text_block )
 {
     /* Convert chars to unicode */
@@ -1046,6 +1079,15 @@ static size_t AddTextAndStyles( filter_sys_t *p_sys,
         return 0;
     p_text_block->pp_styles = p_realloc;
 
+    /* Same for ruby text */
+    if( SIZE_MAX / sizeof(text_segment_ruby_t *) < p_text_block->i_count + i_newchars )
+        return 0;
+    i_realloc = (p_text_block->i_count + i_newchars) * sizeof(text_segment_ruby_t *);
+    p_realloc = realloc( p_text_block->pp_ruby, i_realloc );
+    if ( unlikely(!p_realloc) )
+        return 0;
+    p_text_block->pp_ruby = p_realloc;
+
     /* Copy data */
     memcpy( &p_text_block->p_uchars[p_text_block->i_count], p_ucs4, i_newchars * 4 );
     free( p_ucs4 );
@@ -1065,6 +1107,33 @@ static size_t AddTextAndStyles( filter_sys_t *p_sys,
     for ( size_t i = 0; i < i_newchars; ++i )
         p_text_block->pp_styles[p_text_block->i_count + i] = p_mgstyle;
 
+    ruby_block_t *p_rubyblock = NULL;
+    if( psz_rt )
+    {
+        p_ucs4 = ToCharset( FREETYPE_TO_UCS, psz_rt, &i_bytes );
+        if( !p_ucs4 )
+            return 0;
+        p_rubyblock = malloc(sizeof(ruby_block_t));
+        if( p_rubyblock )
+        {
+            p_rubyblock->p_style = text_style_Duplicate( p_mgstyle );
+            if( !p_rubyblock->p_style )
+            {
+                free( p_ucs4 );
+                free( p_rubyblock );
+                return 0;
+            }
+            p_rubyblock->p_style->i_font_size *= 0.4;
+            p_rubyblock->p_style->f_font_relsize *= 0.4;
+            p_rubyblock->p_uchars = p_ucs4;
+            p_rubyblock->i_count = i_bytes / 4;
+            p_rubyblock->p_laid = NULL;
+        }
+        else free( p_ucs4 );
+    }
+    for ( size_t i = 0; i < i_newchars; ++i )
+        p_text_block->pp_ruby[p_text_block->i_count + i] = p_rubyblock;
+
     /* now safe to update total nb */
     p_text_block->i_count += i_newchars;
 
@@ -1081,8 +1150,22 @@ static size_t SegmentsToTextAndStyles( filter_t *p_filter, const text_segment_t
         if( !s->psz_text || !s->psz_text[0] )
             continue;
 
-        i_nb_char += AddTextAndStyles( p_filter->p_sys, s->psz_text,
-                                       s->style, p_text_block );
+        if( s->p_ruby )
+        {
+            for( const text_segment_ruby_t *p_ruby = s->p_ruby;
+                                            p_ruby; p_ruby = p_ruby->p_next )
+            {
+                i_nb_char += AddTextAndStyles( p_filter->p_sys,
+                                               p_ruby->psz_base, p_ruby->psz_rt,
+                                               s->style, p_text_block );
+            }
+        }
+        else
+        {
+            i_nb_char += AddTextAndStyles( p_filter->p_sys,
+                                           s->psz_text, NULL,
+                                           s->style, p_text_block );
+        }
     }
 
     return i_nb_char;
@@ -1283,6 +1366,8 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
 
     free( text_block.p_uchars );
     FreeStylesArray( text_block.pp_styles, text_block.i_count );
+    if( text_block.pp_ruby )
+        FreeRubyBlockArray( text_block.pp_ruby, text_block.i_count );
     free( text_block.pi_k_durations );
 
     return rv;
diff --git a/modules/text_renderer/freetype/text_layout.c b/modules/text_renderer/freetype/text_layout.c
index ad998b2089..106d6f1ba1 100644
--- a/modules/text_renderer/freetype/text_layout.c
+++ b/modules/text_renderer/freetype/text_layout.c
@@ -137,6 +137,7 @@ typedef struct paragraph_t
     uni_char_t          *p_code_points;    /**< Unicode code points */
     int                 *pi_glyph_indices; /**< Glyph index values within the run's font face */
     text_style_t       **pp_styles;
+    ruby_block_t      **pp_ruby;
     FT_Face             *pp_faces;         /**< Used to determine run boundaries when performing font fallback */
     int                 *pi_run_ids;       /**< The run to which each glyph belongs */
     glyph_bitmaps_t     *p_glyph_bitmaps;
@@ -171,6 +172,9 @@ static void FreeLine( line_desc_t *p_line )
             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
     }
 
+//    if( p_line->p_ruby )
+//        FreeLine( p_line->p_ruby );
+
     free( p_line->p_character );
     free( p_line );
 }
@@ -210,6 +214,67 @@ line_desc_t *NewLine( int i_count )
     return p_line;
 }
 
+static void ShiftGlyph( FT_BitmapGlyph g, int x, int y )
+{
+    if( g )
+    {
+        g->left += x;
+        g->top += y;
+    }
+}
+
+static void ShiftChar( line_character_t *c, int x, int y )
+{
+    ShiftGlyph( c->p_glyph, x, y );
+    ShiftGlyph( c->p_shadow, x, y );
+    ShiftGlyph( c->p_outline, x, y );
+    c->bbox.yMin += y;
+    c->bbox.yMax += y;
+    c->bbox.xMin += x;
+    c->bbox.xMax += x;
+}
+
+static void ShiftLine( line_desc_t *p_line, int x, int y )
+{
+    for( int i=0; i<p_line->i_character_count; i++ )
+        ShiftChar( &p_line->p_character[i], x, y );
+    p_line->i_base_line += y;
+    p_line->bbox.yMin += y;
+    p_line->bbox.yMax += y;
+    p_line->bbox.xMin += x;
+    p_line->bbox.xMax += x;
+}
+
+static void MoveLineTo( line_desc_t *p_line, int x, int y )
+{
+    ShiftLine( p_line, x - p_line->bbox.xMin,
+                       y - p_line->bbox.yMax );
+}
+
+static void IndentCharsFrom( line_desc_t *p_line, int i_start, int i_count, int w, int h )
+{
+    for( int i=0; i<i_count; i++ )
+    {
+        line_character_t *p_ch = &p_line->p_character[i_start + i];
+        ShiftChar( p_ch, w, h );
+        BBoxEnlarge( &p_line->bbox, &p_ch->bbox );
+    }
+}
+
+static int RubyBaseAdvance( const line_desc_t *p_line, int i_start, int *pi_count )
+{
+    int i_total = 0;
+    *pi_count = 0;
+    for( int i = i_start; i < p_line->i_character_count; i++ )
+    {
+        if( p_line->p_character[i].p_ruby != p_line->p_character[i_start].p_ruby )
+            break;
+        (*pi_count)++;
+        i_total += (p_line->p_character[i].bbox.xMax - p_line->p_character[i].bbox.xMin);
+    }
+    return i_total;
+}
+
 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox,
                       FT_Pos i_x_advance, FT_Pos i_y_advance,
                       const FT_Vector *p_pen )
@@ -233,6 +298,7 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
                                   int i_size,
                                   const uni_char_t *p_code_points,
                                   text_style_t **pp_styles,
+                                  ruby_block_t **pp_ruby,
                                   uint32_t *pi_k_dates,
                                   int i_runs_size )
 {
@@ -255,6 +321,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
             calloc( i_size, sizeof( *p_paragraph->p_glyph_bitmaps ) );
     p_paragraph->pi_karaoke_bar =
             calloc( i_size, sizeof( *p_paragraph->pi_karaoke_bar ) );
+    if( pp_ruby )
+        p_paragraph->pp_ruby = calloc( i_size, sizeof( *p_paragraph->pp_ruby ) );
 
     p_paragraph->p_runs = calloc( i_runs_size, sizeof( run_desc_t ) );
     p_paragraph->i_runs_size = i_runs_size;
@@ -272,6 +340,9 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
     if( pp_styles )
         memcpy( p_paragraph->pp_styles, pp_styles,
                 i_size * sizeof( *pp_styles ) );
+    if( p_paragraph->pp_ruby )
+        memcpy( p_paragraph->pp_ruby, pp_ruby, i_size * sizeof( *pp_ruby ) );
+
     if( pi_k_dates )
     {
         int64_t i_elapsed  = var_GetInteger( p_filter, "spu-elapsed" ) / 1000;
@@ -315,6 +386,7 @@ error:
     if( p_paragraph->p_code_points ) free( p_paragraph->p_code_points );
     if( p_paragraph->pi_glyph_indices ) free( p_paragraph->pi_glyph_indices );
     if( p_paragraph->pp_styles ) free( p_paragraph->pp_styles );
+    if( p_paragraph->pp_ruby ) free( p_paragraph->pp_ruby );
     if( p_paragraph->pp_faces ) free( p_paragraph->pp_faces );
     if( p_paragraph->pi_run_ids ) free( p_paragraph->pi_run_ids );
     if( p_paragraph->p_glyph_bitmaps ) free( p_paragraph->p_glyph_bitmaps );
@@ -341,6 +413,7 @@ static void FreeParagraph( paragraph_t *p_paragraph )
     free( p_paragraph->pi_karaoke_bar );
     free( p_paragraph->pi_run_ids );
     free( p_paragraph->pp_faces );
+    free( p_paragraph->pp_ruby );
     free( p_paragraph->pp_styles );
     free( p_paragraph->p_code_points );
 
@@ -732,13 +805,20 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
         i_total_glyphs += p_run->i_glyph_count;
     }
 
-    p_new_paragraph = NewParagraph( p_filter, i_total_glyphs, 0, 0, 0,
+    p_new_paragraph = NewParagraph( p_filter, i_total_glyphs,
+                                    NULL, NULL, NULL, NULL,
                                     p_paragraph->i_runs_size );
     if( !p_new_paragraph )
     {
         i_ret = VLC_ENOMEM;
         goto error;
     }
+    if( p_paragraph->pp_ruby )
+    {
+        p_new_paragraph->pp_ruby = calloc(p_new_paragraph->i_size,
+                                          sizeof(ruby_block_t *));
+    }
+
     p_new_paragraph->paragraph_type = p_paragraph->paragraph_type;
 
     int i_index = 0;
@@ -772,6 +852,9 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
                 p_paragraph->p_levels[ i_source_index ];
             p_new_paragraph->pp_styles[ i_index ] =
                 p_paragraph->pp_styles[ i_source_index ];
+            if( p_new_paragraph->pp_ruby )
+                p_new_paragraph->pp_ruby[ i_index ] =
+                    p_paragraph->pp_ruby[ i_source_index ];
             p_new_paragraph->pi_karaoke_bar[ i_index ] =
                 p_paragraph->pi_karaoke_bar[ i_source_index ];
             p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_offset =
@@ -1056,7 +1139,8 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
 static int LayoutLine( filter_t *p_filter,
                        paragraph_t *p_paragraph,
                        int i_first_char, int i_last_char,
-                       line_desc_t **pp_line, bool b_grid )
+                       bool b_grid,
+                       line_desc_t **pp_line )
 {
     if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0
      || i_first_char < 0 || i_last_char < 0
@@ -1111,6 +1195,9 @@ static int LayoutLine( filter_t *p_filter,
         line_character_t *p_ch = &p_line->p_character[p_line->i_character_count];
         p_ch->p_style = p_paragraph->pp_styles[ i_paragraph_index ];
 
+        if( p_paragraph->pp_ruby )
+            p_ch->p_ruby = p_paragraph->pp_ruby[ i ];
+
         glyph_bitmaps_t *p_bitmaps =
                 p_paragraph->p_glyph_bitmaps + i_paragraph_index;
 
@@ -1273,6 +1360,44 @@ static int LayoutLine( filter_t *p_filter,
         p_line->i_character_count++;
     }
 
+    /* Second pass for ruby layout */
+    if( p_paragraph->pp_ruby )
+    {
+        const int i_ruby_baseline = p_line->bbox.yMax;
+        const ruby_block_t *p_prevruby = NULL;
+        for( int i = 0; i < p_line->i_character_count; ++i )
+        {
+            line_character_t *p_ch = &p_line->p_character[i];
+            if( p_ch->p_ruby == p_prevruby || !p_ch->p_glyph )
+                continue;
+            p_prevruby = p_ch->p_ruby;
+            if( !p_ch->p_ruby )
+                continue;
+            line_desc_t *p_rubyline = p_ch->p_ruby->p_laid;
+            if( !p_rubyline )
+                continue;
+
+            int i_rubyadvance = (p_rubyline->bbox.xMax - p_rubyline->bbox.xMin);
+            int i_rubyheight = (p_rubyline->bbox.yMax - p_rubyline->bbox.yMin);
+            MoveLineTo( p_rubyline, p_ch->bbox.xMin, i_ruby_baseline + i_rubyheight );
+            BBoxEnlarge( &p_line->bbox, &p_rubyline->bbox );
+
+            int i_count;
+            int i_baseadvance = RubyBaseAdvance( p_line, i, &i_count );
+            if( i_baseadvance < i_rubyadvance )
+            {
+                IndentCharsFrom( p_line, i, i_count, (i_rubyadvance - i_baseadvance) / 2, 0 );
+                IndentCharsFrom( p_line, i + i_count, p_line->i_character_count - (i + i_count),
+                                 (i_rubyadvance - i_baseadvance + 1), 0 );
+            }
+            else if( i_baseadvance > i_rubyadvance + 1 )
+            {
+                ShiftLine( p_rubyline, (i_baseadvance - i_rubyadvance) / 2, 0 );
+                BBoxEnlarge( &p_line->bbox, &p_rubyline->bbox ); /* shouldn't be needed */
+            }
+        }
+    }
+
     p_line->i_width = __MAX( 0, p_line->bbox.xMax - p_line->bbox.xMin );
 
     if( b_grid )
@@ -1316,7 +1441,8 @@ static inline bool IsWhitespaceAt( paragraph_t *p_paragraph, size_t i )
 
 static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                             unsigned i_max_width, unsigned i_max_advance_x,
-                            line_desc_t **pp_lines, bool b_grid, bool b_balance )
+                            bool b_grid, bool b_balance,
+                            line_desc_t **pp_lines )
 {
     if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
     {
@@ -1376,12 +1502,27 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
         {
             if( i_line_start < i )
                 if( LayoutLine( p_filter, p_paragraph,
-                                i_line_start, i - 1, pp_line, b_grid ) )
+                                i_line_start, i - 1, b_grid, pp_line ) )
                     goto error;
 
             break;
         }
 
+        if( p_paragraph->pp_ruby &&
+            p_paragraph->pp_ruby[i] &&
+            p_paragraph->pp_ruby[i]->p_laid )
+        {
+            /* Just forward as non breakable */
+            const ruby_block_t *p_rubyseq = p_paragraph->pp_ruby[i];
+            int i_advance = 0;
+            int i_advanceruby = p_rubyseq->p_laid->i_width;
+            while( i < p_paragraph->i_size && p_rubyseq == p_paragraph->pp_ruby[i] )
+                i_advance += p_paragraph->p_glyph_bitmaps[ i++ ].i_x_advance;
+            /* Just forward as non breakable */
+            i_width += (i_advance < i_advanceruby) ? i_advanceruby : i_advance;
+            continue;
+        }
+
         if( IsWhitespaceAt( p_paragraph, i ) )
         {
             if( i_line_start == i )
@@ -1431,7 +1572,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                 i_newline_start = i; /* we break line on last char */
 
             if( LayoutLine( p_filter, p_paragraph, i_line_start,
-                            i_newline_start - 1, pp_line, b_grid ) )
+                            i_newline_start - 1, b_grid, pp_line ) )
                 goto error;
 
             /* Handle early end of renderable content;
@@ -1480,6 +1621,7 @@ static paragraph_t * BuildParagraph( filter_t *p_filter,
                                      int i_size,
                                      const uni_char_t *p_uchars,
                                      text_style_t **pp_styles,
+                                     ruby_block_t **pp_ruby,
                                      uint32_t *pi_k_dates,
                                      int i_runs_size,
                                      unsigned *pi_max_advance_x )
@@ -1487,6 +1629,7 @@ static paragraph_t * BuildParagraph( filter_t *p_filter,
     paragraph_t *p_paragraph = NewParagraph( p_filter, i_size,
                                 p_uchars,
                                 pp_styles,
+                                pp_ruby,
                                 pi_k_dates,
                                 i_runs_size );
     if( !p_paragraph )
@@ -1520,6 +1663,7 @@ static paragraph_t * BuildParagraph( filter_t *p_filter,
     if( LoadGlyphs( p_filter, p_paragraph, false, true, pi_max_advance_x ) )
         goto error;
 #endif
+
     return p_paragraph;
 
 error:
@@ -1529,6 +1673,43 @@ error:
     return NULL;
 }
 
+static int LayoutRubyText( filter_t *p_filter,
+                           const uni_char_t *p_uchars,
+                           int i_uchars,
+                           text_style_t *p_style,
+                           line_desc_t **pp_line )
+{
+    unsigned int i_max_advance_x;
+
+    text_style_t **pp_styles = malloc(sizeof(*pp_styles) * i_uchars);
+    for(int i=0;i<i_uchars;i++)
+        pp_styles[i] = p_style;
+
+    paragraph_t *p_paragraph = BuildParagraph( p_filter, i_uchars,
+                                               p_uchars, pp_styles,
+                                               NULL, NULL, 1,
+                                               &i_max_advance_x );
+    if( !p_paragraph )
+    {
+        free( pp_styles );
+        return VLC_EGENERIC;
+    }
+
+    if( LayoutLine( p_filter, p_paragraph,
+                    0, i_uchars - 1,
+                    false, pp_line ) )
+    {
+        free( pp_styles );
+        FreeParagraph( p_paragraph );
+        return VLC_EGENERIC;
+    }
+
+    FreeParagraph( p_paragraph );
+    free( pp_styles );
+
+    return VLC_SUCCESS;
+}
+
 int LayoutTextBlock( filter_t *p_filter,
                      const layout_text_block_t *p_textblock,
                      line_desc_t **pp_lines, FT_BBox *p_bbox,
@@ -1541,6 +1722,22 @@ int LayoutTextBlock( filter_t *p_filter,
     unsigned i_max_advance_x = 0;
     int i_max_face_height = 0;
 
+    /* Prepare ruby content */
+    if( p_textblock->pp_ruby )
+    {
+        ruby_block_t *p_prev = NULL;
+        for( size_t i=0; i<p_textblock->i_count; i++ )
+        {
+            if( p_textblock->pp_ruby[i] == p_prev )
+                continue;
+            p_prev = p_textblock->pp_ruby[i];
+            if( p_prev )
+                LayoutRubyText( p_filter, p_prev->p_uchars, p_prev->i_count,
+                                p_prev->p_style, &p_prev->p_laid );
+        }
+    }
+    /* !Prepare ruby content */
+
     for( size_t i = 0; i <= p_textblock->i_count; ++i )
     {
         if( i == p_textblock->i_count || p_textblock->p_uchars[ i ] == '\n' )
@@ -1556,6 +1753,8 @@ int LayoutTextBlock( filter_t *p_filter,
                                     i - i_paragraph_start,
                                     &p_textblock->p_uchars[i_paragraph_start],
                                     &p_textblock->pp_styles[i_paragraph_start],
+                                    p_textblock->pp_ruby ?
+                                    &p_textblock->pp_ruby[i_paragraph_start] : NULL,
                                     p_textblock->pi_k_durations ?
                                     &p_textblock->pi_k_durations[i_paragraph_start] : NULL,
                                     20, &i_max_advance_x );
@@ -1567,8 +1766,9 @@ int LayoutTextBlock( filter_t *p_filter,
 
             if( LayoutParagraph( p_filter, p_paragraph,
                                  p_textblock->i_max_width,
-                                 i_max_advance_x, pp_line,
-                                 p_textblock->b_grid, p_textblock->b_balanced ) )
+                                 i_max_advance_x,
+                                 p_textblock->b_grid, p_textblock->b_balanced,
+                                 pp_line ) )
             {
                 FreeParagraph( p_paragraph );
                 if( p_first_line ) FreeLines( p_first_line );
diff --git a/modules/text_renderer/freetype/text_layout.h b/modules/text_renderer/freetype/text_layout.h
index ea0bca7558..bb31aac1b6 100644
--- a/modules/text_renderer/freetype/text_layout.h
+++ b/modules/text_renderer/freetype/text_layout.h
@@ -33,6 +33,9 @@
 
 #include "freetype.h"
 
+typedef struct ruby_block_t ruby_block_t;
+typedef struct line_desc_t line_desc_t;
+
 typedef struct
 {
     FT_BitmapGlyph p_glyph;
@@ -40,16 +43,15 @@ typedef struct
     FT_BitmapGlyph p_shadow;
     FT_BBox        bbox;
     const text_style_t *p_style;
+    const ruby_block_t *p_ruby;
     int            i_line_offset;       /* underline/strikethrough offset */
     int            i_line_thickness;    /* underline/strikethrough thickness */
     bool           b_in_karaoke;
 } line_character_t;
 
-typedef struct line_desc_t line_desc_t;
 struct line_desc_t
 {
     line_desc_t      *p_next;
-
     int              i_width;
     int              i_height;
     int              i_base_line;
@@ -64,6 +66,18 @@ void FreeLines( line_desc_t *p_lines );
 line_desc_t *NewLine( int i_count );
 
 /**
+ * \struct layout_ruby_t
+ * \brief LayoutText parameters
+ */
+struct ruby_block_t
+{
+    uni_char_t *p_uchars;       /*!< array of size \p i_count character codepoints */
+    size_t i_count;             /*!< length of the array */
+    text_style_t *p_style;      /*!< own style */
+    line_desc_t *p_laid;
+};
+
+/**
  * \struct layout_text_block_t
  * \brief LayoutText parameters
  */
@@ -71,6 +85,7 @@ typedef struct
 {
     uni_char_t *p_uchars;       /*!< array of size \p i_count character codepoints */
     text_style_t **pp_styles;   /*!< array of size \p i_count character styles */
+    ruby_block_t **pp_ruby;     /*!< array of size \p  */
     uint32_t *pi_k_durations;   /*!< array of size \p i_count karaoke timestamps */
     size_t i_count;             /*!< length of the arrays */
 



More information about the vlc-commits mailing list