[vlc-devel] [PATCH] FreeType: Font fallback using DirectWrite
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Thu Dec 1 14:28:05 CET 2016
From: Salah-Eddin Shaban <salah at videolan.org>
---
modules/text_renderer/Makefile.am | 10 +-
modules/text_renderer/freetype/fonts/dwrite.cpp | 588 ++++++++++++++++++++++++
modules/text_renderer/freetype/freetype.c | 58 ++-
modules/text_renderer/freetype/freetype.h | 4 +
modules/text_renderer/freetype/platform_fonts.c | 32 +-
modules/text_renderer/freetype/platform_fonts.h | 18 +-
modules/text_renderer/freetype/text_layout.c | 4 +-
7 files changed, 680 insertions(+), 34 deletions(-)
create mode 100644 modules/text_renderer/freetype/fonts/dwrite.cpp
diff --git a/modules/text_renderer/Makefile.am b/modules/text_renderer/Makefile.am
index be98a94..1e4686f 100644
--- a/modules/text_renderer/Makefile.am
+++ b/modules/text_renderer/Makefile.am
@@ -13,8 +13,14 @@ libfreetype_plugin_la_LIBADD = $(LIBM) $(FREETYPE_LIBS)
libfreetype_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(FREETYPE_LDFLAGS) -rpath '$(textdir)'
if HAVE_WIN32_DESKTOP
-libfreetype_plugin_la_SOURCES += text_renderer/freetype/fonts/win32.c
-libfreetype_plugin_la_LIBADD += -liconv -lz -lgdi32 -lusp10
+libfreetype_plugin_la_SOURCES += \
+ text_renderer/freetype/fonts/win32.c text_renderer/freetype/fonts/dwrite.cpp
+libfreetype_plugin_la_LIBADD += -liconv -lz -lgdi32 -lusp10 -luuid
+endif
+if HAVE_WINSTORE
+libfreetype_plugin_la_SOURCES += \
+ text_renderer/freetype/fonts/dwrite.cpp
+libfreetype_plugin_la_LIBADD += -ldwrite
endif
if HAVE_FONTCONFIG
libfreetype_plugin_la_SOURCES += text_renderer/freetype/fonts/fontconfig.c
diff --git a/modules/text_renderer/freetype/fonts/dwrite.cpp b/modules/text_renderer/freetype/fonts/dwrite.cpp
new file mode 100644
index 0000000..2ba4d12
--- /dev/null
+++ b/modules/text_renderer/freetype/fonts/dwrite.cpp
@@ -0,0 +1,588 @@
+/*****************************************************************************
+ * dwrite.cpp : DirectWrite font functions
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Salah-Eddin Shaban <salah at videolan.org>
+ *
+ * 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_charset.h>
+
+#include <dwrite_2.h>
+#include <wrl/client.h>
+#include <vector>
+#include <stdexcept>
+
+#include "../platform_fonts.h"
+
+using Microsoft::WRL::ComPtr;
+using namespace std;
+
+typedef HRESULT ( WINAPI *DWriteCreateFactoryProc ) (
+ _In_ DWRITE_FACTORY_TYPE factory_type,
+ _In_ REFIID iid,
+ _Out_ IUnknown **pp_factory );
+
+struct dw_sys_t
+{
+ HMODULE p_dw_dll;
+ ComPtr< IDWriteFactory2 > p_dw_factory;
+ ComPtr< IDWriteFontCollection > p_dw_system_fonts;
+ ComPtr< IDWriteNumberSubstitution > p_dw_substitution;
+ ComPtr< IDWriteFontFallback > p_dw_fallbacks;
+ vector< FT_Stream > streams;
+
+ dw_sys_t( HMODULE p_dw_dll ) : p_dw_dll( p_dw_dll )
+ {
+ /* This will fail on versions of Windows prior to 8.1 */
+#if VLC_WINSTORE_APP
+ if( DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof( IDWriteFactory2 ),
+ reinterpret_cast<IUnknown **>( p_dw_factory.GetAddressOf() ) ) )
+ throw runtime_error( "failed to create DWrite factory" );
+#else
+ DWriteCreateFactoryProc pf =
+ ( DWriteCreateFactoryProc ) GetProcAddress( p_dw_dll, "DWriteCreateFactory" );
+
+ if( pf == NULL )
+ throw runtime_error( "GetProcAddress() failed" );
+
+ if( pf( DWRITE_FACTORY_TYPE_SHARED, __uuidof( IDWriteFactory2 ),
+ reinterpret_cast<IUnknown **>( p_dw_factory.GetAddressOf() ) ) )
+ throw runtime_error( "failed to create DWrite factory" );
+#endif
+
+ if( p_dw_factory->GetSystemFontCollection( p_dw_system_fonts.GetAddressOf() ) )
+ throw runtime_error( "GetSystemFontCollection() failed" );
+
+ if( p_dw_factory->GetSystemFontFallback( p_dw_fallbacks.GetAddressOf() ) )
+ throw runtime_error( "GetSystemFontFallback() failed" );
+
+ if( p_dw_factory->CreateNumberSubstitution( DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL,
+ L"en-US", true, p_dw_substitution.GetAddressOf() ) )
+ throw runtime_error( "CreateNumberSubstitution() failed" );
+ }
+
+ ~dw_sys_t()
+ {
+ for( unsigned int i = 0; i < streams.size(); ++i )
+ {
+ FT_Stream p_stream = streams.at( i );
+ IDWriteFontFileStream *p_dw_stream = ( IDWriteFontFileStream * ) p_stream->descriptor.pointer;
+ p_dw_stream->Release();
+ free( p_stream );
+ }
+ }
+};
+
+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;
+}
+
+extern "C" int InitDWrite( filter_t *p_filter )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ dw_sys_t *p_dw_sys;
+ HMODULE p_dw_dll = NULL;
+
+ try
+ {
+#if VLC_WINSTORE_APP
+ p_dw_sys = new dw_sys_t( p_dw_dll );
+#else
+ p_dw_dll = LoadLibrary( TEXT( "Dwrite.dll" ) );
+ if( p_dw_dll == NULL )
+ {
+ msg_Err( p_filter, "InitDWrite(): LoadLibrary() failed" );
+ return VLC_EGENERIC;
+ }
+
+ p_dw_sys = new dw_sys_t( p_dw_dll );
+#endif
+ }
+ catch( const exception &e )
+ {
+ msg_Err( p_filter, "InitDWrite(): %s", e.what() );
+
+#if !VLC_WINSTORE_APP
+ FreeLibrary( p_dw_dll );
+#endif
+
+ return VLC_EGENERIC;
+ }
+
+ p_sys->p_dw_sys = p_dw_sys;
+ return VLC_SUCCESS;
+}
+
+extern "C" int ReleaseDWrite( filter_t *p_filter )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ dw_sys_t *p_dw_sys = ( dw_sys_t * ) p_sys->p_dw_sys;
+
+#if VLC_WINSTORE_APP
+ delete p_dw_sys;
+#else
+ HMODULE p_dw_dll = NULL;
+ if( p_dw_sys && p_dw_sys->p_dw_dll )
+ p_dw_dll = p_dw_sys->p_dw_dll;
+
+ delete p_dw_sys;
+ if( p_dw_dll ) FreeLibrary( p_dw_dll );
+#endif
+
+ return VLC_SUCCESS;
+}
+
+extern "C" int DWrite_GetFontStream( filter_t *p_filter, int i_index, FT_Stream *pp_stream )
+{
+ dw_sys_t *p_dw_sys = ( dw_sys_t * ) p_filter->p_sys->p_dw_sys;
+
+ if( i_index < 0 || i_index >= ( int ) p_dw_sys->streams.size() )
+ return VLC_ENOITEM;
+
+ *pp_stream = p_dw_sys->streams.at( i_index );
+
+ return VLC_SUCCESS;
+}
+
+static inline void ToUTF16( uint32_t i_codepoint, wchar_t *p_out, uint32_t *i_length )
+{
+ if( i_codepoint < 0x10000 )
+ {
+ p_out[ 0 ] = i_codepoint;
+ *i_length = 1;
+ }
+ else
+ {
+ i_codepoint -= 0x10000;
+ p_out[ 0 ] = ( i_codepoint >> 10 ) + 0xd800;
+ p_out[ 1 ] = ( i_codepoint & 0x3ff ) + 0xdc00;
+ *i_length = 2;
+ }
+}
+
+extern "C" unsigned long DWrite_Read( FT_Stream p_stream, unsigned long l_offset,
+ unsigned char *p_buffer, unsigned long l_count )
+{
+ const void *p_dw_fragment = NULL;
+ void *p_dw_fragment_context = NULL;
+
+ if( l_count == 0 ) return 0;
+
+ IDWriteFontFileStream *p_dw_stream = ( IDWriteFontFileStream * ) p_stream->descriptor.pointer;
+
+ if( p_dw_stream->ReadFileFragment( &p_dw_fragment, l_offset, l_count, &p_dw_fragment_context ) )
+ return 0;
+
+ memcpy( p_buffer, p_dw_fragment, l_count );
+
+ p_dw_stream->ReleaseFileFragment( p_dw_fragment_context );
+
+ return l_count;
+}
+
+extern "C" void DWrite_Close( FT_Stream )
+{
+}
+
+class TextSource : public IDWriteTextAnalysisSource
+{
+ IDWriteFactory *mp_factory;
+ IDWriteNumberSubstitution *mp_substitution;
+ wchar_t mpwsz_text[ 3 ];
+ uint32_t mi_text_length;
+ ULONG ml_ref_count;
+
+public:
+ TextSource( IDWriteFactory *p_factory, IDWriteNumberSubstitution *p_substitution,
+ const wchar_t *pwsz_text, uint32_t i_text_length )
+ : mp_factory( p_factory ), mp_substitution( p_substitution ), ml_ref_count( 1 )
+ {
+ memset( mpwsz_text, 0, sizeof( mpwsz_text ) );
+ mi_text_length = i_text_length < 2 ? i_text_length : 2;
+ memcpy( mpwsz_text, pwsz_text, mi_text_length * sizeof( *mpwsz_text ) );
+ }
+ virtual ~TextSource() {}
+
+ virtual HRESULT STDMETHODCALLTYPE GetLocaleName( UINT32, UINT32 *,
+ const WCHAR **ppwsz_locale_name )
+ {
+ *ppwsz_locale_name = L"en-US";
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetNumberSubstitution( UINT32, UINT32 *,
+ IDWriteNumberSubstitution **pp_substitution )
+ {
+ mp_substitution->AddRef();
+ *pp_substitution = mp_substitution;
+ return S_OK;
+ }
+
+ virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection()
+ {
+ return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTextAtPosition( UINT32 i_text_position, const WCHAR **ppwsz_text,
+ UINT32 *pi_text_length )
+ {
+ if( i_text_position > mi_text_length )
+ return E_INVALIDARG;
+
+ *ppwsz_text = mpwsz_text + i_text_position;
+ *pi_text_length = mi_text_length - i_text_position;
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTextBeforePosition( UINT32 i_text_position, const WCHAR **ppwsz_text,
+ UINT32 *pi_text_length )
+ {
+ if( i_text_position > mi_text_length )
+ return E_INVALIDARG;
+
+ if( i_text_position == 0 )
+ {
+ *ppwsz_text = NULL;
+ *pi_text_length = 0;
+ return S_OK;
+ }
+
+ *ppwsz_text = mpwsz_text;
+ *pi_text_length = i_text_position;
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID *pp_obj )
+ {
+ if( !pp_obj )
+ return E_INVALIDARG;
+
+ *pp_obj = NULL;
+
+ if( riid == IID_IUnknown || riid == __uuidof( IDWriteTextAnalysisSource ) )
+ {
+ *pp_obj = ( LPVOID ) this;
+ AddRef();
+ return NOERROR;
+ }
+ return E_NOINTERFACE;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement( &ml_ref_count );
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ ULONG l_ret = InterlockedDecrement( &ml_ref_count );
+ if( l_ret == 0 )
+ {
+ delete this;
+ }
+
+ return l_ret;
+ }
+};
+
+static void DWrite_ParseFamily( IDWriteFontFamily *p_dw_family, vlc_family_t *p_family, vector< FT_Stream > &streams )
+{
+ for( int i = 0; i < 4; ++i )
+ {
+ ComPtr< IDWriteFont > p_dw_font;
+ ComPtr< IDWriteFontFace > p_dw_face;
+ ComPtr< IDWriteFontFileLoader > p_dw_loader;
+ ComPtr< IDWriteFontFile > p_dw_file;
+
+ DWRITE_FONT_STYLE style;
+ DWRITE_FONT_WEIGHT weight;
+ bool b_bold, b_italic;
+
+ switch( i )
+ {
+ case 0:
+ weight = DWRITE_FONT_WEIGHT_NORMAL; style = DWRITE_FONT_STYLE_NORMAL;
+ b_bold = false; b_italic = false; break;
+ case 1:
+ weight = DWRITE_FONT_WEIGHT_BOLD; style = DWRITE_FONT_STYLE_NORMAL;
+ b_bold = true; b_italic = false; break;
+ case 2:
+ weight = DWRITE_FONT_WEIGHT_NORMAL; style = DWRITE_FONT_STYLE_ITALIC;
+ b_bold = false; b_italic = true; break;
+ case 3:
+ weight = DWRITE_FONT_WEIGHT_BOLD; style = DWRITE_FONT_STYLE_ITALIC;
+ b_bold = true; b_italic = true; break;
+ }
+
+ if( p_dw_family->GetFirstMatchingFont( weight, DWRITE_FONT_STRETCH_NORMAL, style, p_dw_font.GetAddressOf() )
+ || !p_dw_font )
+ throw runtime_error( "GetFirstMatchingFont() failed" );
+
+ if( p_dw_font->CreateFontFace( p_dw_face.GetAddressOf() ) )
+ throw runtime_error( "CreateFontFace() failed" );
+
+ UINT32 i_num_files = 0;
+ if( p_dw_face->GetFiles( &i_num_files, NULL ) || i_num_files == 0 )
+ throw runtime_error( "GetFiles() failed" );
+
+ i_num_files = 1;
+ if( p_dw_face->GetFiles( &i_num_files, p_dw_file.GetAddressOf() ) )
+ throw runtime_error( "GetFiles() failed" );
+
+ UINT32 i_font_index = p_dw_face->GetIndex();
+
+ if( p_dw_file->GetLoader( p_dw_loader.GetAddressOf() ) )
+ throw runtime_error( "GetLoader() failed" );
+
+ const void *key;
+ UINT32 keySize;
+ if( p_dw_file->GetReferenceKey( &key, &keySize ) )
+ throw runtime_error( "GetReferenceKey() failed" );
+
+ ComPtr< IDWriteFontFileStream > p_dw_stream;
+ if( p_dw_loader->CreateStreamFromKey( key, keySize, p_dw_stream.GetAddressOf() ) )
+ throw runtime_error( "CreateStreamFromKey() failed" );
+
+ UINT64 i_stream_size;
+ if( p_dw_stream->GetFileSize( &i_stream_size ) )
+ throw runtime_error( "GetFileSize() failed" );
+
+ FT_Stream p_stream = ( FT_Stream ) calloc( 1, sizeof( *p_stream ) );
+ if( p_stream == NULL ) throw bad_alloc();
+
+ p_stream->descriptor.pointer = p_dw_stream.Get();
+ p_stream->read = DWrite_Read;
+ p_stream->close = DWrite_Close;
+ p_stream->size = i_stream_size;
+
+ try { streams.push_back( p_stream ); }
+ catch( ... ) { free( p_stream ); throw; }
+ p_dw_stream.Detach();
+
+ char *psz_font_path = NULL;
+ if( asprintf( &psz_font_path, ":dw/%d", ( int ) streams.size() - 1 ) < 0 )
+ throw bad_alloc();
+
+ NewFont( psz_font_path, i_font_index, b_bold, b_italic, p_family );
+ }
+}
+
+extern "C" const vlc_family_t *DWrite_GetFamily( filter_t *p_filter, const char *psz_family )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ dw_sys_t *p_dw_sys = ( dw_sys_t * ) p_sys->p_dw_sys;
+ ComPtr< IDWriteFontFamily > p_dw_family;
+
+ char *psz_lc = ToLower( psz_family );
+ if( unlikely( !psz_lc ) )
+ return NULL;
+
+ vlc_family_t *p_family =
+ ( vlc_family_t * ) vlc_dictionary_value_for_key( &p_sys->family_map, psz_lc );
+
+ free( psz_lc );
+
+ if( p_family )
+ return p_family;
+
+ p_family = NewFamily( p_filter, psz_family, &p_sys->p_families,
+ &p_sys->family_map, psz_family );
+
+ if( unlikely( !p_family ) )
+ return NULL;
+
+ msg_Dbg( p_filter, "DWrite_GetFamily(): family name: %s", psz_family );
+
+ wchar_t *pwsz_family = ToWide( psz_family );
+ if( unlikely( !pwsz_family ) )
+ goto done;
+
+ UINT32 i_index;
+ BOOL b_exists;
+
+ if( p_dw_sys->p_dw_system_fonts->FindFamilyName( pwsz_family, &i_index, &b_exists ) || !b_exists )
+ {
+ msg_Warn( p_filter, "DWrite_GetFamily: FindFamilyName() failed" );
+ goto done;
+ }
+
+ if( p_dw_sys->p_dw_system_fonts->GetFontFamily( i_index, p_dw_family.GetAddressOf() ) )
+ {
+ msg_Err( p_filter, "DWrite_GetFamily: GetFontFamily() failed" );
+ goto done;
+ }
+
+ try
+ {
+ DWrite_ParseFamily( p_dw_family.Get(), p_family, p_dw_sys->streams );
+ }
+ catch( const exception &e )
+ {
+ msg_Err( p_filter, "DWrite_GetFamily(): %s", e.what() );
+ }
+
+done:
+ free( pwsz_family );
+ return p_family;
+}
+
+static char *DWrite_Fallback( filter_t *p_filter, const char *psz_family,
+ uni_char_t codepoint )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ dw_sys_t *p_dw_sys = ( dw_sys_t * ) p_sys->p_dw_sys;
+ wchar_t *pwsz_buffer = NULL;
+ char *psz_result = NULL;
+ UINT32 i_string_length = 0;
+ UINT32 i_mapped_length = 0;
+ float f_scale;
+ ComPtr< IDWriteFont > p_dw_font;
+ ComPtr< IDWriteFontFamily > p_dw_family;
+ ComPtr< IDWriteLocalizedStrings > p_names;
+
+ msg_Dbg( p_filter, "DWrite_Fallback(): family: %s, codepoint: 0x%x", psz_family, codepoint );
+
+ wchar_t *pwsz_family = ToWide( psz_family );
+ if( unlikely( !pwsz_family ) ) return NULL;
+
+ wchar_t p_text[2];
+ UINT32 i_text_length;
+ ToUTF16( codepoint, p_text, &i_text_length );
+
+ ComPtr< TextSource > p_ts;
+ p_ts = new(std::nothrow) TextSource( p_dw_sys->p_dw_factory.Get(), p_dw_sys->p_dw_substitution.Get(), p_text, i_text_length );
+ if( unlikely( p_ts == NULL ) ) { goto done; }
+
+ if( p_dw_sys->p_dw_fallbacks->MapCharacters( p_ts.Get(), 0, i_text_length, p_dw_sys->p_dw_system_fonts.Get(), pwsz_family,
+ DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
+ &i_mapped_length, p_dw_font.GetAddressOf(), &f_scale )
+ || !p_dw_font )
+ {
+ msg_Warn( p_filter, "DWrite_Fallback(): MapCharacters() failed" );
+ goto done;
+ }
+
+ if( p_dw_font->GetFontFamily( p_dw_family.GetAddressOf() ) )
+ {
+ msg_Err( p_filter, "DWrite_Fallback(): GetFontFamily() failed" );
+ goto done;
+ }
+
+ if( p_dw_family->GetFamilyNames( p_names.GetAddressOf() ) )
+ {
+ msg_Err( p_filter, "DWrite_Fallback(): GetFamilyNames() failed" );
+ goto done;
+ }
+
+ if( p_names->GetStringLength( 0, &i_string_length ) )
+ {
+ msg_Err( p_filter, "DWrite_Fallback(): GetStringLength() failed" );
+ goto done;
+ }
+
+ pwsz_buffer = ( wchar_t * ) malloc( ( i_string_length + 1 ) * sizeof( *pwsz_buffer ) );
+ if( unlikely( !pwsz_buffer ) )
+ goto done;
+
+ if( p_names->GetString( 0, pwsz_buffer, i_string_length + 1 ) )
+ {
+ msg_Err( p_filter, "DWrite_Fallback(): GetString() failed" );
+ goto done;
+ }
+
+ psz_result = FromWide( pwsz_buffer );
+ msg_Dbg( p_filter, "DWrite_Fallback(): returning %s", psz_result );
+
+done:
+ free( pwsz_buffer );
+ free( pwsz_family );
+ return psz_result;
+}
+
+extern "C" vlc_family_t *DWrite_GetFallbacks( filter_t *p_filter, const char *psz_family,
+ uni_char_t codepoint )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ vlc_family_t *p_family = NULL;
+ vlc_family_t *p_fallbacks = NULL;
+ char *psz_fallback = NULL;
+
+
+ char *psz_lc = ToLower( psz_family );
+
+ if( unlikely( !psz_lc ) )
+ return NULL;
+
+ p_fallbacks = ( vlc_family_t * ) vlc_dictionary_value_for_key( &p_sys->fallback_map, psz_lc );
+
+ if( p_fallbacks )
+ p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
+
+ /*
+ * If the fallback list of psz_family has no family which contains the requested
+ * codepoint, try DWrite_Fallback(). If it returns a valid family which does
+ * contain that codepoint, add the new family to the fallback list to speed up
+ * later searches.
+ */
+ if( !p_family )
+ {
+ psz_fallback = DWrite_Fallback( p_filter, psz_lc, codepoint );
+
+ if( !psz_fallback )
+ goto done;
+
+ const vlc_family_t *p_fallback = DWrite_GetFamily( p_filter, psz_fallback );
+ if( !p_fallback || !p_fallback->p_fonts )
+ goto done;
+
+ FT_Face p_face = GetFace( p_filter, p_fallback->p_fonts );
+
+ if( !p_face || !FT_Get_Char_Index( p_face, codepoint ) )
+ goto done;
+
+ p_family = NewFamily( p_filter, psz_fallback, NULL, NULL, NULL );
+
+ if( unlikely( !p_family ) )
+ goto done;
+
+ p_family->p_fonts = p_fallback->p_fonts;
+
+ if( p_fallbacks )
+ AppendFamily( &p_fallbacks, p_family );
+ else
+ vlc_dictionary_insert( &p_sys->fallback_map,
+ psz_lc, p_family );
+ }
+
+done:
+ free( psz_lc );
+ free( psz_fallback );
+ return p_family;
+}
diff --git a/modules/text_renderer/freetype/freetype.c b/modules/text_renderer/freetype/freetype.c
index 6baf45f..2683c1a 100644
--- a/modules/text_renderer/freetype/freetype.c
+++ b/modules/text_renderer/freetype/freetype.c
@@ -52,9 +52,7 @@
/* Win32 */
#ifdef _WIN32
# undef HAVE_FONTCONFIG
-# if !VLC_WINSTORE_APP
-# define HAVE_GET_FONT_BY_FAMILY_NAME
-# endif
+# define HAVE_GET_FONT_BY_FAMILY_NAME
#endif
/* FontConfig */
@@ -1268,14 +1266,29 @@ static int Create( vlc_object_t *p_this )
p_sys->pf_select = Generic_Select;
p_sys->pf_get_family = CoreText_GetFamily;
p_sys->pf_get_fallbacks = CoreText_GetFallbacks;
-#elif defined( _WIN32 ) && defined( HAVE_GET_FONT_BY_FAMILY_NAME )
- const char *const ppsz_win32_default[] =
- { "Tahoma", "FangSong", "SimHei", "KaiTi" };
- p_sys->pf_get_family = Win32_GetFamily;
- p_sys->pf_get_fallbacks = Win32_GetFallbacks;
- p_sys->pf_select = Generic_Select;
- InitDefaultList( p_filter, ppsz_win32_default,
- sizeof( ppsz_win32_default ) / sizeof( *ppsz_win32_default ) );
+#elif defined( _WIN32 )
+ if( InitDWrite( p_filter ) == VLC_SUCCESS )
+ {
+ p_sys->pf_get_family = DWrite_GetFamily;
+ p_sys->pf_get_fallbacks = DWrite_GetFallbacks;
+ p_sys->pf_select = Generic_Select;
+ }
+ else
+ {
+#if VLC_WINSTORE_APP
+ msg_Err( p_filter, "Error initializing DirectWrite" );
+ goto error;
+#else
+ msg_Warn( p_filter, "DirectWrite initialization failed. Falling back to GDI/Uniscribe" );
+ const char *const ppsz_win32_default[] =
+ { "Tahoma", "FangSong", "SimHei", "KaiTi" };
+ p_sys->pf_get_family = Win32_GetFamily;
+ p_sys->pf_get_fallbacks = Win32_GetFallbacks;
+ p_sys->pf_select = Generic_Select;
+ InitDefaultList( p_filter, ppsz_win32_default,
+ sizeof( ppsz_win32_default ) / sizeof( *ppsz_win32_default ) );
+#endif
+ }
#elif defined( __ANDROID__ )
p_sys->pf_get_family = Android_GetFamily;
p_sys->pf_get_fallbacks = Android_GetFallbacks;
@@ -1327,15 +1340,6 @@ static void Destroy( vlc_object_t *p_this )
DumpDictionary( p_filter, &p_sys->fallback_map, true, -1 );
#endif
- /* Attachments */
- 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 );
- }
-
/* Text styles */
text_style_Delete( p_sys->p_default_style );
text_style_Delete( p_sys->p_forced_style );
@@ -1347,6 +1351,20 @@ static void Destroy( vlc_object_t *p_this )
if( p_sys->p_families )
FreeFamiliesAndFonts( p_sys->p_families );
+ /* Attachments */
+ 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 defined( _WIN32 )
+ if( p_sys->pf_get_family == DWrite_GetFamily )
+ ReleaseDWrite( p_filter );
+#endif
+
/* Freetype */
if( p_sys->p_stroker )
FT_Stroker_Done( p_sys->p_stroker );
diff --git a/modules/text_renderer/freetype/freetype.h b/modules/text_renderer/freetype/freetype.h
index 44b2731..7a4e496 100644
--- a/modules/text_renderer/freetype/freetype.h
+++ b/modules/text_renderer/freetype/freetype.h
@@ -137,6 +137,10 @@ struct filter_sys_t
*/
vlc_family_t * (*pf_get_fallbacks) ( filter_t *p_filter, const char *psz_family,
uni_char_t codepoint );
+
+#if defined( _WIN32 )
+ void *p_dw_sys;
+#endif
};
/**
diff --git a/modules/text_renderer/freetype/platform_fonts.c b/modules/text_renderer/freetype/platform_fonts.c
index 24286af..bbd86fc 100644
--- a/modules/text_renderer/freetype/platform_fonts.c
+++ b/modules/text_renderer/freetype/platform_fonts.c
@@ -65,31 +65,44 @@ static FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx
return NULL;
FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->face_map, psz_key );
- if( p_face != kVLCDictionaryNotFound )
+ if( p_face != NULL )
goto done;
if( psz_fontfile[0] == ':' && psz_fontfile[1] == '/' )
{
int i_attach = atoi( psz_fontfile + 2 );
if( i_attach < 0 || i_attach >= p_sys->i_font_attachments )
- {
msg_Err( p_filter, "LoadFace: Invalid font attachment index" );
- p_face = NULL;
- }
else
{
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;
+ msg_Err( p_filter, "LoadFace: Error creating face for %s", psz_key );
}
}
+
+#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_filter, 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;
+ 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 );
+ }
+ }
+#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 );
- p_face = NULL;
- }
if( !p_face )
goto done;
@@ -545,6 +558,9 @@ char* Generic_Select( filter_t *p_filter, const char* psz_family,
if( !p_family )
p_family = p_sys->pf_get_family( p_filter, psz_family );
+ if( !p_family || !p_family->p_fonts )
+ p_family = p_sys->pf_get_family( p_filter, SYSTEM_DEFAULT_FAMILY );
+
vlc_font_t *p_font;
if( p_family && ( p_font = GetBestFont( p_filter, p_family, b_bold,
b_italic, codepoint ) ) )
diff --git a/modules/text_renderer/freetype/platform_fonts.h b/modules/text_renderer/freetype/platform_fonts.h
index 28a9925..83af800 100644
--- a/modules/text_renderer/freetype/platform_fonts.h
+++ b/modules/text_renderer/freetype/platform_fonts.h
@@ -47,6 +47,10 @@
#include "freetype.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Default fonts */
#ifdef __APPLE__
# define SYSTEM_DEFAULT_FONT_FILE "/System/Library/Fonts/HelveticaNeue.dfont"
@@ -143,11 +147,19 @@ const vlc_family_t *FontConfig_GetFamily( filter_t *p_filter, const char *psz_fa
int FontConfig_Prepare( filter_t *p_filter );
#endif /* FONTCONFIG */
-#if defined( _WIN32 ) && !VLC_WINSTORE_APP
+#if defined( _WIN32 )
+const vlc_family_t *DWrite_GetFamily( filter_t *p_filter, const char *psz_family );
+vlc_family_t *DWrite_GetFallbacks( filter_t *p_filter, const char *psz_family,
+ uni_char_t codepoint );
+int InitDWrite( filter_t *p_filter );
+int ReleaseDWrite( filter_t *p_filter );
+int DWrite_GetFontStream( filter_t *p_filter, int i_index, FT_Stream *pp_stream );
+#if !VLC_WINSTORE_APP
vlc_family_t *Win32_GetFallbacks( filter_t *p_filter, const char *psz_family,
uni_char_t codepoint );
const vlc_family_t *Win32_GetFamily( filter_t *p_filter, const char *psz_family );
+#endif /* !VLC_WINSTORE_APP */
#endif /* _WIN32 */
#ifdef __APPLE__
@@ -275,4 +287,8 @@ vlc_family_t *SearchFallbacks( filter_t *p_filter, vlc_family_t *p_fallbacks,
uni_char_t codepoint );
FT_Face GetFace( filter_t *p_filter, vlc_font_t *p_font );
+#ifdef __cplusplus
+}
+#endif
+
#endif //PLATFORM_FONTS_H
diff --git a/modules/text_renderer/freetype/text_layout.c b/modules/text_renderer/freetype/text_layout.c
index a8c8610..14cf7f1 100644
--- a/modules/text_renderer/freetype/text_layout.c
+++ b/modules/text_renderer/freetype/text_layout.c
@@ -69,9 +69,7 @@
/* Win32 */
#ifdef _WIN32
# undef HAVE_FONTCONFIG
-# if !VLC_WINSTORE_APP
-# define HAVE_FONT_FALLBACK
-# endif
+# define HAVE_FONT_FALLBACK
#endif
/* FontConfig */
--
2.10.2
More information about the vlc-devel
mailing list