[vlc-commits] freetype: use cache subsystem

Francois Cartegnie git at videolan.org
Mon Aug 17 23:43:11 CEST 2020


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Wed Jul 22 09:58:33 2020 +0200| [151ccdce37fc54780b1a3262dde7be910ae0fbef] | committer: Francois Cartegnie

freetype: use cache subsystem

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

 modules/text_renderer/Makefile.am               |   1 +
 modules/text_renderer/freetype/fonts/dwrite.cpp |   2 +-
 modules/text_renderer/freetype/fonts/win32.c    |   2 +-
 modules/text_renderer/freetype/freetype.c       |  35 +--
 modules/text_renderer/freetype/freetype.h       |  21 +-
 modules/text_renderer/freetype/ftcache.c        | 234 ++++++++++++++++++
 modules/text_renderer/freetype/ftcache.h        |  74 ++++++
 modules/text_renderer/freetype/platform_fonts.c | 128 ++++------
 modules/text_renderer/freetype/platform_fonts.h |   6 +-
 modules/text_renderer/freetype/text_layout.c    | 312 +++++++++++++-----------
 10 files changed, 569 insertions(+), 246 deletions(-)

diff --git a/modules/text_renderer/Makefile.am b/modules/text_renderer/Makefile.am
index ef521d4c53..76e941cf87 100644
--- a/modules/text_renderer/Makefile.am
+++ b/modules/text_renderer/Makefile.am
@@ -6,6 +6,7 @@ text_LTLIBRARIES = libtdummy_plugin.la
 libfreetype_plugin_la_SOURCES = \
 	text_renderer/freetype/platform_fonts.c text_renderer/freetype/platform_fonts.h \
 	text_renderer/freetype/freetype.c text_renderer/freetype/freetype.h \
+	text_renderer/freetype/ftcache.c text_renderer/freetype/ftcache.h \
 	text_renderer/freetype/text_layout.c text_renderer/freetype/text_layout.h \
         text_renderer/freetype/fonts/backends.h
 
diff --git a/modules/text_renderer/freetype/fonts/dwrite.cpp b/modules/text_renderer/freetype/fonts/dwrite.cpp
index 1e46381dfc..13e44ac76e 100644
--- a/modules/text_renderer/freetype/fonts/dwrite.cpp
+++ b/modules/text_renderer/freetype/fonts/dwrite.cpp
@@ -790,7 +790,7 @@ extern "C" vlc_family_t *DWrite_GetFallbacks( vlc_font_select_t *fs, const char
         if( !p_fallback || !p_fallback->p_fonts )
             goto done;
 
-        if( !GetFace( fs, p_fallback->p_fonts, codepoint ) )
+        if( !CheckFace( fs, p_fallback->p_fonts, codepoint ) )
             goto done;
 
         p_family = NewFamily( fs, psz_fallback, NULL, NULL, NULL );
diff --git a/modules/text_renderer/freetype/fonts/win32.c b/modules/text_renderer/freetype/fonts/win32.c
index bed26294c1..f2ab4cf769 100644
--- a/modules/text_renderer/freetype/fonts/win32.c
+++ b/modules/text_renderer/freetype/fonts/win32.c
@@ -555,7 +555,7 @@ vlc_family_t *Win32_GetFallbacks( vlc_font_select_t *fs, const char *psz_family,
         if( !p_uniscribe || !p_uniscribe->p_fonts )
             goto done;
 
-        if( !GetFace( fs, p_uniscribe->p_fonts, codepoint ) )
+        if( !CheckFace( fs, p_uniscribe->p_fonts, codepoint ) )
             goto done;
 
         p_family = NewFamily( fs, psz_uniscribe, NULL, NULL, NULL );
diff --git a/modules/text_renderer/freetype/freetype.c b/modules/text_renderer/freetype/freetype.c
index c800a8cff2..a5f34547c3 100644
--- a/modules/text_renderer/freetype/freetype.c
+++ b/modules/text_renderer/freetype/freetype.c
@@ -83,6 +83,8 @@ static void Destroy( vlc_object_t * );
 #define SHADOW_COLOR_TEXT N_("Shadow color")
 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
+#define CACHE_SIZE_TEXT N_("Cache size")
+#define CACHE_SIZE_LONGTEXT N_("Cache size in kBytes")
 
 #define TEXT_DIRECTION_TEXT N_("Text direction")
 #define TEXT_DIRECTION_LONGTEXT N_("Paragraph base direction for the Unicode bi-directional algorithm.")
@@ -182,6 +184,10 @@ vlc_module_begin ()
                           SHADOW_DISTANCE_TEXT, NULL, false )
         change_safe()
 
+    add_integer_with_range( "freetype-cache-size", 200, 25, (UINT32_MAX >> 10),
+                            CACHE_SIZE_TEXT, CACHE_SIZE_LONGTEXT, true )
+        change_safe()
+
     add_obsolete_integer( "freetype-fontsize" );
     add_obsolete_integer( "freetype-rel-fontsize" );
     add_obsolete_integer( "freetype-effect" );
@@ -1168,11 +1174,11 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
     UpdateDefaultLiveStyles( p_filter );
 
     int i_font_default_size = ConvertToLiveSize( p_filter, p_sys->p_default_style );
-    if( !p_sys->p_face || i_font_default_size != p_sys->i_font_default_size )
+    if( !p_sys->p_faceid || i_font_default_size != p_sys->i_font_default_size )
     {
         /* 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, ' ' );
-        if( !p_sys->p_face )
+        p_sys->p_faceid = SelectAndLoadFace( p_filter, p_sys->p_default_style, ' ' );
+        if( !p_sys->p_faceid )
         {
             msg_Err( p_filter, "Render(): Error loading default face" );
             return VLC_EGENERIC;
@@ -1353,13 +1359,6 @@ 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
  *****************************************************************************
@@ -1389,8 +1388,10 @@ static int Create( vlc_object_t *p_this )
         p_sys->p_stroker = NULL;
     }
 
-    /* Dictionnaries for fonts */
-    vlc_dictionary_init( &p_sys->face_map, 50 );
+    p_sys->ftcache = vlc_ftcache_New( p_this, p_sys->p_library,
+                            var_InheritInteger( p_this, "freetype-cache-size" ) );
+    if( !p_sys->ftcache )
+        goto error;
 
     p_sys->i_scale = 100;
 
@@ -1432,8 +1433,8 @@ static int Create( vlc_object_t *p_this )
     if( LoadFontsFromAttachments( p_filter ) == VLC_ENOMEM )
         goto error;
 
-    p_sys->p_face = SelectAndLoadFace( p_filter, p_sys->p_default_style, ' ' );
-    if( !p_sys->p_face )
+    p_sys->p_faceid = SelectAndLoadFace( p_filter, p_sys->p_default_style, ' ' );
+    if( !p_sys->p_faceid )
     {
         msg_Err( p_filter, "Error loading default face %s",
                  p_sys->p_default_style->psz_fontname );
@@ -1471,9 +1472,6 @@ static void Destroy( vlc_object_t *p_this )
     text_style_Delete( p_sys->p_default_style );
     text_style_Delete( p_sys->p_forced_style );
 
-    /* Fonts dicts */
-    vlc_dictionary_clear( &p_sys->face_map, FreeFace, p_filter );
-
     /* Attachments */
     if( p_sys->pp_font_attachments )
     {
@@ -1490,6 +1488,9 @@ static void Destroy( vlc_object_t *p_this )
     if( p_sys->p_stroker )
         FT_Stroker_Done( p_sys->p_stroker );
 
+    if( p_sys->ftcache )
+        vlc_ftcache_Delete( p_sys->ftcache );
+
     FT_Done_FreeType( p_sys->p_library );
 
     free( p_sys );
diff --git a/modules/text_renderer/freetype/freetype.h b/modules/text_renderer/freetype/freetype.h
index 44d91aecd3..aff536b8e1 100644
--- a/modules/text_renderer/freetype/freetype.h
+++ b/modules/text_renderer/freetype/freetype.h
@@ -74,8 +74,11 @@ typedef uint32_t uni_char_t;
   #define FT_GLYPH_BBOX_GRIDFIT     ft_glyph_bbox_gridfit
   #define FT_GLYPH_BBOX_TRUNCATE    ft_glyph_bbox_truncate
   #define FT_GLYPH_BBOX_PIXELS      ft_glyph_bbox_pixels
+  #define FT_ENCODING_UNICODE       ft_encoding_unicode
 #endif
 
+#include "ftcache.h"
+
 typedef struct vlc_font_select_t vlc_font_select_t;
 
 /*****************************************************************************
@@ -89,7 +92,7 @@ typedef struct vlc_family_t vlc_family_t;
 typedef struct
 {
     FT_Library     p_library;       /* handle to library     */
-    FT_Face        p_face;          /* handle to face object */
+    vlc_face_id_t *p_faceid;        /* handle to face object */
     FT_Stroker     p_stroker;       /* handle to path stroker object */
 
     text_style_t  *p_default_style;
@@ -106,15 +109,13 @@ typedef struct
     input_attachment_t **pp_font_attachments;
     int                  i_font_attachments;
 
-    /** Font face cache */
-    vlc_dictionary_t  face_map;
-
     /* Current scaling of the text, default is 100 (%) */
     int               i_scale;
     int               i_font_default_size;
     int               i_outline_thickness;
 
     vlc_font_select_t *fs;
+    vlc_ftcache_t     *ftcache;
 
 } filter_sys_t;
 
@@ -125,8 +126,8 @@ typedef struct
  * \param p_style the requested style (fonts can be different for italic or bold) [IN]
  * \param codepoint the codepoint needed [IN]
  */
-FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
-                           uni_char_t codepoint );
+vlc_face_id_t * SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
+                                   uni_char_t codepoint );
 
 static inline void BBoxInit( FT_BBox *p_box )
 {
@@ -144,5 +145,13 @@ static inline void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
     p_max->yMax = __MAX(p_max->yMax, p->yMax);
 }
 
+static inline int GetFontWidthForStyle( const text_style_t *p_style, int i_size )
+{
+    if( p_style->i_style_flags & STYLE_HALFWIDTH )
+        i_size /= 2;
+    else if( p_style->i_style_flags & STYLE_DOUBLEWIDTH )
+        i_size *= 2;
+    return i_size;
+}
 
 #endif
diff --git a/modules/text_renderer/freetype/ftcache.c b/modules/text_renderer/freetype/ftcache.c
new file mode 100644
index 0000000000..d7d90a887e
--- /dev/null
+++ b/modules/text_renderer/freetype/ftcache.c
@@ -0,0 +1,234 @@
+/*****************************************************************************
+ * ftcache.c : Font Face and glyph cache freetype2
+ *****************************************************************************
+ * Copyright (C) 2020 - VideoLabs, VLC authors and VideoLAN
+ *
+ * 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
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_filter.h>
+#include <vlc_text_style.h>
+
+/* Freetype */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+#include "ftcache.h"
+#include "platform_fonts.h"
+
+struct vlc_ftcache_t
+{
+    vlc_object_t *obj;
+    /** Font face cache */
+    vlc_dictionary_t  face_ids;
+    FTC_ScalerRec     scaler;
+    FTC_Manager       cachemanager;
+    FTC_ImageCache    image_cache;
+    FTC_CMapCache     charmap_cache;
+    /* current face properties */
+    FT_Long           style_flags;
+};
+
+vlc_face_id_t * vlc_ftcache_GetFaceID( vlc_ftcache_t *ftcache,
+                                       const char *psz_fontfile, int i_idx )
+{
+    char *psz_key;
+    if( asprintf( &psz_key, "%s#%d",
+                  psz_fontfile, i_idx ) < 0 )
+        return NULL;
+
+    vlc_face_id_t *faceid = vlc_dictionary_value_for_key( &ftcache->face_ids, psz_key );
+    if( !faceid )
+    {
+        faceid = malloc(sizeof(vlc_face_id_t));
+        if( faceid )
+        {
+            faceid->idx = i_idx;
+            faceid->psz_filename = strdup(psz_fontfile);
+            if( faceid->psz_filename )
+            {
+                faceid->charmap_index = -1;
+                vlc_dictionary_insert( &ftcache->face_ids, psz_key, faceid );
+            }
+            else
+            {
+                free( faceid );
+                faceid = NULL;
+            }
+        }
+    }
+    free( psz_key );
+    return faceid;
+}
+
+vlc_face_id_t * vlc_ftcache_GetActiveFaceInfo( vlc_ftcache_t *ftcache,
+                                               vlc_ftcache_metrics_t *metrics )
+{
+    if( metrics )
+    {
+        metrics->width_px = ftcache->scaler.width;
+        metrics->height_px = ftcache->scaler.height;
+    }
+    return (vlc_face_id_t *) ftcache->scaler.face_id;
+}
+
+static int vlc_ftcache_SetCharmap( vlc_face_id_t *faceid, FT_Face p_face )
+{
+    if( FT_Select_Charmap( p_face, FT_ENCODING_UNICODE ) )
+        /* We've loaded a font face which is unhelpful for actually rendering text */
+        return VLC_EGENERIC;
+
+    faceid->charmap_index =
+            p_face->charmap ? FT_Get_Charmap_Index( p_face->charmap ) : 0;
+
+    return VLC_SUCCESS;
+}
+
+FT_Face vlc_ftcache_LoadFaceByID( vlc_ftcache_t *ftcache, vlc_face_id_t *faceid,
+                                  const vlc_ftcache_metrics_t *metrics )
+{
+    ftcache->scaler.face_id = faceid;
+    ftcache->scaler.width = metrics->width_px;
+    ftcache->scaler.height = metrics->height_px;
+
+    FT_Size size;
+    if(FTC_Manager_LookupSize( ftcache->cachemanager, &ftcache->scaler, &size ))
+    {
+        ftcache->scaler.face_id = NULL;
+        ftcache->scaler.width = 0;
+        ftcache->scaler.height = 0;
+        return NULL;
+    }
+
+    return size->face;
+}
+
+int vlc_ftcache_LoadFaceByIDNoSize( vlc_ftcache_t *ftcache, vlc_face_id_t *faceid )
+{
+    FT_Face face;
+    if( FTC_Manager_LookupFace( ftcache->cachemanager, faceid, &face ) )
+        return VLC_EGENERIC;
+
+    ftcache->style_flags = face->style_flags;
+    ftcache->scaler.face_id = faceid;
+    ftcache->scaler.width = 0;
+    ftcache->scaler.height = 0;
+    return VLC_SUCCESS;
+}
+
+FT_UInt vlc_ftcache_LookupCMapIndex( vlc_ftcache_t *ftcache, vlc_face_id_t *faceid, FT_UInt codepoint )
+{
+    return FTC_CMapCache_Lookup( ftcache->charmap_cache, faceid,
+                                 faceid->charmap_index, codepoint );
+}
+
+int vlc_ftcache_GetGlyphForCurrentFace( vlc_ftcache_t *ftcache, FT_UInt index,
+                                        vlc_ftcache_glyph_t *glyph, FT_Long *styleflags )
+{
+    int ret = FTC_ImageCache_LookupScaler( ftcache->image_cache,
+                                           &ftcache->scaler,
+                                           FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT,
+                                           index, &glyph->p_glyph, &glyph->ref );
+    if( !ret && styleflags )
+        *styleflags = ftcache->style_flags;
+    return ret;
+}
+
+static FT_Error RequestFace( FTC_FaceID face_id, FT_Library library,
+                             FT_Pointer req_data, FT_Face* aface )
+{
+    vlc_ftcache_t *ftcache = (vlc_ftcache_t *) req_data;
+    vlc_face_id_t *faceid = (vlc_face_id_t *) face_id;
+    VLC_UNUSED(library);
+
+    *aface = doLoadFace( ftcache->obj, faceid->psz_filename, faceid->idx);
+
+    if( !*aface || vlc_ftcache_SetCharmap( faceid, *aface ) )
+        return -1;
+
+    return 0;
+}
+
+static void FreeFaceID( void *p_faceid, void *p_obj )
+{
+    VLC_UNUSED(p_obj);
+    vlc_face_id_t *faceid = p_faceid;
+    free(faceid->psz_filename);
+    free(faceid);
+}
+
+void vlc_ftcache_Delete( vlc_ftcache_t *ftcache )
+{
+    if( ftcache->cachemanager )
+        FTC_Manager_Done( ftcache->cachemanager );
+
+    /* Fonts dicts */
+    vlc_dictionary_clear( &ftcache->face_ids, FreeFaceID, NULL );
+
+    free( ftcache );
+}
+
+vlc_ftcache_t * vlc_ftcache_New( vlc_object_t *obj, FT_Library p_library,
+                                 unsigned maxkb )
+{
+    vlc_ftcache_t *ftcache = calloc(1, sizeof(*ftcache));
+    if(!ftcache)
+        return NULL;
+    ftcache->obj = obj;
+
+    /* Dictionnaries for fonts */
+    vlc_dictionary_init( &ftcache->face_ids, 50 );
+
+    if(FTC_Manager_New( p_library, 4, 8, maxkb << 10,
+                        RequestFace, ftcache, &ftcache->cachemanager ) ||
+       FTC_ImageCache_New( ftcache->cachemanager, &ftcache->image_cache ) ||
+       FTC_CMapCache_New( ftcache->cachemanager, &ftcache->charmap_cache ))
+    {
+        vlc_ftcache_Delete( ftcache );
+        return NULL;
+    }
+
+    ftcache->scaler.pixel = 1;
+    ftcache->scaler.x_res = 0;
+    ftcache->scaler.y_res = 0;
+
+    return ftcache;
+}
+
+void vlc_ftcache_Glyph_Release( vlc_ftcache_t *ftcache, vlc_ftcache_glyph_t *g )
+{
+    if( g->ref )
+    {
+        FTC_Node_Unref( g->ref, ftcache->cachemanager );
+        g->ref = NULL;
+        g->p_glyph = NULL;
+    }
+    else if( g->p_glyph ) /* glyph is not cache reference */
+    {
+        FT_Done_Glyph( g->p_glyph );
+        g->p_glyph = NULL;
+    }
+}
+
+void vlc_ftcache_Glyph_Init( vlc_ftcache_glyph_t *g )
+{
+    g->p_glyph = NULL;
+    g->ref = NULL;
+}
diff --git a/modules/text_renderer/freetype/ftcache.h b/modules/text_renderer/freetype/ftcache.h
new file mode 100644
index 0000000000..cb6d12d740
--- /dev/null
+++ b/modules/text_renderer/freetype/ftcache.h
@@ -0,0 +1,74 @@
+/*****************************************************************************
+ * ftcache.h : Font Face and glyph cache freetype2
+ *****************************************************************************
+ * Copyright (C) 2020 - VideoLabs, VLC authors and VideoLAN
+ *
+ * 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
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef FTCACHE_H
+#define FTCACHE_H
+
+#include FT_CACHE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct vlc_ftcache_t vlc_ftcache_t;
+
+typedef struct
+{
+    char *psz_filename;
+    unsigned idx;
+    unsigned int charmap_index;
+
+} vlc_face_id_t;
+
+vlc_ftcache_t * vlc_ftcache_New( vlc_object_t *, FT_Library, unsigned );
+void vlc_ftcache_Delete( vlc_ftcache_t * );
+
+/* Glyphs managed by the cache. Always use vlc_ftcache_Init/Release */
+typedef struct
+{
+    FT_Glyph p_glyph;
+    FTC_Node ref;
+} vlc_ftcache_glyph_t;
+
+void vlc_ftcache_Glyph_Init( vlc_ftcache_glyph_t * );
+void vlc_ftcache_Glyph_Release( vlc_ftcache_t *, vlc_ftcache_glyph_t * );
+
+vlc_face_id_t * vlc_ftcache_GetFaceID( vlc_ftcache_t *, const char *psz_fontfile, int i_idx );
+
+typedef struct
+{
+    int width_px;
+    int height_px;
+} vlc_ftcache_metrics_t;
+
+vlc_face_id_t * vlc_ftcache_GetActiveFaceInfo( vlc_ftcache_t *, vlc_ftcache_metrics_t * );
+int vlc_ftcache_GetGlyphForCurrentFace( vlc_ftcache_t *, FT_UInt charmap_index,
+                                        vlc_ftcache_glyph_t *, FT_Long * );
+FT_UInt vlc_ftcache_LookupCMapIndex( vlc_ftcache_t *, vlc_face_id_t *faceid, FT_UInt codepoint );
+/* Big fat warning : Do not store FT_Face.
+ * Faces are fully managed by cache and possibly invalidated when changing face */
+FT_Face vlc_ftcache_LoadFaceByID( vlc_ftcache_t *, vlc_face_id_t *faceid,
+                                  const vlc_ftcache_metrics_t * );
+int vlc_ftcache_LoadFaceByIDNoSize( vlc_ftcache_t *ftcache, vlc_face_id_t *faceid );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/text_renderer/freetype/platform_fonts.c b/modules/text_renderer/freetype/platform_fonts.c
index 084b011e23..dfe50df108 100644
--- a/modules/text_renderer/freetype/platform_fonts.c
+++ b/modules/text_renderer/freetype/platform_fonts.c
@@ -49,27 +49,12 @@
 #include "freetype.h"
 #include "fonts/backends.h"
 
-static FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx,
-                  const text_style_t *p_style )
+FT_Face doLoadFace( void *ctx, const char *psz_fontfile, int i_idx )
 {
+    filter_t *p_filter = ctx;
     filter_sys_t *p_sys = p_filter->p_sys;
-    char *psz_key = NULL;
-
-    int i_font_size  = ConvertToLiveSize( p_filter, p_style );
-    int i_font_width = i_font_size;
-    if( p_style->i_style_flags & STYLE_HALFWIDTH )
-        i_font_width /= 2;
-    else if( p_style->i_style_flags & STYLE_DOUBLEWIDTH )
-        i_font_width *= 2;
-
-    if( asprintf( &psz_key, "%s - %d - %d - %d",
-                  psz_fontfile, i_idx,
-                  i_font_size, i_font_width ) < 0 )
-        return NULL;
 
-    FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->face_map, psz_key );
-    if( p_face != NULL )
-        goto done;
+    FT_Face p_face = NULL;
 
     if( psz_fontfile[0] == ':' && psz_fontfile[1] == '/' )
     {
@@ -81,76 +66,48 @@ static FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx
             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 ) )
-                msg_Err( p_filter, "LoadFace: Error creating face for %s", psz_key );
+                return NULL;
         }
     }
-
 #if defined( _WIN32 )
     else if( !memcmp( psz_fontfile, ":dw/", 4 ) )
     {
         int i_index = atoi( psz_fontfile + 4 );
-        FT_Stream p_stream;
-        if( DWrite_GetFontStream( p_sys->fs, i_index, &p_stream ) != VLC_SUCCESS )
-            msg_Err( p_filter, "LoadFace: Invalid font stream index" );
-        else
+        FT_Open_Args args = {0};
+        args.flags = FT_OPEN_STREAM;
+        if( DWrite_GetFontStream( p_sys->fs, i_index, &args.stream ) != VLC_SUCCESS ||
+            FT_Open_Face( p_sys->p_library, &args, i_idx, &p_face ) )
         {
-            FT_Open_Args args = {0};
-            args.flags = FT_OPEN_STREAM;
-            args.stream = p_stream;
-            if( FT_Open_Face( p_sys->p_library, &args, i_idx, &p_face ) )
-                msg_Err( p_filter, "LoadFace: Error creating face for %s", psz_key );
+            msg_Err( p_filter, "LoadFace: Invalid font stream index" );
+            return NULL;
         }
     }
 #endif
 
     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 );
-
-    if( !p_face )
-        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 );
-        p_face = NULL;
-        goto done;
-    }
-
-    if( FT_Set_Pixel_Sizes( p_face, i_font_width, i_font_size ) )
-    {
-        msg_Err( p_filter,
-                 "LoadFace: Failed to set font size for %s", psz_key );
-        FT_Done_Face( p_face );
-        p_face = NULL;
-        goto done;
-    }
-
-    vlc_dictionary_insert( &p_sys->face_map, psz_key, p_face );
+            return NULL;
 
-done:
-    free( psz_key );
     return p_face;
 }
 
-FT_Face GetFace( vlc_font_select_t *fs, vlc_font_t *p_font, uni_char_t codepoint )
+bool CheckFace( vlc_font_select_t *fs, vlc_font_t *p_font, uni_char_t codepoint )
 {
     filter_sys_t *p_sys = fs->p_filter->p_sys;
 
-    if( !p_font->p_face )
-        p_font->p_face = LoadFace( fs->p_filter, p_font->psz_fontfile,
-                                   p_font->i_index,
-                                   p_sys->p_default_style );
+    vlc_face_id_t *faceid = p_font->faceid;
+    if( !faceid )
+    {
+        faceid = vlc_ftcache_GetFaceID( p_sys->ftcache,
+                                        p_font->psz_fontfile,
+                                        p_font->i_index );
+        p_font->faceid = faceid;
+    }
+
+    if( !faceid || vlc_ftcache_LoadFaceByIDNoSize( p_sys->ftcache, faceid ) )
+        return false;
 
-    if( p_font->p_face &&
-        FT_Get_Char_Index( p_font->p_face, codepoint ) )
-        return p_font->p_face;
-    else
-        return NULL;
+    return vlc_ftcache_LookupCMapIndex( p_sys->ftcache, faceid, codepoint );
 }
 
 /**
@@ -173,7 +130,7 @@ static vlc_font_t *GetBestFont( vlc_font_select_t *fs, const vlc_family_t *p_fam
     {
         int i_score = 0;
 
-        if( codepoint && GetFace( fs, p_font, codepoint ) )
+        if( codepoint && CheckFace( fs, p_font, codepoint ) )
             i_score += 1000;
 
         if( !!p_font->b_bold == !!b_bold )
@@ -207,7 +164,7 @@ vlc_family_t *SearchFallbacks( vlc_font_select_t *fs, vlc_family_t *p_fallbacks,
             p_fallback->p_fonts = p_temp->p_fonts;
         }
 
-        if( !GetFace( fs, p_fallback->p_fonts, codepoint ) )
+        if( !CheckFace( fs, p_fallback->p_fonts, codepoint ) )
             continue;
 
         p_family = p_fallback;
@@ -226,7 +183,7 @@ static vlc_family_t *SearchFontByFamilyName( vlc_font_select_t *fs,
     {
         if( !strcasecmp( p->psz_name, psz_familyname ) &&
             p->p_fonts &&
-            GetFace( fs, p->p_fonts, codepoint ) )
+            CheckFace( fs, p->p_fonts, codepoint ) )
             return p;
     }
     return NULL;
@@ -603,7 +560,7 @@ static char* SelectFontWithFamilyFallback( vlc_font_select_t *fs,
     const bool b_italic = p_style->i_style_flags & STYLE_ITALIC;
     const vlc_family_t *p_family = NULL;
 
-    if( codepoint )
+    if( codepoint && !p_family )
     {
         vlc_family_t *p_fallbacks;
         const char *psz_name;
@@ -628,7 +585,7 @@ static char* SelectFontWithFamilyFallback( vlc_font_select_t *fs,
             p_family = FontSelectGetFamily( fs, psz_name );
             if( p_family && p_family->p_fonts )
             {
-                if( GetFace( fs, p_family->p_fonts, codepoint ) )
+                if( CheckFace( fs, p_family->p_fonts, codepoint ) )
                 {
                     Debug( fs->p_obj, "Found family \"%s\" for codepoint %x",
                            psz_name, codepoint );
@@ -642,7 +599,7 @@ static char* SelectFontWithFamilyFallback( vlc_font_select_t *fs,
         /* Try font attachments if not available locally */
         if( !p_family )
         {
-            Debug( fs->p_obj, "Looking for family \"%s\" in attachments", psz_name );
+            Debug( fs->p_obj, "Looking for family \"%s\" in attachments cp %x", psz_name, codepoint );
             p_fallbacks = vlc_dictionary_value_for_key( &fs->fallback_map,
                                                         FB_LIST_ATTACHMENTS );
             if( p_fallbacks )
@@ -661,7 +618,7 @@ static char* SelectFontWithFamilyFallback( vlc_font_select_t *fs,
         {
             vlc_vector_foreach( psz_name, families )
             {
-                Debug( fs->p_obj, "Looking for family \"%s\" in system fallbacks", psz_name );
+                Debug( fs->p_obj, "Looking for family \"%s\" in system fallbacks cp %x", psz_name, codepoint );
                 p_fallbacks = FontSelectGetFallbacks( fs, psz_name, codepoint );
                 if( p_fallbacks )
                 {
@@ -715,8 +672,8 @@ static char* SelectFontWithFamilyFallback( vlc_font_select_t *fs,
     return NULL;
 }
 
-FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
-                           uni_char_t codepoint )
+vlc_face_id_t *
+SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style, uni_char_t codepoint )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     const char *psz_fontname = (p_style->i_style_flags & STYLE_MONOSPACED)
@@ -731,17 +688,27 @@ FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
         return NULL;
     }
 
-    FT_Face p_face = NULL;
+    vlc_face_id_t *p_faceid = NULL;
 
     int  i_idx = 0;
     char *psz_fontfile =
             SelectFontWithFamilyFallback( p_sys->fs, &families, p_style,
                                           &i_idx, codepoint );
+
     if( psz_fontfile && *psz_fontfile != '\0' )
     {
-        p_face = LoadFace( p_filter, psz_fontfile, i_idx, p_style );
+        p_faceid  = vlc_ftcache_GetFaceID( p_sys->ftcache, psz_fontfile, i_idx );
+        if( p_faceid )
+        {
+            vlc_ftcache_metrics_t metrics;
+            metrics.height_px = ConvertToLiveSize( p_filter, p_style );
+            metrics.width_px = GetFontWidthForStyle( p_style, metrics.height_px );
+            if( !vlc_ftcache_LoadFaceByID( p_sys->ftcache, p_faceid, &metrics ) )
+                p_faceid = NULL;
+        }
     }
-    else
+
+    if( !p_faceid )
     {
         msg_Warn( p_filter,
                   "SelectAndLoadFace: no font found for family: %s, codepoint: 0x%x",
@@ -753,7 +720,8 @@ FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
         free( psz_temp );
     vlc_vector_clear( &families );
     free( psz_fontfile );
-    return p_face;
+
+    return p_faceid;
 }
 
 #ifndef HAVE_GET_FONT_BY_FAMILY_NAME
diff --git a/modules/text_renderer/freetype/platform_fonts.h b/modules/text_renderer/freetype/platform_fonts.h
index feb03078ba..a977a1b459 100644
--- a/modules/text_renderer/freetype/platform_fonts.h
+++ b/modules/text_renderer/freetype/platform_fonts.h
@@ -132,7 +132,7 @@ struct vlc_font_t
     int         i_index;   /**< index of the font in the font file, starts at 0 */
     bool        b_bold;    /**< if the font is a bold version */
     bool        b_italic;  /**< if the font is an italic version */
-    FT_Face     p_face;    /**< the freetype structure for the font */
+    void       *faceid;    /* fontloader ref to font */
 };
 
 /**
@@ -277,11 +277,11 @@ char* ToLower( const char *psz_src );
 /* Size helper, depending on the scaling factor */
 int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style );
 
-
 /* Only for fonts implementors */
 vlc_family_t *SearchFallbacks( vlc_font_select_t *, vlc_family_t *p_fallbacks,
                                       uni_char_t codepoint );
-FT_Face GetFace( vlc_font_select_t *, vlc_font_t *p_font, uni_char_t codepoint );
+bool CheckFace( vlc_font_select_t *fs, vlc_font_t *p_font, uni_char_t codepoint );
+FT_Face doLoadFace( void *, const char *psz_fontfile, int i_idx );
 
 char * MakeFilePath( vlc_font_select_t *, const char *psz_filename );
 
diff --git a/modules/text_renderer/freetype/text_layout.c b/modules/text_renderer/freetype/text_layout.c
index 15ac84f041..127d53d151 100644
--- a/modules/text_renderer/freetype/text_layout.c
+++ b/modules/text_renderer/freetype/text_layout.c
@@ -101,7 +101,7 @@ typedef struct run_desc_t
 {
     int                         i_start_offset;
     int                         i_end_offset;
-    FT_Face                     p_face;
+    vlc_face_id_t              *p_faceid;
     const text_style_t         *p_style;
 
 #ifdef HAVE_HARFBUZZ
@@ -117,7 +117,7 @@ typedef struct run_desc_t
  */
 typedef struct glyph_bitmaps_t
 {
-    FT_Glyph p_glyph;
+    vlc_ftcache_glyph_t cglyph;
     FT_Glyph p_outline;
     FT_Glyph p_shadow;
     FT_BBox  glyph_bbox;
@@ -134,8 +134,8 @@ 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 */
+    ruby_block_t       **pp_ruby;
+    vlc_face_id_t      **pp_facesidx;      /**< 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;
     int                  i_size;
@@ -290,7 +290,7 @@ static void FreeParagraph( paragraph_t *p_paragraph )
     free( p_paragraph->pi_glyph_indices );
     free( p_paragraph->p_glyph_bitmaps );
     free( p_paragraph->pi_run_ids );
-    free( p_paragraph->pp_faces );
+    free( p_paragraph->pp_facesidx );
     free( p_paragraph->pp_ruby );
     free( p_paragraph->pp_styles );
     free( p_paragraph->p_code_points );
@@ -329,8 +329,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
             vlc_alloc( i_size, sizeof( *p_paragraph->pi_glyph_indices ) );
     p_paragraph->pp_styles =
             vlc_alloc( i_size, sizeof( *p_paragraph->pp_styles ) );
-    p_paragraph->pp_faces =
-            calloc( i_size, sizeof( *p_paragraph->pp_faces ) );
+    p_paragraph->pp_facesidx =
+            calloc( i_size, sizeof( *p_paragraph->pp_facesidx ) );
     p_paragraph->pi_run_ids =
             calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) );
     p_paragraph->p_glyph_bitmaps =
@@ -343,7 +343,7 @@ 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->pp_faces
+     || !p_paragraph->pp_styles || !p_paragraph->pp_facesidx
      || !p_paragraph->pi_run_ids|| !p_paragraph->p_glyph_bitmaps
      || !p_paragraph->p_runs )
         goto error;
@@ -471,7 +471,7 @@ static int AddRun( filter_t *p_filter,
                    paragraph_t *p_paragraph,
                    int i_start_offset,
                    int i_end_offset,
-                   FT_Face p_face,
+                   vlc_face_id_t *p_faceid,
                    const text_style_t *p_style )
 {
     if( i_start_offset >= i_end_offset
@@ -504,7 +504,7 @@ 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_face = p_face;
+    p_run->p_faceid = p_faceid;
 
     if( p_style )
         p_run->p_style = p_style;
@@ -530,6 +530,8 @@ static int AddRun( filter_t *p_filter,
 static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
                                int i_start_offset, int i_end_offset )
 {
+    filter_sys_t *p_sys = p_filter->p_sys;
+
     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 )
@@ -545,8 +547,8 @@ static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
 
     /* Maximum number of faces to try for each run */
     #define MAX_FACES 5
-    FT_Face pp_faces[ MAX_FACES ] = {0};
-    FT_Face p_face = NULL;
+    vlc_face_id_t *pp_facesidx[ MAX_FACES ] = {0};
+    vlc_face_id_t *p_faceidx = NULL;
 
     for( int i = i_start_offset; i < i_end_offset; ++i )
     {
@@ -558,33 +560,32 @@ static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
          * For white space, punctuation and neutral characters, try to use
          * the font of the previous character, if any. See #20466.
          */
-        if( p_face &&
+        if( p_faceidx &&
             ( p_paragraph->p_types[ i ] == FRIBIDI_TYPE_WS
            || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_CS
            || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_ON ) )
         {
-            i_glyph_index = FT_Get_Char_Index( p_face,
-                                               p_paragraph->p_code_points[ i ] );
+            i_glyph_index = vlc_ftcache_LookupCMapIndex( p_sys->ftcache, p_faceidx,
+                                                         p_paragraph->p_code_points[ i ] );
             if( i_glyph_index )
             {
-                p_paragraph->pp_faces[ i ] = p_face;
+                p_paragraph->pp_facesidx[ i ] = p_faceidx;
                 continue;
             }
         }
 #endif
 
         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 )
+            p_faceidx = pp_facesidx[ i_index ];
+            if( !p_faceidx )
+                p_faceidx = pp_facesidx[ i_index ] =
+                     SelectAndLoadFace( p_filter, p_style, p_paragraph->p_code_points[ i ] );
+            if( !p_faceidx )
                 continue;
-            i_glyph_index = FT_Get_Char_Index( p_face,
-                                               p_paragraph->p_code_points[ i ] );
+            i_glyph_index = vlc_ftcache_LookupCMapIndex( p_sys->ftcache, p_faceidx,
+                                             p_paragraph->p_code_points[ i ] );
             if( i_glyph_index )
-                p_paragraph->pp_faces[ i ] = p_face;
+                p_paragraph->pp_facesidx[ i ] = p_faceidx;
 
         } while( i_glyph_index == 0 && ++i_index < MAX_FACES );
     }
@@ -593,11 +594,11 @@ static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
     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 ] )
+         || p_paragraph->pp_facesidx[ i_run_start ] != p_paragraph->pp_facesidx[ i ] )
         {
-            if( p_paragraph->pp_faces[ i_run_start ] &&
+            if( p_paragraph->pp_facesidx[ i_run_start ] &&
                 AddRun( p_filter, p_paragraph, i_run_start, i,
-                        p_paragraph->pp_faces[ i_run_start ], NULL ) )
+                        p_paragraph->pp_facesidx[ i_run_start ], NULL ) )
                 return VLC_EGENERIC;
 
             i_run_start = i;
@@ -707,7 +708,6 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
     for( int i = 0; i < p_paragraph->i_runs_count; ++i )
     {
         run_desc_t *p_run = p_paragraph->p_runs + i;
-        const text_style_t *p_style = p_run->p_style;
 
         /*
          * With HarfBuzz and no font fallback, this is where font faces
@@ -718,21 +718,30 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
          * 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 )
+        if( !p_run->p_faceid )
         {
-            p_face = SelectAndLoadFace( p_filter, p_style,
-                                         p_paragraph->p_code_points[p_run->i_start_offset] );
-            if( !p_face )
+            p_run->p_faceid = SelectAndLoadFace( p_filter, p_run->p_style,
+                                                 p_paragraph->p_code_points[p_run->i_start_offset] );
+            if( !p_run->p_faceid )
             {
-                p_face = p_sys->p_face;
-                p_style = p_sys->p_default_style;
-                p_run->p_style = p_style;
+                p_run->p_faceid = p_sys->p_faceid;
+                p_run->p_style = p_sys->p_default_style;
             }
-            p_run->p_face = p_face;
         }
-        else
-            p_face = p_run->p_face;
+
+        vlc_face_id_t *p_faceid = p_run->p_faceid;
+        const text_style_t *p_style = p_run->p_style;
+
+        if(!p_faceid)
+            goto error;
+
+        vlc_ftcache_metrics_t metrics;
+        metrics.height_px = ConvertToLiveSize( p_filter, p_style );
+        metrics.width_px = GetFontWidthForStyle( p_style, metrics.height_px );
+
+        FT_Face p_face = vlc_ftcache_LoadFaceByID( p_sys->ftcache, p_faceid, &metrics );
+        if(!p_face)
+            goto error;
 
         hb_font_t *p_hb_font = hb_ft_font_create( p_face, 0 );
         if( !p_hb_font )
@@ -767,6 +776,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
         hb_shape( p_hb_font, p_run->p_buffer, 0, 0 );
 
         hb_font_destroy( p_hb_font );
+        p_hb_font = 0;
 
         const unsigned length = hb_buffer_get_length( p_run->p_buffer );
         if( length == 0 )
@@ -844,7 +854,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
             ++i_index;
         }
         if( AddRun( p_filter, p_new_paragraph, i_index - i_glyph_count,
-                    i_index, p_run->p_face, p_run->p_style ) )
+                    i_index, p_run->p_faceid, p_run->p_style ) )
             goto error;
     }
 
@@ -916,8 +926,9 @@ static int ShapeParagraphFriBidi( filter_t *p_filter, paragraph_t *p_paragraph )
  * inserted when shaping with FriBidi, when it performs glyph substitution for
  * ligatures.
  */
-static int RemoveZeroWidthCharacters( paragraph_t *p_paragraph )
+static int RemoveZeroWidthCharacters( filter_t *p_filter, paragraph_t *p_paragraph )
 {
+    filter_sys_t *p_sys = p_filter->p_sys;
     for( int i = 0; i < p_paragraph->i_size; ++i )
     {
         uni_char_t ch = p_paragraph->p_code_points[ i ];
@@ -928,11 +939,9 @@ static int RemoveZeroWidthCharacters( paragraph_t *p_paragraph )
          || ( ch >= 0x200b && ch <= 0x200f ) )
         {
             glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + i;
-            if( p_bitmaps->p_glyph )
-                FT_Done_Glyph( p_bitmaps->p_glyph );
             if( p_bitmaps->p_outline )
                 FT_Done_Glyph( p_bitmaps->p_outline );
-            p_bitmaps->p_glyph = 0;
+            vlc_ftcache_Glyph_Release( p_sys->ftcache, &p_bitmaps->cglyph );
             p_bitmaps->p_outline = 0;
             p_bitmaps->p_shadow = 0;
             p_bitmaps->i_x_advance = 0;
@@ -961,6 +970,18 @@ static int ZeroNsmAdvance( paragraph_t *p_paragraph )
 #endif
 #endif
 
+static void ReleaseGlyphBitMaps(filter_t *p_filter, glyph_bitmaps_t *p_bitmaps)
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    if( p_bitmaps->p_shadow &&
+        p_bitmaps->p_shadow != p_bitmaps->cglyph.p_glyph &&
+        p_bitmaps->p_shadow != p_bitmaps->p_outline )
+        FT_Done_Glyph( p_bitmaps->p_shadow );
+    if( p_bitmaps->p_outline )
+        FT_Done_Glyph( p_bitmaps->p_outline );
+    vlc_ftcache_Glyph_Release( p_sys->ftcache, &p_bitmaps->cglyph );
+}
+
 /**
  * Load the glyphs of a paragraph. When shaping with HarfBuzz the glyph indices
  * have already been determined at this point, as well as the advance values.
@@ -984,30 +1005,33 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
     {
         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 );
+        vlc_ftcache_metrics_t metrics;
+
+        if( p_run->p_faceid )
+        {
+            metrics.height_px = ConvertToLiveSize( p_filter, p_style );
+            metrics.width_px = GetFontWidthForStyle( p_style, metrics.height_px );
+            if(! vlc_ftcache_LoadFaceByID( p_sys->ftcache, p_run->p_faceid, &metrics ) )
+                p_run->p_faceid = NULL;
+        }
 
-        FT_Face p_face = 0;
-        if( !p_run->p_face )
+        if( !p_run->p_faceid ) /* Fallback on default font and style */
         {
-            p_face = SelectAndLoadFace( p_filter, p_style,
-                                        p_paragraph->p_code_points[p_run->i_start_offset] );
-            if( !p_face )
+            metrics.height_px = ConvertToLiveSize( p_filter, p_sys->p_default_style );
+            metrics.width_px = GetFontWidthForStyle( p_sys->p_default_style, metrics.height_px );
+            if( vlc_ftcache_LoadFaceByID( p_sys->ftcache, p_sys->p_faceid, &metrics ) )
             {
-                /* Uses the default font and style */
-                p_face = p_sys->p_face;
-                p_style = p_sys->p_default_style;
-                p_run->p_style = p_style;
+                p_run->p_faceid = p_sys->p_faceid;
+                p_run->p_style = p_style = p_sys->p_default_style;
             }
-            p_run->p_face = p_face;
+            else continue; /* can't do much from now */
         }
-        else
-            p_face = p_run->p_face;
 
         if( p_sys->p_stroker && (p_style->i_style_flags & STYLE_OUTLINE) )
         {
             double f_outline_thickness = p_sys->i_outline_thickness / 100.0;
             f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
-            int i_radius = ( i_live_size << 6 ) * f_outline_thickness;
+            int i_radius = ( metrics.height_px << 6 ) * f_outline_thickness;
             FT_Stroker_Set( p_sys->p_stroker,
                             i_radius,
                             FT_STROKER_LINECAP_ROUND,
@@ -1020,14 +1044,16 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
             if( b_use_glyph_indices )
                 i_glyph_index = p_paragraph->pi_glyph_indices[ j ];
             else
-                i_glyph_index =
-                    FT_Get_Char_Index( p_face, p_paragraph->p_code_points[ j ] );
+            {
+                i_glyph_index = vlc_ftcache_LookupCMapIndex( p_sys->ftcache, p_run->p_faceid,
+                                                 p_paragraph->p_code_points[ j ] );
+            }
 
             glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + j;
 
 #define SKIP_GLYPH( p_bitmaps ) \
     { \
-        p_bitmaps->p_glyph = 0; \
+        vlc_ftcache_Glyph_Init( &p_bitmaps->cglyph );\
         p_bitmaps->p_outline = 0; \
         p_bitmaps->p_shadow = 0; \
         p_bitmaps->i_x_advance = 0; \
@@ -1064,26 +1090,45 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
                     SKIP_GLYPH( p_bitmaps )
             }
 
-            if( FT_Load_Glyph( p_face, i_glyph_index,
-                               FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT )
-             && FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
-                SKIP_GLYPH( p_bitmaps )
-
-            if( ( p_style->i_style_flags & STYLE_BOLD )
-                  && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ) )
-                FT_GlyphSlot_Embolden( p_face->glyph );
-            if( ( p_style->i_style_flags & STYLE_ITALIC )
-                  && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ) )
-                FT_GlyphSlot_Oblique( p_face->glyph );
 
-            if( FT_Get_Glyph( p_face->glyph, &p_bitmaps->p_glyph ) )
-                SKIP_GLYPH( p_bitmaps )
+            FT_Long style_flags;
+            if( vlc_ftcache_GetGlyphForCurrentFace( p_sys->ftcache,
+                                                    i_glyph_index,
+                                                    &p_bitmaps->cglyph,
+                                                    &style_flags ) )
+                SKIP_GLYPH( p_bitmaps );
 
 #undef SKIP_GLYPH
 
+            const bool b_embolden = ( p_style->i_style_flags & STYLE_BOLD ) &&
+                                   !( style_flags & FT_STYLE_FLAG_BOLD );
+            const bool b_oblique = ( p_style->i_style_flags & STYLE_ITALIC ) &&
+                                   !( style_flags & FT_STYLE_FLAG_ITALIC );
+            /* Apply missing style by modifying the outline */
+            if( (b_embolden || b_oblique) &&
+                p_bitmaps->cglyph.p_glyph->format == FT_GLYPH_FORMAT_OUTLINE )
+            {
+                FT_Glyph transformed;
+                if( !FT_Glyph_Copy( p_bitmaps->cglyph.p_glyph, &transformed ) )
+                {
+                    /* using a copy from now */
+                    if( b_oblique )
+                    {
+                        FT_Matrix matrix = { .xx = 0x10000L, .xy = 0.12 * 0x10000L,
+                                             .yy = 0x10000L, .yx = 0 };
+                        FT_Glyph_Transform( transformed, &matrix, 0 );
+                    }
+                    if( b_embolden )
+                        FT_Outline_Embolden( &((FT_OutlineGlyph)transformed)->outline, 1<<6 );
+                    vlc_ftcache_Glyph_Release( p_sys->ftcache, &p_bitmaps->cglyph );
+                    p_bitmaps->cglyph.p_glyph = transformed;
+                }
+            }
+
+            /* !warn: style STYLE_OUTLINE != glyph FORMAT_OUTLINE */
             if( p_sys->p_stroker && (p_style->i_style_flags & STYLE_OUTLINE) )
             {
-                p_bitmaps->p_outline = p_bitmaps->p_glyph;
+                p_bitmaps->p_outline = p_bitmaps->cglyph.p_glyph;
                 if( FT_Glyph_StrokeBorder( &p_bitmaps->p_outline,
                                            p_sys->p_stroker, 0, 0 ) )
                     p_bitmaps->p_outline = 0;
@@ -1091,12 +1136,12 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
 
             if( p_style->i_shadow_alpha != STYLE_ALPHA_TRANSPARENT )
                 p_bitmaps->p_shadow = p_bitmaps->p_outline ?
-                                      p_bitmaps->p_outline : p_bitmaps->p_glyph;
+                                      p_bitmaps->p_outline : p_bitmaps->cglyph.p_glyph;
 
             if( b_overwrite_advance )
             {
-                p_bitmaps->i_x_advance = p_face->glyph->advance.x;
-                p_bitmaps->i_y_advance = p_face->glyph->advance.y;
+                p_bitmaps->i_x_advance = p_bitmaps->cglyph.p_glyph->advance.x >> 10;
+                p_bitmaps->i_y_advance = p_bitmaps->cglyph.p_glyph->advance.y >> 10;
             }
 
             unsigned i_x_advance = FT_FLOOR( abs( p_bitmaps->i_x_advance ) );
@@ -1134,8 +1179,7 @@ static int LayoutLine( filter_t *p_filter,
     FT_Face p_face = 0;
     FT_Vector pen = { .x = 0, .y = 0 };
 
-    int i_font_size = 0;
-    int i_font_width = 0;
+    vlc_ftcache_metrics_t metrics = { 0 };
     int i_font_max_advance_y = 0;
     int i_ul_offset = 0;
     int i_ul_thickness = 0;
@@ -1172,7 +1216,7 @@ static int LayoutLine( filter_t *p_filter,
         glyph_bitmaps_t *p_bitmaps =
                 p_paragraph->p_glyph_bitmaps + i_paragraph_index;
 
-        if( !p_bitmaps->p_glyph )
+        if( !p_bitmaps->cglyph.p_glyph )
         {
             BBoxInit( &p_ch->bbox );
             continue;
@@ -1183,13 +1227,11 @@ static int LayoutLine( filter_t *p_filter,
             i_last_run = p_paragraph->pi_run_ids[ i_paragraph_index ];
             p_run = p_paragraph->p_runs + i_last_run;
             p_style = p_run->p_style;
-            p_face = p_run->p_face;
 
-            i_font_width = i_font_size = ConvertToLiveSize( p_filter, p_style );
-            if( p_style->i_style_flags & STYLE_HALFWIDTH )
-                i_font_width /= 2;
-            else if( p_style->i_style_flags & STYLE_DOUBLEWIDTH )
-                i_font_width *= 2;
+            metrics.height_px = ConvertToLiveSize( p_filter, p_style );
+            metrics.width_px = GetFontWidthForStyle( p_style, metrics.height_px );
+
+            p_face = vlc_ftcache_LoadFaceByID( p_sys->ftcache, p_run->p_faceid, &metrics );
         }
 
         FT_Vector pen_new = {
@@ -1197,59 +1239,61 @@ static int LayoutLine( filter_t *p_filter,
             .y = pen.y + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_y_offset
         };
         FT_Vector pen_shadow = {
-            .x = pen_new.x + p_sys->f_shadow_vector_x * ( i_font_width << 6 ),
-            .y = pen_new.y + p_sys->f_shadow_vector_y * ( i_font_size << 6 )
+            .x = pen_new.x + p_sys->f_shadow_vector_x * ( metrics.width_px << 6 ),
+            .y = pen_new.y + p_sys->f_shadow_vector_y * ( metrics.height_px << 6 )
         };
 
-        if( p_bitmaps->p_shadow )
+        /* Shadow being a reference to main glyph, it must be processed first */
+        if( p_bitmaps->p_shadow &&
+            FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL,
+                                &pen_shadow, 0 ) )
         {
-            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL,
-                                    &pen_shadow, 0 ) )
-                p_bitmaps->p_shadow = 0;
-            else
-                FT_Glyph_Get_CBox( p_bitmaps->p_shadow, FT_GLYPH_BBOX_PIXELS,
-                                   &p_bitmaps->shadow_bbox );
+            p_bitmaps->p_shadow = 0;
         }
-        if( p_bitmaps->p_glyph )
+
+        /* Ensure we don't release reference */
+        FT_Glyph bitmapglyph = p_bitmaps->cglyph.p_glyph;
+        if( FT_Glyph_To_Bitmap( &bitmapglyph,
+                                FT_RENDER_MODE_NORMAL,
+                                &pen_new, 0 ) )
         {
-            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_glyph, FT_RENDER_MODE_NORMAL,
-                                    &pen_new, 1 ) )
-            {
-                FT_Done_Glyph( p_bitmaps->p_glyph );
-                if( p_bitmaps->p_outline )
-                    FT_Done_Glyph( p_bitmaps->p_outline );
-                if( p_bitmaps->p_shadow != p_bitmaps->p_glyph )
-                    FT_Done_Glyph( p_bitmaps->p_shadow );
-                continue;
-            }
-            else
-                FT_Glyph_Get_CBox( p_bitmaps->p_glyph, FT_GLYPH_BBOX_PIXELS,
-                                   &p_bitmaps->glyph_bbox );
+            ReleaseGlyphBitMaps( p_filter, p_bitmaps );
+            continue;
         }
-        if( p_bitmaps->p_outline )
+
+        /* release the source glyph or reference */
+        vlc_ftcache_Glyph_Release( p_sys->ftcache, &p_bitmaps->cglyph );
+        p_bitmaps->cglyph.p_glyph = bitmapglyph;
+
+        if( p_bitmaps->p_outline &&
+            FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL,
+                                &pen_new, 1 ) )
         {
-            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL,
-                                    &pen_new, 1 ) )
-            {
-                FT_Done_Glyph( p_bitmaps->p_outline );
-                p_bitmaps->p_outline = 0;
-            }
-            else
-                FT_Glyph_Get_CBox( p_bitmaps->p_outline, FT_GLYPH_BBOX_PIXELS,
-                                   &p_bitmaps->outline_bbox );
+            FT_Done_Glyph( p_bitmaps->p_outline );
+            p_bitmaps->p_outline = 0;
         }
 
-        FixGlyph( p_bitmaps->p_glyph, &p_bitmaps->glyph_bbox,
+        FT_Glyph_Get_CBox( p_bitmaps->cglyph.p_glyph, FT_GLYPH_BBOX_PIXELS,
+                           &p_bitmaps->glyph_bbox );
+        FixGlyph( p_bitmaps->cglyph.p_glyph, &p_bitmaps->glyph_bbox,
                   p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                   &pen_new );
         if( p_bitmaps->p_outline )
+        {
+            FT_Glyph_Get_CBox( p_bitmaps->p_outline, FT_GLYPH_BBOX_PIXELS,
+                               &p_bitmaps->outline_bbox );
             FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox,
                       p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                       &pen_new );
+        }
         if( p_bitmaps->p_shadow )
+        {
+            FT_Glyph_Get_CBox( p_bitmaps->p_shadow, FT_GLYPH_BBOX_PIXELS,
+                               &p_bitmaps->shadow_bbox );
             FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox,
                       p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                       &pen_shadow );
+        }
 
         int i_line_offset    = 0;
         int i_line_thickness = 0;
@@ -1293,7 +1337,7 @@ static int LayoutLine( filter_t *p_filter,
             }
         }
 
-        p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->p_glyph;
+        p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->cglyph.p_glyph;
         p_ch->p_outline = ( FT_BitmapGlyph ) p_bitmaps->p_outline;
         p_ch->p_shadow = ( FT_BitmapGlyph ) p_bitmaps->p_shadow;
 
@@ -1392,14 +1436,6 @@ static int LayoutLine( filter_t *p_filter,
     return VLC_SUCCESS;
 }
 
-static inline void ReleaseGlyphBitMaps(glyph_bitmaps_t *p_bitmaps)
-{
-    if( p_bitmaps->p_glyph )
-        FT_Done_Glyph( p_bitmaps->p_glyph );
-    if( p_bitmaps->p_outline )
-        FT_Done_Glyph( p_bitmaps->p_outline );
-}
-
 static inline bool IsWhitespaceAt( paragraph_t *p_paragraph, size_t i )
 {
     return ( p_paragraph->p_code_points[ i ] == ' '
@@ -1456,7 +1492,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
     if( i_total_width == 0 )
     {
         for( int i=0; i < p_paragraph->i_size; ++i )
-            ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i ] );
+            ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i ] );
         return VLC_SUCCESS;
     }
 
@@ -1503,7 +1539,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                  * At this point p_shadow points to either p_glyph or p_outline,
                  * so we should not free it explicitly.
                  */
-                ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i ] );
+                ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i ] );
                 i_line_start = i + 1;
                 continue;
             }
@@ -1531,7 +1567,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                  *  Not wrapping, that can't be rendered anymore. */
                 msg_Dbg( p_filter, "LayoutParagraph(): First glyph width in line exceeds maximum, skipping" );
                 for( ; i < p_paragraph->i_size; ++i )
-                    ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i ] );
+                    ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i ] );
                 return VLC_SUCCESS;
             }
 
@@ -1550,7 +1586,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
             if( p_run->p_style->e_wrapinfo == STYLE_WRAP_NONE )
             {
                 for( ; i < p_paragraph->i_size; ++i )
-                    ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i ] );
+                    ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i ] );
                 break;
             }
 
@@ -1564,7 +1600,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                 if( i_newline_start + 1 < p_paragraph->i_size )
                 {
                     i_line_start = i_newline_start + 1;
-                    ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i_newline_start ] );
+                    ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i_newline_start ] );
                 }
                 else
                     i_line_start = i_newline_start; // == i
@@ -1584,7 +1620,7 @@ static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
 
 error:
     for( int i = i_line_start; i < p_paragraph->i_size; ++i )
-        ReleaseGlyphBitMaps( &p_paragraph->p_glyph_bitmaps[ i ] );
+        ReleaseGlyphBitMaps( p_filter, &p_paragraph->p_glyph_bitmaps[ i ] );
     if( p_first_line )
         FreeLines( p_first_line );
     return VLC_EGENERIC;
@@ -1626,7 +1662,7 @@ static paragraph_t * BuildParagraph( filter_t *p_filter,
         goto error;
     if( LoadGlyphs( p_filter, p_paragraph, false, true, pi_max_advance_x ) )
         goto error;
-    if( RemoveZeroWidthCharacters( p_paragraph ) )
+    if( RemoveZeroWidthCharacters( p_filter, p_paragraph ) )
         goto error;
     if( ZeroNsmAdvance( p_paragraph ) )
         goto error;



More information about the vlc-commits mailing list