[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