vlc | branch: master | Francois Cartegnie <fcvlc...@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 */ _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits