Commit aa11155a authored by François Cartegnie's avatar François Cartegnie 🤞

text_renderer: freetype add support for ruby

parent 65b4b3a7
......@@ -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;
......
......@@ -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 );
......
......@@ -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;
......@@ -63,6 +65,18 @@ struct line_desc_t
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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment