[vlc-devel] [PATCH 1/4] freetype: font fallback structs and functions
Salah-Eddin Shaban
salshaaban at gmail.com
Thu Oct 22 13:53:47 CEST 2015
---
modules/text_renderer/freetype.c | 517 +++++++++++++++++----------------
modules/text_renderer/freetype.h | 78 +++--
modules/text_renderer/platform_fonts.c | 378 +++++++++++++++++++++++-
modules/text_renderer/platform_fonts.h | 101 ++++++-
modules/text_renderer/text_layout.c | 157 ++++++++--
5 files changed, 913 insertions(+), 318 deletions(-)
diff --git a/modules/text_renderer/freetype.c b/modules/text_renderer/freetype.c
index f55e23a..95b045e 100644
--- a/modules/text_renderer/freetype.c
+++ b/modules/text_renderer/freetype.c
@@ -44,12 +44,6 @@
#include <vlc_text_style.h> /* text_style_t*/
#include <vlc_charset.h>
-/* Freetype */
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_GLYPH_H
-#include FT_STROKER_H
-
/* apple stuff */
#ifdef __APPLE__
# include <TargetConditionals.h>
@@ -280,6 +274,8 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
input_attachment_t **pp_attachments;
int i_attachments_cnt;
+ FT_Face p_face = NULL;
+ char *psz_lc = NULL;
if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
return VLC_EGENERIC;
@@ -287,9 +283,15 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
p_sys->i_font_attachments = 0;
p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
if( !p_sys->pp_font_attachments )
+ {
+ for( int i = 0; i < i_attachments_cnt; ++i )
+ vlc_input_attachment_Delete( pp_attachments[ i ] );
+ free( pp_attachments );
return VLC_ENOMEM;
+ }
- for( int k = 0; k < i_attachments_cnt; k++ )
+ int k = 0;
+ for( ; k < i_attachments_cnt; k++ )
{
input_attachment_t *p_attach = pp_attachments[k];
@@ -298,15 +300,100 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
p_attach->i_data > 0 && p_attach->p_data )
{
p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
+
+ int i_font_idx = 0;
+
+ while( 0 == FT_New_Memory_Face( p_sys->p_library,
+ p_attach->p_data,
+ p_attach->i_data,
+ i_font_idx,
+ &p_face ))
+ {
+
+ bool b_bold = p_face->style_flags & FT_STYLE_FLAG_BOLD;
+ bool b_italic = p_face->style_flags & FT_STYLE_FLAG_ITALIC;
+
+ if( p_face->family_name )
+ psz_lc = ToLower( p_face->family_name );
+ else
+ if( asprintf( &psz_lc, FB_NAME"-%02d",
+ p_sys->i_fallback_counter++ ) < 0 )
+ psz_lc = NULL;
+
+ if( unlikely( !psz_lc ) )
+ goto error;
+
+ vlc_family_t *p_family =
+ vlc_dictionary_value_for_key( &p_sys->family_map, psz_lc );
+
+ if( p_family == kVLCDictionaryNotFound )
+ {
+ p_family = NewFamily( p_filter, psz_lc, &p_sys->p_families,
+ &p_sys->family_map, psz_lc );
+
+ if( unlikely( !p_family ) )
+ goto error;
+ }
+
+ free( psz_lc );
+ psz_lc = NULL;
+
+ char *psz_fontfile;
+ if( asprintf( &psz_fontfile, ":/%d",
+ p_sys->i_font_attachments - 1 ) < 0
+ || !NewFont( psz_fontfile, i_font_idx, b_bold, b_italic, p_family ) )
+ goto error;
+
+ FT_Done_Face( p_face );
+ p_face = NULL;
+
+ i_font_idx++;
+ }
+
}
else
{
vlc_input_attachment_Delete( p_attach );
}
}
+
free( pp_attachments );
+ /* Add font attachments to the "attachments" fallback list */
+ vlc_family_t *p_attachments = NULL;
+
+ for( vlc_family_t *p_family = p_sys->p_families; p_family;
+ p_family = p_family->p_next )
+ {
+ vlc_family_t *p_temp = NewFamily( p_filter, p_family->psz_name, &p_attachments,
+ NULL, NULL );
+ if( unlikely( !p_temp ) )
+ {
+ if( p_attachments )
+ FreeFamilies( p_attachments, NULL );
+ return VLC_ENOMEM;
+ }
+ else
+ p_temp->p_fonts = p_family->p_fonts;
+ }
+
+ if( p_attachments )
+ vlc_dictionary_insert( &p_sys->fallback_map, FB_LIST_ATTACHMENTS, p_attachments );
+
return VLC_SUCCESS;
+
+error:
+ if( p_face )
+ FT_Done_Face( p_face );
+
+ if( psz_lc )
+ free( psz_lc );
+
+ for( int i = k + 1; i < i_attachments_cnt; ++i )
+ vlc_input_attachment_Delete( pp_attachments[ i ] );
+
+ free( pp_attachments );
+ return VLC_ENOMEM;
}
/*****************************************************************************
@@ -891,11 +978,10 @@ static void FreeStylesArray( text_style_t **pp_styles, size_t i_styles )
}
static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segment_t *p_segment, size_t *pi_string_length,
- text_style_t ***ppp_styles, size_t *pi_styles, bool b_grid )
+ text_style_t ***ppp_styles, size_t *pi_styles )
{
text_style_t **pp_styles = NULL;
uni_char_t *psz_uni = NULL;
- const int i_scale = ( b_grid ) ? 100 : var_InheritInteger( p_filter, "sub-text-scale");
size_t i_size = 0;
size_t i_nb_char = 0;
*pi_styles = 0;
@@ -951,12 +1037,6 @@ static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segme
/* Overwrite any default or value with forced ones */
text_style_Merge( p_style, p_filter->p_sys->p_forced_style, true );
- if( i_scale != 100 )
- {
- p_style->i_font_size = p_style->i_font_size * i_scale / 100;
- p_style->f_font_relsize = p_style->f_font_relsize * i_scale / 100;
- }
-
// i_string_bytes is a number of bytes, while here we're going to assign pointer by pointer
for ( size_t i = 0; i < i_string_bytes / sizeof( *psz_uni ); ++i )
pp_styles[i_nb_char + i] = p_style;
@@ -981,11 +1061,25 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
if( !p_region_in )
return VLC_EGENERIC;
+ filter_sys_t *p_sys = p_filter->p_sys;
+ bool b_grid = p_region_in->b_gridmode;
+ p_sys->i_scale = ( b_grid ) ? 100 : var_InheritInteger( p_filter, "sub-text-scale");
+
+ /*
+ * Update the default face to reflect changes in video size or text scaling
+ */
+ p_sys->p_face = SelectAndLoadFace( p_filter, p_sys->p_default_style, 0 );
+ if( !p_sys->p_face )
+ {
+ msg_Err( p_filter, "Render(): Error loading default face" );
+ return VLC_EGENERIC;
+ }
+
text_style_t **pp_styles = NULL;
size_t i_text_length = 0;
size_t i_styles = 0;
uni_char_t *psz_text = SegmentsToTextAndStyles( p_filter, p_region_in->p_text, &i_text_length,
- &pp_styles, &i_styles, p_region_in->b_gridmode );
+ &pp_styles, &i_styles );
if( !psz_text || !pp_styles )
{
return VLC_EGENERIC;
@@ -1065,100 +1159,61 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
return rv;
}
+static void FreeFace( void *p_face, void *p_obj )
+{
+ VLC_UNUSED( p_obj );
+
+ FT_Done_Face( ( FT_Face ) p_face );
+}
+
/*****************************************************************************
* Create: allocates osd-text video thread output method
*****************************************************************************
* This function allocates and initializes a Clone vout method.
*****************************************************************************/
-static int Init_FT( vlc_object_t *p_this,
- const char *psz_fontfile,
- const int fontindex )
+static int Create( vlc_object_t *p_this )
{
- filter_t *p_filter = (filter_t *)p_this;
- filter_sys_t *p_sys = p_filter->p_sys;
-
- /* */
- int i_error = FT_Init_FreeType( &p_sys->p_library );
- if( i_error )
- {
- msg_Err( p_filter, "couldn't initialize freetype" );
- goto error;
- }
-
- i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
- fontindex, &p_sys->p_face );
+ filter_t *p_filter = ( filter_t * ) p_this;
+ filter_sys_t *p_sys = NULL;
- if( i_error == FT_Err_Unknown_File_Format )
- {
- msg_Err( p_filter, "file %s have unknown format",
- psz_fontfile ? psz_fontfile : "(null)" );
- goto error;
- }
- else if( i_error )
- {
- msg_Err( p_filter, "failed to load font file %s",
- psz_fontfile ? psz_fontfile : "(null)" );
- goto error;
- }
+ /* Allocate structure */
+ p_filter->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
+ if( !p_sys )
+ return VLC_ENOMEM;
- i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
- if( i_error )
+ if( FT_Init_FreeType( &p_sys->p_library ) )
{
- msg_Err( p_filter, "font has no unicode translation table" );
- goto error;
+ msg_Err( p_filter, "Failed to initialize FreeType" );
+ free( p_sys );
+ return VLC_EGENERIC;
}
- if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, STYLE_DEFAULT_FONT_SIZE ) )
+ if( FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker ) )
{
- msg_Err( p_filter, "couldn't set font size to %d", STYLE_DEFAULT_FONT_SIZE );
- goto error;
- }
-
- i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
- if( i_error )
msg_Err( p_filter, "Failed to create stroker for outlining" );
+ p_sys->p_stroker = NULL;
+ }
- return VLC_SUCCESS;
-
-error:
- if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
- if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
-
- return VLC_EGENERIC;
-}
-
-static int Create( vlc_object_t *p_this )
-{
- filter_t *p_filter = (filter_t *)p_this;
- filter_sys_t *p_sys;
- char *psz_fontfile = NULL;
- char *psz_monofontfile = NULL;
- int fontindex = 0, monofontindex = 0;
+ p_sys->pp_font_attachments = NULL;
+ p_sys->i_font_attachments = 0;
+ p_sys->p_families = NULL;
- /* Allocate structure */
- p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
- if( !p_sys )
- return VLC_ENOMEM;
+ vlc_dictionary_init( &p_sys->face_map, 50 );
+ vlc_dictionary_init( &p_sys->family_map, 50 );
+ vlc_dictionary_init( &p_sys->fallback_map, 20 );
- p_sys->p_face = 0;
- p_sys->p_library = 0;
+ p_sys->i_fallback_counter = 0;
+ p_sys->i_scale = 100;
/* default style to apply to uncomplete segmeents styles */
p_sys->p_default_style = text_style_Create( STYLE_FULLY_SET );
if(unlikely(!p_sys->p_default_style))
- {
- free(p_sys);
- return VLC_ENOMEM;
- }
+ goto error;
/* empty style for style overriding cases */
p_sys->p_forced_style = text_style_Create( STYLE_NO_DEFAULTS );
if(unlikely(!p_sys->p_forced_style))
- {
- text_style_Delete( p_sys->p_default_style );
- free(p_sys);
- return VLC_ENOMEM;
- }
+ goto error;
/* fills default and forced style */
FillDefaultStyles( p_filter );
@@ -1200,6 +1255,9 @@ static int Create( vlc_object_t *p_this )
#endif
}
+ if( LoadFontsFromAttachments( p_filter ) == VLC_ENOMEM )
+ goto error;
+
#ifdef HAVE_FONTCONFIG
p_sys->pf_select = FontConfig_Select;
FontConfig_BuildCache( p_filter );
@@ -1213,64 +1271,42 @@ static int Create( vlc_object_t *p_this )
p_sys->pf_select = Dummy_Select;
#endif
- /* */
- psz_fontfile = p_sys->pf_select( p_filter, p_sys->p_default_style->psz_fontname,
- false, false, p_sys->p_default_style->i_font_size, &fontindex );
- psz_monofontfile = p_sys->pf_select( p_filter, p_sys->p_default_style->psz_monofontname,
- false, false, p_sys->p_default_style->i_font_size,
- &monofontindex );
- msg_Dbg( p_filter, "Using %s as font from file %s",
- p_sys->p_default_style->psz_fontname, psz_fontfile );
- msg_Dbg( p_filter, "Using %s as mono-font from file %s",
- p_sys->p_default_style->psz_monofontname, psz_monofontfile );
-
- /* If nothing is found, use the default family */
- if( !psz_fontfile )
- psz_fontfile = File_Select( p_sys->p_default_style->psz_fontname );
- if( !psz_monofontfile )
- psz_monofontfile = File_Select( p_sys->p_default_style->psz_monofontname );
-
- if( Init_FT( p_this, psz_fontfile, fontindex ) != VLC_SUCCESS )
+ p_sys->p_face = SelectAndLoadFace( p_filter, p_sys->p_default_style, 0 );
+ if( !p_sys->p_face )
+ {
+ msg_Err( p_filter, "Error loading default face" );
goto error;
-
- int i_faces_size = 20;
- p_sys->faces_cache.p_faces = malloc( i_faces_size * sizeof( *p_sys->faces_cache.p_faces ) );
- p_sys->faces_cache.p_styles = malloc( i_faces_size * sizeof( *p_sys->faces_cache.p_styles ) );
- p_sys->faces_cache.i_cache_size = i_faces_size;
- p_sys->faces_cache.i_faces_count = 0;
-
- p_sys->pp_font_attachments = NULL;
- p_sys->i_font_attachments = 0;
+ }
p_filter->pf_render = Render;
- LoadFontsFromAttachments( p_filter );
-
- free( psz_fontfile );
- free( psz_monofontfile );
-
return VLC_SUCCESS;
error:
- free( psz_fontfile );
- free( psz_monofontfile );
text_style_Delete( p_sys->p_default_style );
text_style_Delete( p_sys->p_forced_style );
- free( p_sys );
- return VLC_EGENERIC;
-}
+ vlc_dictionary_clear( &p_sys->fallback_map, FreeFamilies, p_filter );
+ vlc_dictionary_clear( &p_sys->face_map, FreeFace, p_filter );
+ vlc_dictionary_clear( &p_sys->family_map, NULL, NULL );
+ if( p_sys->p_families )
+ FreeFamiliesAndFonts( p_sys->p_families );
-static void Destroy_FT( vlc_object_t *p_this )
-{
- filter_t *p_filter = (filter_t *)p_this;
- filter_sys_t *p_sys = p_filter->p_sys;
+ if( p_sys->pp_font_attachments )
+ {
+ for( int k = 0; k < p_sys->i_font_attachments; k++ )
+ vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
+
+ free( p_sys->pp_font_attachments );
+ }
if( p_sys->p_stroker )
FT_Stroker_Done( p_sys->p_stroker );
- FT_Done_Face( p_sys->p_face );
FT_Done_FreeType( p_sys->p_library );
+
+ free( p_sys );
+ return VLC_EGENERIC;
}
/*****************************************************************************
@@ -1283,14 +1319,26 @@ static void Destroy( vlc_object_t *p_this )
filter_t *p_filter = (filter_t *)p_this;
filter_sys_t *p_sys = p_filter->p_sys;
- faces_cache_t *p_cache = &p_sys->faces_cache;
- for( int i = 0; i < p_cache->i_faces_count; ++i )
- {
- FT_Done_Face( p_cache->p_faces[ i ] );
- free( p_cache->p_styles[ i ].psz_fontname );
- }
- free( p_sys->faces_cache.p_faces );
- free( p_sys->faces_cache.p_styles );
+#if 0
+ msg_Dbg( p_filter, "------------------" );
+ msg_Dbg( p_filter, "p_sys->p_families:" );
+ msg_Dbg( p_filter, "------------------" );
+ DumpFamily( p_filter, p_sys->p_families, true, -1 );
+ msg_Dbg( p_filter, "-----------------" );
+ msg_Dbg( p_filter, "p_sys->family_map" );
+ msg_Dbg( p_filter, "-----------------" );
+ DumpDictionary( p_filter, &p_sys->family_map, false, 1 );
+ msg_Dbg( p_filter, "-------------------" );
+ msg_Dbg( p_filter, "p_sys->fallback_map" );
+ msg_Dbg( p_filter, "-------------------" );
+ DumpDictionary( p_filter, &p_sys->fallback_map, true, -1 );
+#endif
+
+ vlc_dictionary_clear( &p_sys->fallback_map, FreeFamilies, p_filter );
+ vlc_dictionary_clear( &p_sys->face_map, FreeFace, p_filter );
+ vlc_dictionary_clear( &p_sys->family_map, NULL, NULL );
+ if( p_sys->p_families )
+ FreeFamiliesAndFonts( p_sys->p_families );
if( p_sys->pp_font_attachments )
{
@@ -1303,59 +1351,19 @@ static void Destroy( vlc_object_t *p_this )
text_style_Delete( p_sys->p_default_style );
text_style_Delete( p_sys->p_forced_style );
- Destroy_FT( p_this );
- free( p_sys );
-}
-
-/* Face loading */
-bool FaceStyleEquals( const text_style_t *p_style1,
- const text_style_t *p_style2 )
-{
- if( !p_style1 || !p_style2 )
- return false;
- if( p_style1 == p_style2 )
- return true;
-
- const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
- return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
- !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
-}
+ if( p_sys->p_stroker )
+ FT_Stroker_Done( p_sys->p_stroker );
-static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const char *psz_fontname,
- const text_style_t *p_style )
-{
- for( int k = 0; k < p_sys->i_font_attachments; k++ )
- {
- input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
- int i_font_idx = 0;
- FT_Face p_face = NULL;
-
- while( 0 == FT_New_Memory_Face( p_sys->p_library,
- p_attach->p_data,
- p_attach->i_data,
- i_font_idx,
- &p_face ))
- {
- if( p_face )
- {
- int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
- ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
- if( p_face->family_name != NULL
- && !strcasecmp( p_face->family_name, psz_fontname )
- && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
- == i_style_received )
- return p_face;
+ FT_Done_FreeType( p_sys->p_library );
- FT_Done_Face( p_face );
- }
- i_font_idx++;
- }
- }
- return NULL;
+ free( p_sys );
}
+/* Face loading */
int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style )
{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
int i_font_size = STYLE_DEFAULT_FONT_SIZE;
if( p_style->i_font_size )
{
@@ -1365,115 +1373,118 @@ int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style )
{
i_font_size = (int) p_filter->fmt_out.video.i_height * p_style->f_font_relsize;
}
+
+ if( p_sys->i_scale != 100 )
+ i_font_size = i_font_size * p_sys->i_scale / 100;
+
return i_font_size;
}
-FT_Face LoadFace( filter_t *p_filter,
- const text_style_t *p_style, int i_font_size )
+FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx,
+ const text_style_t *p_style )
{
filter_sys_t *p_sys = p_filter->p_sys;
+ char *psz_key = NULL;
- faces_cache_t *p_cache = &p_sys->faces_cache;
- for( int i = 0; i < p_cache->i_faces_count; ++i )
- if( FaceStyleEquals( &p_cache->p_styles[ i ], p_style )
- && p_cache->p_styles[ i ].i_font_size == i_font_size
- && !( ( p_cache->p_styles[ i ].i_style_flags ^ p_style->i_style_flags ) & STYLE_HALFWIDTH ) )
- return p_cache->p_faces[ i ];
+ int i_font_size = ConvertToLiveSize( p_filter, p_style );
+ int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ?
+ i_font_size / 2 : i_font_size;
- const char *psz_fontname = (p_style->i_style_flags & STYLE_MONOSPACED)
- ? p_style->psz_monofontname : p_style->psz_fontname;
+ if( asprintf( &psz_key, "%s - %d - %d - %d",
+ psz_fontfile, i_idx,
+ i_font_size, i_font_width ) < 0 )
+ return NULL;
- /* Look for a match amongst our attachments first */
- FT_Face p_face = LoadEmbeddedFace( p_sys, psz_fontname, p_style );
+ FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->face_map, psz_key );
+ if( p_face != kVLCDictionaryNotFound )
+ goto done;
- /* Load system wide font otheriwse */
- if( !p_face )
+ if( psz_fontfile[0] == ':' && psz_fontfile[1] == '/' )
{
- int i_idx = 0;
- char *psz_fontfile = NULL;
- if( p_sys->pf_select )
- psz_fontfile = p_sys->pf_select( p_filter,
- psz_fontname,
- (p_style->i_style_flags & STYLE_BOLD) != 0,
- (p_style->i_style_flags & STYLE_ITALIC) != 0,
- -1,
- &i_idx );
- else
- psz_fontfile = NULL;
-
- if( !psz_fontfile )
- return NULL;
-
- if( *psz_fontfile == '\0' )
+ int i_attach = atoi( psz_fontfile + 2 );
+ if( i_attach < 0 || i_attach >= p_sys->i_font_attachments )
{
- msg_Warn( p_filter,
- "We were not able to find a matching font: \"%s\" (%s %s),"
- " so using default font",
- psz_fontname,
- (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
- (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
+ msg_Err( p_filter, "LoadFace: Invalid font attachment index" );
p_face = NULL;
}
else
{
- if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
+ input_attachment_t *p_attach = p_sys->pp_font_attachments[ i_attach ];
+ if( FT_New_Memory_Face( p_sys->p_library, p_attach->p_data,
+ p_attach->i_data, i_idx, &p_face ) )
p_face = NULL;
}
- free( psz_fontfile );
}
+ else
+ if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
+ {
+ msg_Err( p_filter, "LoadFace: Error creating face for %s", psz_key );
+ p_face = NULL;
+ }
+
if( !p_face )
- return NULL;
+ goto done;
if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
{
/* We've loaded a font face which is unhelpful for actually
* rendering text - fallback to the default one.
*/
+ msg_Err( p_filter, "LoadFace: Error selecting charmap for %s", psz_key );
FT_Done_Face( p_face );
- return NULL;
+ p_face = NULL;
+ goto done;
}
- int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH
- ? i_font_size / 2 : i_font_size;
-
if( FT_Set_Pixel_Sizes( p_face, i_font_width, i_font_size ) )
{
msg_Err( p_filter,
- "Failed to set font size to %d", i_font_size );
+ "LoadFace: Failed to set font size for %s", psz_key );
FT_Done_Face( p_face );
- return NULL;
+ p_face = NULL;
+ goto done;
}
- if( p_cache->i_faces_count == p_cache->i_cache_size )
- {
- FT_Face *p_new_faces =
- realloc( p_cache->p_faces, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_faces ) );
- if( !p_new_faces )
- {
- FT_Done_Face( p_face );
- return NULL;
- }
+ vlc_dictionary_insert( &p_sys->face_map, psz_key, p_face );
- p_cache->p_faces = p_new_faces;
+done:
+ free( psz_key );
+ return p_face;
+}
- text_style_t *p_new_styles =
- realloc( p_cache->p_styles, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_styles ) ) ;
- if( !p_new_styles )
- {
- FT_Done_Face( p_face );
- return NULL;
- }
+FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
+ uni_char_t codepoint )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
- p_cache->p_styles = p_new_styles;
- p_cache->i_cache_size *= 2;
+ const char *psz_fontname = (p_style->i_style_flags & STYLE_MONOSPACED)
+ ? p_style->psz_monofontname : p_style->psz_fontname;
+
+ bool b_bold = p_style->i_style_flags & STYLE_BOLD;
+ bool b_italic = p_style->i_style_flags & STYLE_ITALIC;
+
+ FT_Face p_face = NULL;
+
+
+ int i_idx = 0;
+ char *psz_fontfile = NULL;
+ if( p_sys->pf_select )
+ psz_fontfile = p_sys->pf_select( p_filter, psz_fontname, b_bold, b_italic,
+ &i_idx, codepoint );
+ else
+ psz_fontfile = NULL;
+
+ if( !psz_fontfile || *psz_fontfile == '\0' )
+ {
+ msg_Warn( p_filter,
+ "SelectAndLoadFace: no font found for family: %s, codepoint: 0x%x",
+ psz_fontname, codepoint );
+ free( psz_fontfile );
+ return NULL;
}
- text_style_t *p_face_style = p_cache->p_styles + p_cache->i_faces_count;
- p_face_style->i_font_size = i_font_size;
- p_face_style->i_style_flags = p_style->i_style_flags;
- p_face_style->psz_fontname = strdup( psz_fontname );
- p_cache->p_faces[ p_cache->i_faces_count ] = p_face;
- ++p_cache->i_faces_count;
+ p_face = LoadFace( p_filter, psz_fontfile, i_idx, p_style );
+ free( psz_fontfile );
return p_face;
}
diff --git a/modules/text_renderer/freetype.h b/modules/text_renderer/freetype.h
index ae7c36f..7ec2e61 100644
--- a/modules/text_renderer/freetype.h
+++ b/modules/text_renderer/freetype.h
@@ -29,14 +29,14 @@
#define VLC_FREETYPE_H
#include <vlc_text_style.h> /* text_style_t*/
+#include <vlc_arrays.h>
-typedef struct faces_cache_t
-{
- FT_Face *p_faces;
- text_style_t *p_styles;
- int i_faces_count;
- int i_cache_size;
-} faces_cache_t;
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_STROKER_H
+
+#include "platform_fonts.h"
/*****************************************************************************
* filter_sys_t: freetype local data
@@ -61,13 +61,48 @@ struct filter_sys_t
input_attachment_t **pp_font_attachments;
int i_font_attachments;
- /* Font faces cache */
- faces_cache_t faces_cache;
+ /*
+ * This is the master family list. It owns the lists of vlc_font_t's
+ * and should be freed using FreeFamiliesAndFonts()
+ */
+ vlc_family_t *p_families;
- char * (*pf_select) (filter_t *, const char* family,
- bool bold, bool italic, int size,
- int *index);
+ /*
+ * This maps a family name to a vlc_family_t within the master list
+ */
+ vlc_dictionary_t family_map;
+
+ /*
+ * This maps a family name to a fallback list of vlc_family_t's.
+ * Fallback lists only reference the lists of vlc_font_t's within the
+ * master list, so they should be freed using FreeFamilies()
+ */
+ vlc_dictionary_t fallback_map;
+
+ /* Font face cache */
+ vlc_dictionary_t face_map;
+ int i_fallback_counter;
+ int i_scale;
+
+ char * (*pf_select) (filter_t *, const char* family,
+ bool bold, bool italic,
+ int *index, uni_char_t codepoint);
+
+ /*
+ * Get a pointer to the vlc_family_t in the master list that matches psz_family.
+ * Add this family to the list if it hasn't been added yet.
+ */
+ const vlc_family_t * (*pf_get_family) ( filter_t *p_filter, const char *psz_family );
+
+ /*
+ * Get the fallback list for psz_family from the system and cache
+ * it in fallback_map.
+ * On Windows fallback lists are populated progressively as required
+ * using Uniscribe, so we need the codepoint here.
+ */
+ vlc_family_t * (*pf_get_fallbacks) ( filter_t *p_filter, const char *psz_family,
+ uni_char_t codepoint );
};
#define FT_FLOOR(X) ((X & -64) >> 6)
@@ -76,23 +111,12 @@ struct filter_sys_t
#define FT_MulFix(v, s) (((v)*(s))>>16)
#endif
-#ifdef __OS2__
-typedef uint16_t uni_char_t;
-# define FREETYPE_TO_UCS "UCS-2LE"
-#else
-typedef uint32_t uni_char_t;
-# if defined(WORDS_BIGENDIAN)
-# define FREETYPE_TO_UCS "UCS-4BE"
-# else
-# define FREETYPE_TO_UCS "UCS-4LE"
-# endif
-#endif
+FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx,
+ const text_style_t *p_style );
+FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
+ uni_char_t codepoint );
-FT_Face LoadFace( filter_t *p_filter, const text_style_t *p_style, int );
int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style );
-bool FaceStyleEquals( const text_style_t *p_style1,
- const text_style_t *p_style2 );
-
#endif
diff --git a/modules/text_renderer/platform_fonts.c b/modules/text_renderer/platform_fonts.c
index 7869dba..1195272 100644
--- a/modules/text_renderer/platform_fonts.c
+++ b/modules/text_renderer/platform_fonts.c
@@ -9,6 +9,7 @@
* Bernie Purcell <bitmap at videolan.org>
* Jean-Baptiste Kempf <jb at videolan.org>
* Felix Paul Kühne <fkuehne at videolan.org>
+ * Salah-Eddin Shaban <salshaaban at gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
@@ -36,6 +37,7 @@
#include <vlc_common.h>
#include <vlc_filter.h> /* filter_sys_t */
#include <vlc_text_style.h> /* text_style_t*/
+#include <ctype.h>
/* apple stuff */
#ifdef __APPLE__
@@ -60,6 +62,359 @@
#endif
#include "platform_fonts.h"
+#include "freetype.h"
+
+static FT_Face GetFace( filter_t *p_filter, vlc_font_t *p_font )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ if( p_font->p_face )
+ return p_font->p_face;
+
+ p_font->p_face = LoadFace( p_filter, p_font->psz_fontfile, p_font->i_index,
+ p_sys->p_default_style );
+
+ return p_font->p_face;
+}
+
+static vlc_font_t *GetBestFont( filter_t *p_filter, const vlc_family_t *p_family,
+ bool b_bold, bool b_italic, uni_char_t codepoint )
+{
+ int i_best_score = 0;
+ vlc_font_t *p_best_font = p_family->p_fonts;
+
+ for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
+ {
+ int i_score = 0;
+
+ if( codepoint )
+ {
+ FT_Face p_face = GetFace( p_filter, p_font );
+ if( p_face && FT_Get_Char_Index( p_face, codepoint ) )
+ i_score += 1000;
+ }
+
+ if( !!p_font->b_bold == !!b_bold )
+ i_score += 100;
+ if( !!p_font->b_italic == !!b_italic )
+ i_score += 10;
+
+ if( i_score > i_best_score )
+ {
+ p_best_font = p_font;
+ i_best_score = i_score;
+ }
+ }
+
+ return p_best_font;
+}
+
+static vlc_family_t *SearchFallbacks( filter_t *p_filter, vlc_family_t *p_fallbacks,
+ uni_char_t codepoint )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ vlc_family_t *p_family = NULL;
+
+ for( vlc_family_t *p_fallback = p_fallbacks; p_fallback;
+ p_fallback = p_fallback->p_next )
+ {
+ if( !p_fallback->p_fonts )
+ {
+ const vlc_family_t *p_temp =
+ p_sys->pf_get_family( p_filter, p_fallback->psz_name );
+ if( !p_temp || !p_temp->p_fonts )
+ continue;
+ p_fallback->p_fonts = p_temp->p_fonts;
+ }
+
+ FT_Face p_face = GetFace( p_filter, p_fallback->p_fonts );
+ if( !p_face || !FT_Get_Char_Index( p_face, codepoint ) )
+ continue;
+ p_family = p_fallback;
+ break;
+ }
+
+ return p_family;
+}
+
+vlc_family_t *NewFamily( filter_t *p_filter, const char *psz_family,
+ vlc_family_t **pp_list, vlc_dictionary_t *p_dict,
+ const char *psz_key )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ vlc_family_t *p_family = NULL;
+
+ p_family = calloc( 1, sizeof( *p_family ) );
+
+ char *psz_name;
+ if( psz_family && *psz_family )
+ psz_name = ToLower( psz_family );
+ else
+ if( asprintf( &psz_name, FB_NAME"-%02d",
+ p_sys->i_fallback_counter++ ) < 0 )
+ psz_name = NULL;
+
+ char *psz_lc = NULL;
+ if( likely( psz_name ) )
+ {
+ if( !psz_key )
+ psz_lc = strdup( psz_name );
+ else
+ psz_lc = ToLower( psz_key );
+ }
+
+ if( unlikely( !p_family || !psz_name || !psz_lc ) )
+ {
+ free( p_family );
+ free( psz_name );
+ free( psz_lc );
+ return NULL;
+ }
+
+ p_family->psz_name = psz_name;
+
+ if( pp_list )
+ AppendFamily( pp_list, p_family );
+
+ if( p_dict )
+ {
+ vlc_family_t *p_root = vlc_dictionary_value_for_key( p_dict, psz_lc );
+ if( p_root )
+ AppendFamily( &p_root, p_family );
+ else
+ vlc_dictionary_insert( p_dict, psz_lc, p_family );
+ }
+
+ free( psz_lc );
+ return p_family;
+}
+
+vlc_font_t *NewFont( char *psz_fontfile, int i_index,
+ bool b_bold, bool b_italic,
+ vlc_family_t *p_parent )
+{
+ vlc_font_t *p_font = calloc( 1, sizeof( *p_font ) );
+
+ if( unlikely( !p_font ) )
+ {
+ free( psz_fontfile );
+ return NULL;
+ }
+
+ p_font->psz_fontfile = psz_fontfile;
+ p_font->i_index = i_index;
+ p_font->b_bold = b_bold;
+ p_font->b_italic = b_italic;
+
+ if( p_parent )
+ {
+ /* Keep regular faces first */
+ if( p_parent->p_fonts
+ && ( p_parent->p_fonts->b_bold || p_parent->p_fonts->b_italic )
+ && !b_bold && !b_italic )
+ {
+ p_font->p_next = p_parent->p_fonts;
+ p_parent->p_fonts = p_font;
+ }
+ else
+ AppendFont( &p_parent->p_fonts, p_font );
+ }
+
+ return p_font;
+}
+
+void FreeFamiliesAndFonts( vlc_family_t *p_family )
+{
+ if( p_family->p_next )
+ FreeFamiliesAndFonts( p_family->p_next );
+
+ for( vlc_font_t *p_font = p_family->p_fonts; p_font; )
+ {
+ vlc_font_t *p_temp = p_font->p_next;
+ free( p_font->psz_fontfile );
+ free( p_font );
+ p_font = p_temp;
+ }
+
+ free( p_family->psz_name );
+ free( p_family );
+}
+
+void FreeFamilies( void *p_families, void *p_obj )
+{
+ vlc_family_t *p_family = ( vlc_family_t * ) p_families;
+
+ if( p_family->p_next )
+ FreeFamilies( p_family->p_next, p_obj );
+
+ free( p_family->psz_name );
+ free( p_family );
+}
+
+vlc_family_t *InitDefaultList( filter_t *p_filter, const char *const *ppsz_default,
+ int i_size )
+{
+
+ vlc_family_t *p_default = NULL;
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ for( int i = 0; i < i_size; ++i )
+ {
+ const vlc_family_t *p_family =
+ p_sys->pf_get_family( p_filter, ppsz_default[ i ] );
+
+ if( p_family )
+ {
+ vlc_family_t *p_temp =
+ NewFamily( p_filter, ppsz_default[ i ], &p_default, NULL, NULL );
+
+ if( unlikely( !p_temp ) )
+ goto error;
+
+ p_temp->p_fonts = p_family->p_fonts;
+ }
+ }
+
+ if( p_default )
+ vlc_dictionary_insert( &p_sys->fallback_map, FB_LIST_DEFAULT, p_default );
+
+ return p_default;
+
+error:
+ if( p_default ) FreeFamilies( p_default, NULL );
+ return NULL;
+}
+
+void DumpFamily( filter_t *p_filter, const vlc_family_t *p_family,
+ bool b_dump_fonts, int i_max_families )
+{
+
+ if( i_max_families < 0 )
+ i_max_families = INT_MAX;
+
+ for( int i = 0; p_family && i < i_max_families ; p_family = p_family->p_next, ++i )
+ {
+ msg_Dbg( p_filter, "\t[0x%"PRIxPTR"] %s",
+ ( uintptr_t ) p_family, p_family->psz_name );
+
+ if( b_dump_fonts )
+ {
+ for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
+ {
+ const char *psz_style = NULL;
+ if( !p_font->b_bold && !p_font->b_italic )
+ psz_style = "Regular";
+ else if( p_font->b_bold && !p_font->b_italic )
+ psz_style = "Bold";
+ else if( !p_font->b_bold && p_font->b_italic )
+ psz_style = "Italic";
+ else if( p_font->b_bold && p_font->b_italic )
+ psz_style = "Bold Italic";
+
+ msg_Dbg( p_filter, "\t\t[0x%"PRIxPTR"] (%s): %s - %d",
+ ( uintptr_t ) p_font, psz_style,
+ p_font->psz_fontfile, p_font->i_index );
+
+ }
+
+ }
+ }
+}
+
+void DumpDictionary( filter_t *p_filter, const vlc_dictionary_t *p_dict,
+ bool b_dump_fonts, int i_max_families )
+{
+ char **ppsz_keys = vlc_dictionary_all_keys( p_dict );
+ for( int i = 0; ppsz_keys[ i ]; ++i )
+ {
+ vlc_family_t *p_family = vlc_dictionary_value_for_key( p_dict, ppsz_keys[ i ] );
+ msg_Dbg( p_filter, "Key: %s", ppsz_keys[ i ] );
+ if( p_family )
+ DumpFamily( p_filter, p_family, b_dump_fonts, i_max_families );
+ free( ppsz_keys[ i ] );
+ }
+ free( ppsz_keys );
+}
+
+char* ToLower( const char *psz_src )
+{
+ int i_size = strlen( psz_src ) + 1;
+ char *psz_buffer = malloc( i_size );
+ if( unlikely( !psz_buffer ) )
+ return NULL;
+
+ for( int i = 0; i < i_size; ++i )
+ psz_buffer[ i ] = tolower( psz_src[ i ] );
+
+ return psz_buffer;
+}
+
+char* Generic_Select( filter_t *p_filter, const char* psz_family,
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint )
+{
+
+ filter_sys_t *p_sys = p_filter->p_sys;
+ const vlc_family_t *p_family = NULL;
+ vlc_family_t *p_fallbacks = NULL;
+
+ if( codepoint )
+ {
+ /*
+ * Try regular face of the same family first.
+ * It usually has the best coverage.
+ */
+ const vlc_family_t *p_temp = p_sys->pf_get_family( p_filter, psz_family );
+ if( p_temp && p_temp->p_fonts )
+ {
+ FT_Face p_face = GetFace( p_filter, p_temp->p_fonts );
+ if( p_face && FT_Get_Char_Index( p_face, codepoint ) )
+ p_family = p_temp;
+ }
+
+ /* Try font attachments */
+ if( !p_family )
+ {
+ p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map,
+ FB_LIST_ATTACHMENTS );
+ if( p_fallbacks )
+ p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
+ }
+
+ /* Try system fallbacks */
+ if( !p_family )
+ {
+ p_fallbacks = p_sys->pf_get_fallbacks( p_filter, psz_family, codepoint );
+ if( p_fallbacks )
+ p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
+ }
+
+ /* Try the default fallback list, if any */
+ if( !p_family )
+ {
+ p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map,
+ FB_LIST_DEFAULT );
+ if( p_fallbacks )
+ p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
+ }
+
+ if( !p_family )
+ return NULL;
+ }
+
+ if( !p_family )
+ p_family = p_sys->pf_get_family( p_filter, psz_family );
+
+ vlc_font_t *p_font;
+ if( p_family && ( p_font = GetBestFont( p_filter, p_family, b_bold,
+ b_italic, codepoint ) ) )
+ {
+ *i_idx = p_font->i_index;
+ return strdup( p_font->psz_fontfile );
+ }
+
+ return File_Select( SYSTEM_DEFAULT_FONT_FILE );
+}
#ifdef HAVE_FONTCONFIG
void FontConfig_BuildCache( filter_t *p_filter )
@@ -110,7 +465,8 @@ void FontConfig_BuildCache( filter_t *p_filter )
* \brief Selects a font matching family, bold, italic provided
***/
char* FontConfig_Select( filter_t *p_filter, const char* family,
- bool b_bold, bool b_italic, int i_size, int *i_idx )
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint )
{
FcResult result = FcResultMatch;
FcPattern *pat, *p_pat;
@@ -119,6 +475,7 @@ char* FontConfig_Select( filter_t *p_filter, const char* family,
char *ret = NULL;
FcConfig* config = NULL;
VLC_UNUSED(p_filter);
+ VLC_UNUSED(codepoint);
/* Create a pattern and fills it */
pat = FcPatternCreate();
@@ -129,10 +486,6 @@ char* FontConfig_Select( filter_t *p_filter, const char* family,
FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
- if( i_size > 0 )
- {
- FcPatternAddDouble( pat, FC_SIZE, (double)i_size );
- }
/* */
FcDefaultSubstitute( pat );
@@ -259,9 +612,10 @@ static char* GetWindowsFontPath()
}
char* Win32_Select( filter_t *p_filter, const char* family,
- bool b_bold, bool b_italic, int i_size, int *i_idx )
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint )
{
- VLC_UNUSED( i_size );
+ VLC_UNUSED( codepoint );
VLC_UNUSED( i_idx );
VLC_UNUSED( p_filter );
@@ -328,11 +682,12 @@ fail:
#ifdef __APPLE__
#if !TARGET_OS_IPHONE
char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
- bool b_bold, bool b_italic, int i_size, int *i_idx )
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint )
{
VLC_UNUSED( b_bold );
VLC_UNUSED( b_italic );
- VLC_UNUSED( i_size );
+ VLC_UNUSED( codepoint );
FSRef ref;
unsigned char path[MAXPATHLEN];
char * psz_path;
@@ -409,12 +764,13 @@ char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
#endif
char* Dummy_Select( filter_t *p_filter, const char* psz_font,
- bool b_bold, bool b_italic, int i_size, int *i_idx )
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint )
{
VLC_UNUSED(p_filter);
VLC_UNUSED(b_bold);
VLC_UNUSED(b_italic);
- VLC_UNUSED(i_size);
+ VLC_UNUSED(codepoint);
VLC_UNUSED(i_idx);
char *psz_fontname;
diff --git a/modules/text_renderer/platform_fonts.h b/modules/text_renderer/platform_fonts.h
index cff52b1..ce788ec 100644
--- a/modules/text_renderer/platform_fonts.h
+++ b/modules/text_renderer/platform_fonts.h
@@ -9,6 +9,7 @@
* Bernie Purcell <bitmap at videolan.org>
* Jean-Baptiste Kempf <jb at videolan.org>
* Felix Paul Kühne <fkuehne at videolan.org>
+ * Salah-Eddin Shaban <salshaaban at gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
@@ -29,10 +30,28 @@
* Preamble
*****************************************************************************/
+#ifndef PLATFORM_FONTS_H
+#define PLATFORM_FONTS_H
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef __OS2__
+typedef uint16_t uni_char_t;
+# define FREETYPE_TO_UCS "UCS-2LE"
+#else
+typedef uint32_t uni_char_t;
+# if defined(WORDS_BIGENDIAN)
+# define FREETYPE_TO_UCS "UCS-4BE"
+# else
+# define FREETYPE_TO_UCS "UCS-4LE"
+# endif
+#endif
+
/* Default fonts */
#ifdef __APPLE__
# define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
@@ -77,28 +96,100 @@
#define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
#endif
+typedef struct vlc_font_t vlc_font_t;
+struct vlc_font_t
+{
+ vlc_font_t *p_next;
+ char *psz_fontfile;
+ int i_index;
+ bool b_bold;
+ bool b_italic;
+ FT_Face p_face;
+};
+
+typedef struct vlc_family_t vlc_family_t;
+struct vlc_family_t
+{
+ vlc_family_t *p_next;
+ char *psz_name;
+ vlc_font_t *p_fonts;
+};
+
+#define FB_LIST_ATTACHMENTS "attachments"
+#define FB_LIST_DEFAULT "default"
+#define FB_NAME "fallback"
#ifdef HAVE_FONTCONFIG
char* FontConfig_Select( filter_t *p_filter, const char* family,
- bool b_bold, bool b_italic, int i_size, int *i_idx );
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint );
void FontConfig_BuildCache( filter_t *p_filter );
#endif
#if defined( _WIN32 ) && !VLC_WINSTORE_APP
char* Win32_Select( filter_t *p_filter, const char* family,
- bool b_bold, bool b_italic, int i_size, int *i_idx );
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint );
#endif /* _WIN32 */
#ifdef __APPLE__
#if !TARGET_OS_IPHONE
char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
- bool b_bold, bool b_italic, int i_size, int *i_idx );
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint );
#endif
#endif
char* Dummy_Select( filter_t *p_filter, const char* family,
- bool b_bold, bool b_italic, int i_size, int *i_idx );
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint );
+
+#define File_Select(a) Dummy_Select(NULL, a, 0, 0, NULL, 0)
+
+char* Generic_Select( filter_t *p_filter, const char* family,
+ bool b_bold, bool b_italic,
+ int *i_idx, uni_char_t codepoint );
+
+static inline void AppendFont( vlc_font_t **pp_list, vlc_font_t *p_font )
+{
+ while( *pp_list )
+ pp_list = &( *pp_list )->p_next;
+
+ *pp_list = p_font;
+}
+
+static inline void AppendFamily( vlc_family_t **pp_list, vlc_family_t *p_family )
+{
+ while( *pp_list )
+ pp_list = &( *pp_list )->p_next;
+
+ *pp_list = p_family;
+}
+
+vlc_family_t *NewFamily( filter_t *p_filter, const char *psz_family,
+ vlc_family_t **pp_list, vlc_dictionary_t *p_dict,
+ const char *psz_key );
+
+/* This function takes ownership of psz_fontfile */
+vlc_font_t *NewFont( char *psz_fontfile, int i_index,
+ bool b_bold, bool b_italic,
+ vlc_family_t *p_parent );
+
+void FreeFamiliesAndFonts( vlc_family_t *p_family );
+void FreeFamilies( void *p_families, void *p_obj );
+
+
+vlc_family_t *InitDefaultList( filter_t *p_filter, const char *const *ppsz_default,
+ int i_size );
+
+void DumpFamily( filter_t *p_filter, const vlc_family_t *p_family,
+ bool b_dump_fonts, int i_max_families );
+
+void DumpDictionary( filter_t *p_filter, const vlc_dictionary_t *p_dict,
+ bool b_dump_fonts, int i_max_families );
+
+char* ToLower( const char *psz_src );
-#define File_Select(a) Dummy_Select(NULL, a, 0, 0, 0, NULL)
+#endif //PLATFORM_FONTS_H
diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index f028502..a3386f9 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -34,7 +34,6 @@
#endif
#include <vlc_common.h>
-#include <vlc_charset.h>
#include <vlc_filter.h>
#include <vlc_text_style.h>
@@ -105,6 +104,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;
+ FT_Face *pp_faces;
int *pi_run_ids; //The run to which each glyph belongs
glyph_bitmaps_t *p_glyph_bitmaps;
uint8_t *pi_karaoke_bar;
@@ -225,6 +225,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
malloc( i_size * sizeof( *p_paragraph->pi_glyph_indices ) );
p_paragraph->pp_styles =
malloc( i_size * sizeof( *p_paragraph->pp_styles ) );
+ p_paragraph->pp_faces =
+ calloc( i_size, sizeof( *p_paragraph->pp_faces ) );
p_paragraph->pi_run_ids =
calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) );
p_paragraph->p_glyph_bitmaps =
@@ -237,9 +239,9 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
p_paragraph->i_runs_count = 0;
if( !p_paragraph->p_code_points || !p_paragraph->pi_glyph_indices
- || !p_paragraph->pp_styles || !p_paragraph->pi_run_ids
- || !p_paragraph->p_glyph_bitmaps || !p_paragraph->pi_karaoke_bar
- || !p_paragraph->p_runs )
+ || !p_paragraph->pp_styles || !p_paragraph->pp_faces
+ || !p_paragraph->pi_run_ids|| !p_paragraph->p_glyph_bitmaps
+ || !p_paragraph->pi_karaoke_bar || !p_paragraph->p_runs )
goto error;
if( p_code_points )
@@ -288,6 +290,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_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 );
if (p_paragraph->pi_karaoke_bar ) free( p_paragraph->pi_karaoke_bar );
@@ -312,6 +315,7 @@ static void FreeParagraph( paragraph_t *p_paragraph )
free( p_paragraph->p_glyph_bitmaps );
free( p_paragraph->pi_karaoke_bar );
free( p_paragraph->pi_run_ids );
+ free( p_paragraph->pp_faces );
free( p_paragraph->pp_styles );
free( p_paragraph->p_code_points );
@@ -385,7 +389,8 @@ static int AddRun( filter_t *p_filter,
paragraph_t *p_paragraph,
int i_start_offset,
int i_end_offset,
- FT_Face p_face )
+ FT_Face p_face,
+ const text_style_t *p_style )
{
if( i_start_offset >= i_end_offset
|| i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
@@ -417,9 +422,13 @@ static int AddRun( filter_t *p_filter,
run_desc_t *p_run = p_paragraph->p_runs + p_paragraph->i_runs_count++;
p_run->i_start_offset = i_start_offset;
p_run->i_end_offset = i_end_offset;
- p_run->p_style = p_paragraph->pp_styles[ i_start_offset ];
p_run->p_face = p_face;
+ if( p_style )
+ p_run->p_style = p_style;
+ else
+ p_run->p_style = p_paragraph->pp_styles[ i_start_offset ];
+
#ifdef HAVE_HARFBUZZ
p_run->script = p_paragraph->p_scripts[ i_start_offset ];
p_run->direction = p_paragraph->p_levels[ i_start_offset ] & 1 ?
@@ -433,6 +442,109 @@ static int AddRun( filter_t *p_filter,
}
/*
+ * Add a run with font fallback, possibly breaking the run further
+ * into runs of glyphs that end up having the same font face.
+ */
+#ifdef HAVE_FONT_FALLBACK
+static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
+ int i_start_offset, int i_end_offset )
+{
+ if( i_start_offset >= i_end_offset
+ || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
+ || i_end_offset <= 0 || i_end_offset > p_paragraph->i_size )
+ {
+ msg_Err( p_filter,
+ "AddRunWithFallback() invalid parameters. Paragraph size: %d, "
+ "Start offset: %d, End offset: %d",
+ p_paragraph->i_size, i_start_offset, i_end_offset );
+ return VLC_EGENERIC;
+ }
+
+ const text_style_t *p_style = p_paragraph->pp_styles[ i_start_offset ];
+
+ /* Maximum number of faces to try for each run */
+ #define MAX_FACES 5
+ FT_Face pp_faces[ MAX_FACES ] = {0};
+
+ pp_faces[ 0 ] = SelectAndLoadFace( p_filter, p_style, 0 );
+
+ for( int i = i_start_offset; i < i_end_offset; ++i )
+ {
+ int i_index = 0;
+ int i_glyph_index = 0;
+ FT_Face p_face = NULL;
+ do {
+ p_face = pp_faces[ i_index ];
+ if( !p_face )
+ p_face = pp_faces[ i_index ] =
+ SelectAndLoadFace( p_filter, p_style,
+ p_paragraph->p_code_points[ i ] );
+ if( !p_face )
+ continue;
+ i_glyph_index = FT_Get_Char_Index( p_face,
+ p_paragraph->p_code_points[ i ] );
+ if( i_glyph_index )
+ {
+ p_paragraph->pp_faces[ i ] = p_face;
+
+ /*
+ * Move p_face to the beginning of the array. Otherwise strikethrough
+ * lines can appear segmented, being rendered at a certain height
+ * through spaces and at a different height through words
+ */
+ if( i_index > 0 )
+ {
+ pp_faces[ i_index ] = pp_faces[ 0 ];
+ pp_faces[ 0 ] = p_face;
+ }
+ }
+
+ } while( i_glyph_index == 0 && ++i_index < MAX_FACES );
+ }
+
+ int i_run_start = i_start_offset;
+ for( int i = i_start_offset; i <= i_end_offset; ++i )
+ {
+ if( i == i_end_offset
+ || p_paragraph->pp_faces[ i_run_start ] != p_paragraph->pp_faces[ i ] )
+ {
+ if( AddRun( p_filter, p_paragraph, i_run_start, i,
+ p_paragraph->pp_faces[ i_run_start ], NULL ) )
+ return VLC_EGENERIC;
+
+ i_run_start = i;
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+#endif
+
+static bool FaceStyleEquals( filter_t *p_filter, const text_style_t *p_style1,
+ const text_style_t *p_style2 )
+{
+ if( !p_style1 || !p_style2 )
+ return false;
+ if( p_style1 == p_style2 )
+ return true;
+
+ const int i_style_mask = STYLE_BOLD | STYLE_ITALIC | STYLE_HALFWIDTH;
+
+ const char *psz_fontname1 = p_style1->i_style_flags & STYLE_MONOSPACED
+ ? p_style1->psz_monofontname : p_style1->psz_fontname;
+
+ const char *psz_fontname2 = p_style2->i_style_flags & STYLE_MONOSPACED
+ ? p_style2->psz_monofontname : p_style2->psz_fontname;
+
+ const int i_size1 = ConvertToLiveSize( p_filter, p_style1 );
+ const int i_size2 = ConvertToLiveSize( p_filter, p_style2 );
+
+ return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask)
+ && i_size1 == i_size2
+ && !strcasecmp( psz_fontname1, psz_fontname2 );
+}
+
+/*
* Segment a paragraph into runs
*/
static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
@@ -460,16 +572,14 @@ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
|| last_script != p_paragraph->p_scripts[ i ]
|| last_level != p_paragraph->p_levels[ i ]
#endif
- || ( p_paragraph->pp_styles[ i ] != NULL && (
- p_last_style->i_font_size != p_paragraph->pp_styles[ i ]->i_font_size
- || ( ( p_last_style->i_style_flags
- ^ p_paragraph->pp_styles[ i ]->i_style_flags )
- & STYLE_HALFWIDTH )
- ||!FaceStyleEquals( p_last_style, p_paragraph->pp_styles[ i ] ) )
- )
- )
+ || !FaceStyleEquals( p_filter, p_last_style, p_paragraph->pp_styles[ i ] ) )
{
- int i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, 0 );
+ int i_ret;
+#ifdef HAVE_FONT_FALLBACK
+ i_ret = AddRunWithFallback( p_filter, p_paragraph, i_last_run_start, i );
+#else
+ i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, NULL, NULL );
+#endif
if( i_ret )
return i_ret;
@@ -516,17 +626,20 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
{
run_desc_t *p_run = p_paragraph->p_runs + i;
const text_style_t *p_style = p_run->p_style;
- const int i_live_size = ConvertToLiveSize( p_filter, p_style );
/*
- * When using HarfBuzz, this is where font faces are loaded.
- * In the other two paths (shaping with FriBidi or no
- * shaping at all), faces are loaded in LoadGlyphs()
+ * With HarfBuzz and no font fallback, this is where font faces
+ * are loaded. In the other two paths (shaping with FriBidi or no
+ * shaping at all), faces are loaded in LoadGlyphs().
+ *
+ * If we have font fallback, font faces in all paths will be
+ * loaded in AddRunWithFallback(), except for runs of codepoints
+ * for which no font could be found.
*/
FT_Face p_face = 0;
if( !p_run->p_face )
{
- p_face = LoadFace( p_filter, p_style, i_live_size );
+ p_face = SelectAndLoadFace( p_filter, p_style, 0 );
if( !p_face )
{
p_face = p_sys->p_face;
@@ -637,7 +750,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
++i_index;
}
if( AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count,
- i_index, p_run->p_face ) )
+ i_index, p_run->p_face, p_run->p_style ) )
goto error;
}
@@ -785,7 +898,7 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
FT_Face p_face = 0;
if( !p_run->p_face )
{
- p_face = LoadFace( p_filter, p_style, i_live_size );
+ p_face = SelectAndLoadFace( p_filter, p_style, 0 );
if( !p_face )
{
/* Uses the default font and style */
--
1.9.1
More information about the vlc-devel
mailing list