]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
opus: update i_buffer when memmoving decoder output
[vlc] / modules / text_renderer / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *          Jean-Baptiste Kempf <jb@videolan.org>
11  *          Felix Paul Kühne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35 #include <math.h>
36
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_stream.h>                        /* stream_MemoryNew */
40 #include <vlc_input.h>                         /* vlc_input_attachment_* */
41 #include <vlc_xml.h>                           /* xml_reader */
42 #include <vlc_dialog.h>                        /* FcCache dialog */
43 #include <vlc_filter.h>                                      /* filter_sys_t */
44 #include <vlc_text_style.h>                                   /* text_style_t*/
45
46 /* Default fonts */
47 #ifdef __APPLE__
48 # define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
49 # define SYSTEM_DEFAULT_FAMILY "Arial Unicode MS"
50 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/System/Library/Fonts/Monaco.dfont"
51 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monaco"
52 #elif defined( _WIN32 )
53 # define SYSTEM_DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
54 # define SYSTEM_DEFAULT_FAMILY "Arial"
55 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "cour.ttf"
56 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Courier New"
57 #elif defined( __OS2__ )
58 # define SYSTEM_DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
59 # define SYSTEM_DEFAULT_FAMILY "Times New Roman WT K"
60 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/psfonts/mtsansdk.ttf"
61 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
62 #elif defined( __ANDROID__ )
63 # define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
64 # define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
65 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
66 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
67 #else
68 # define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
69 # define SYSTEM_DEFAULT_FAMILY "Serif Bold"
70 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeMono.ttf"
71 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
72 #endif
73
74 #ifndef DEFAULT_FONT_FILE
75 #define DEFAULT_FONT_FILE SYSTEM_DEFAULT_FONT_FILE
76 #endif
77
78 #ifndef DEFAULT_FAMILY
79 #define DEFAULT_FAMILY SYSTEM_DEFAULT_FAMILY
80 #endif
81
82 #ifndef DEFAULT_MONOSPACE_FONT_FILE
83 #define DEFAULT_MONOSPACE_FONT_FILE SYSTEM_DEFAULT_MONOSPACE_FONT_FILE
84 #endif
85
86 #ifndef DEFAULT_MONOSPACE_FAMILY
87 #define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
88 #endif
89
90 /* Freetype */
91 #include <freetype/ftsynth.h>
92 #include FT_FREETYPE_H
93 #include FT_GLYPH_H
94 #include FT_STROKER_H
95
96 #define FT_FLOOR(X)     ((X & -64) >> 6)
97 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
98 #ifndef FT_MulFix
99  #define FT_MulFix(v, s) (((v)*(s))>>16)
100 #endif
101
102 /* apple stuff */
103 #ifdef __APPLE__
104 #include <TargetConditionals.h>
105 #if !TARGET_OS_IPHONE
106 #include <Carbon/Carbon.h>
107 #endif
108 #include <sys/param.h>                         /* for MAXPATHLEN */
109 #undef HAVE_FONTCONFIG
110 #define HAVE_STYLES
111 #endif
112
113 /* RTL */
114 #if defined(HAVE_FRIBIDI)
115 # include <fribidi/fribidi.h>
116 #endif
117
118 /* Win32 GDI */
119 #ifdef _WIN32
120 # include <windows.h>
121 # include <shlobj.h>
122 # define HAVE_STYLES
123 # undef HAVE_FONTCONFIG
124 # include <vlc_charset.h>                                     /* FromT */
125 #endif
126
127 /* FontConfig */
128 #ifdef HAVE_FONTCONFIG
129 # include <fontconfig/fontconfig.h>
130 # define HAVE_STYLES
131 #endif
132
133 #include <assert.h>
134
135 #include "text_renderer.h"
136
137 /*****************************************************************************
138  * Module descriptor
139  *****************************************************************************/
140 static int  Create ( vlc_object_t * );
141 static void Destroy( vlc_object_t * );
142
143 #define FONT_TEXT N_("Font")
144 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
145
146 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
147 #define FONT_LONGTEXT N_("Font file for the font you want to use")
148
149 #define FONTSIZE_TEXT N_("Font size in pixels")
150 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
151     "that will be rendered on the video. " \
152     "If set to something different than 0 this option will override the " \
153     "relative font size." )
154 #define OPACITY_TEXT N_("Text opacity")
155 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
156     "text that will be rendered on the video. 0 = transparent, " \
157     "255 = totally opaque. " )
158 #define COLOR_TEXT N_("Text default color")
159 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
160     "the video. This must be an hexadecimal (like HTML colors). The first two "\
161     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
162     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
163 #define FONTSIZER_TEXT N_("Relative font size")
164 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
165     "fonts that will be rendered on the video. If absolute font size is set, "\
166     "relative size will be overridden." )
167 #define BOLD_TEXT N_("Force bold")
168
169 #define BG_OPACITY_TEXT N_("Background opacity")
170 #define BG_COLOR_TEXT N_("Background color")
171
172 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
173 #define OUTLINE_COLOR_TEXT N_("Outline color")
174 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
175
176 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
177 #define SHADOW_COLOR_TEXT N_("Shadow color")
178 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
179 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
180
181
182 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
183 static const char *const ppsz_sizes_text[] = {
184     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
185 #define YUVP_TEXT N_("Use YUVP renderer")
186 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
187   "This option is only needed if you want to encode into DVB subtitles" )
188
189 static const int pi_color_values[] = {
190   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
191   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
192   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
193
194 static const char *const ppsz_color_descriptions[] = {
195   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
196   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
197   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
198
199 static const int pi_outline_thickness[] = {
200     0, 2, 4, 6,
201 };
202 static const char *const ppsz_outline_thickness[] = {
203     N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
204 };
205
206 vlc_module_begin ()
207     set_shortname( N_("Text renderer"))
208     set_description( N_("Freetype2 font renderer") )
209     set_category( CAT_VIDEO )
210     set_subcategory( SUBCAT_VIDEO_SUBPIC )
211
212 #ifdef HAVE_STYLES
213     add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
214     add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
215 #else
216     add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
217     add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
218 #endif
219
220     add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
221                  FONTSIZE_LONGTEXT, true )
222         change_integer_range( 0, 4096)
223         change_safe()
224
225     add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
226                  FONTSIZER_LONGTEXT, false )
227         change_integer_list( pi_sizes, ppsz_sizes_text )
228         change_safe()
229
230     /* opacity valid on 0..255, with default 255 = fully opaque */
231     add_integer_with_range( "freetype-opacity", 255, 0, 255,
232         OPACITY_TEXT, OPACITY_LONGTEXT, false )
233         change_safe()
234
235     /* hook to the color values list, with default 0x00ffffff = white */
236     add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
237                  COLOR_LONGTEXT, false )
238         change_integer_list( pi_color_values, ppsz_color_descriptions )
239         change_safe()
240
241     add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
242         change_safe()
243
244     add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
245                             BG_OPACITY_TEXT, NULL, false )
246         change_safe()
247     add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
248              NULL, false )
249         change_integer_list( pi_color_values, ppsz_color_descriptions )
250         change_safe()
251
252     add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
253                             OUTLINE_OPACITY_TEXT, NULL, false )
254         change_safe()
255     add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
256              NULL, false )
257         change_integer_list( pi_color_values, ppsz_color_descriptions )
258         change_safe()
259     add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
260              NULL, false )
261         change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
262         change_safe()
263
264     add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
265                             SHADOW_OPACITY_TEXT, NULL, false )
266         change_safe()
267     add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
268              NULL, false )
269         change_integer_list( pi_color_values, ppsz_color_descriptions )
270         change_safe()
271     add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
272                           SHADOW_ANGLE_TEXT, NULL, false )
273         change_safe()
274     add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
275                           SHADOW_DISTANCE_TEXT, NULL, false )
276         change_safe()
277
278     add_obsolete_integer( "freetype-effect" );
279
280     add_bool( "freetype-yuvp", false, YUVP_TEXT,
281               YUVP_LONGTEXT, true )
282     set_capability( "text renderer", 100 )
283     add_shortcut( "text" )
284     set_callbacks( Create, Destroy )
285 vlc_module_end ()
286
287
288 /*****************************************************************************
289  * Local prototypes
290  *****************************************************************************/
291
292 typedef struct
293 {
294     FT_BitmapGlyph p_glyph;
295     FT_BitmapGlyph p_outline;
296     FT_BitmapGlyph p_shadow;
297     uint32_t       i_color;             /* ARGB color */
298     int            i_line_offset;       /* underline/strikethrough offset */
299     int            i_line_thickness;    /* underline/strikethrough thickness */
300 } line_character_t;
301
302 typedef struct line_desc_t line_desc_t;
303 struct line_desc_t
304 {
305     line_desc_t      *p_next;
306
307     int              i_width;
308     int              i_height;
309     int              i_base_line;
310     int              i_character_count;
311     line_character_t *p_character;
312 };
313
314 /*****************************************************************************
315  * filter_sys_t: freetype local data
316  *****************************************************************************
317  * This structure is part of the video output thread descriptor.
318  * It describes the freetype specific properties of an output thread.
319  *****************************************************************************/
320 struct filter_sys_t
321 {
322     FT_Library     p_library;   /* handle to library     */
323     FT_Face        p_face;      /* handle to face object */
324     FT_Stroker     p_stroker;   /* handle to path stroker object */
325
326     xml_reader_t  *p_xml;       /* vlc xml parser */
327
328     text_style_t   style;       /* Current Style */
329
330     /* More styles... */
331     float          f_shadow_vector_x;
332     float          f_shadow_vector_y;
333     int            i_default_font_size;
334
335     /* Attachments */
336     input_attachment_t **pp_font_attachments;
337     int                  i_font_attachments;
338
339     /* Cache the Win32 font folder */
340 #ifdef _WIN32
341     char*          psz_win_fonts_path;
342 #endif
343
344 };
345
346 /* */
347 static void YUVFromRGB( uint32_t i_argb,
348                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
349 {
350     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
351     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
352     int i_blue  = ( i_argb & 0x000000ff );
353
354     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
355                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
356     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
357                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
358     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
359                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
360 }
361 static void RGBFromRGB( uint32_t i_argb,
362                         uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
363 {
364     *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
365     *pi_g = ( i_argb & 0x0000ff00 ) >>  8;
366     *pi_b = ( i_argb & 0x000000ff );
367 }
368
369 /*****************************************************************************
370  * Make any TTF/OTF fonts present in the attachments of the media file
371  * and store them for later use by the FreeType Engine
372  *****************************************************************************/
373 static int LoadFontsFromAttachments( filter_t *p_filter )
374 {
375     filter_sys_t         *p_sys = p_filter->p_sys;
376     input_attachment_t  **pp_attachments;
377     int                   i_attachments_cnt;
378
379     if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
380         return VLC_EGENERIC;
381
382     p_sys->i_font_attachments = 0;
383     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
384     if( !p_sys->pp_font_attachments )
385         return VLC_ENOMEM;
386
387     for( int k = 0; k < i_attachments_cnt; k++ )
388     {
389         input_attachment_t *p_attach = pp_attachments[k];
390
391         if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
392               !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
393             p_attach->i_data > 0 && p_attach->p_data )
394         {
395             p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
396         }
397         else
398         {
399             vlc_input_attachment_Delete( p_attach );
400         }
401     }
402     free( pp_attachments );
403
404     return VLC_SUCCESS;
405 }
406
407 static int GetFontSize( filter_t *p_filter )
408 {
409     filter_sys_t *p_sys = p_filter->p_sys;
410     int           i_size = 0;
411
412     if( p_sys->i_default_font_size )
413     {
414         i_size = p_sys->i_default_font_size;
415     }
416     else
417     {
418         int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
419         if( i_ratio > 0 )
420         {
421             i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
422         }
423     }
424     if( i_size <= 0 )
425     {
426         msg_Warn( p_filter, "invalid fontsize, using 12" );
427         i_size = 12;
428     }
429     return i_size;
430 }
431
432 static int SetFontSize( filter_t *p_filter, int i_size )
433 {
434     filter_sys_t *p_sys = p_filter->p_sys;
435
436     if( !i_size )
437     {
438         i_size = GetFontSize( p_filter );
439
440         msg_Dbg( p_filter, "using fontsize: %i", i_size );
441     }
442
443     p_sys->style.i_font_size = i_size;
444
445     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
446     {
447         msg_Err( p_filter, "couldn't set font size to %d", i_size );
448         return VLC_EGENERIC;
449     }
450
451     return VLC_SUCCESS;
452 }
453
454 #ifdef HAVE_STYLES
455 #ifdef HAVE_FONTCONFIG
456 static void FontConfig_BuildCache( filter_t *p_filter )
457 {
458     /* */
459     msg_Dbg( p_filter, "Building font databases.");
460     mtime_t t1, t2;
461     t1 = mdate();
462
463 #ifdef __OS2__
464     FcInit();
465 #endif
466
467 #if defined( _WIN32 ) || defined( __APPLE__ )
468     dialog_progress_bar_t *p_dialog = NULL;
469     FcConfig *fcConfig = FcInitLoadConfig();
470
471     p_dialog = dialog_ProgressCreate( p_filter,
472             _("Building font cache"),
473             _("Please wait while your font cache is rebuilt.\n"
474                 "This should take less than a few minutes."), NULL );
475
476 /*    if( p_dialog )
477         dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
478
479     FcConfigBuildFonts( fcConfig );
480 #if defined( __APPLE__ )
481     // By default, scan only the directory /System/Library/Fonts.
482     // So build the set of available fonts under another directories,
483     // and add the set to the current configuration.
484     FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
485     FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
486     FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
487     //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
488 #endif
489     if( p_dialog )
490     {
491 //        dialog_ProgressSet( p_dialog, NULL, 1.0 );
492         dialog_ProgressDestroy( p_dialog );
493         p_dialog = NULL;
494     }
495 #endif
496     t2 = mdate();
497     msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
498 }
499
500 /***
501  * \brief Selects a font matching family, bold, italic provided
502  ***/
503 static char* FontConfig_Select( FcConfig* config, const char* family,
504                           bool b_bold, bool b_italic, int i_size, int *i_idx )
505 {
506     FcResult result = FcResultMatch;
507     FcPattern *pat, *p_pat;
508     FcChar8* val_s;
509     FcBool val_b;
510     char *ret = NULL;
511
512     /* Create a pattern and fills it */
513     pat = FcPatternCreate();
514     if (!pat) return NULL;
515
516     /* */
517     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
518     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
519     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
520     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
521     if( i_size != -1 )
522     {
523         char *psz_fontsize;
524         if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
525         {
526             FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
527             free( psz_fontsize );
528         }
529     }
530
531     /* */
532     FcDefaultSubstitute( pat );
533     if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
534     {
535         FcPatternDestroy( pat );
536         return NULL;
537     }
538
539     /* Find the best font for the pattern, destroy the pattern */
540     p_pat = FcFontMatch( config, pat, &result );
541     FcPatternDestroy( pat );
542     if( !p_pat || result == FcResultNoMatch ) return NULL;
543
544     /* Check the new pattern */
545     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
546         || ( val_b != FcTrue ) )
547     {
548         FcPatternDestroy( p_pat );
549         return NULL;
550     }
551     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
552     {
553         *i_idx = 0;
554     }
555
556     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
557     {
558         FcPatternDestroy( p_pat );
559         return NULL;
560     }
561
562     /* if( strcasecmp((const char*)val_s, family ) != 0 )
563         msg_Warn( p_filter, "fontconfig: selected font family is not"
564                             "the requested one: '%s' != '%s'\n",
565                             (const char*)val_s, family );   */
566
567     if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
568         ret = strdup( (const char*)val_s );
569
570     FcPatternDestroy( p_pat );
571     return ret;
572 }
573 #endif
574
575 #ifdef _WIN32
576 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
577
578 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
579 {
580     HKEY hKey;
581     TCHAR vbuffer[MAX_PATH];
582     TCHAR dbuffer[256];
583
584     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
585             != ERROR_SUCCESS )
586         return 1;
587
588     char *font_name_temp = FromT( font_name );
589     size_t fontname_len = strlen( font_name_temp );
590
591     for( int index = 0;; index++ )
592     {
593         DWORD vbuflen = MAX_PATH - 1;
594         DWORD dbuflen = 255;
595
596         LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
597                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
598         if( i_result != ERROR_SUCCESS )
599         {
600             RegCloseKey( hKey );
601             return i_result;
602         }
603
604         char *psz_value = FromT( vbuffer );
605
606         char *s = strchr( psz_value,'(' );
607         if( s != NULL && s != psz_value ) s[-1] = '\0';
608
609         /* Manage concatenated font names */
610         if( strchr( psz_value, '&') ) {
611             if( strcasestr( psz_value, font_name_temp ) != NULL )
612             {
613                 free( psz_value );
614                 break;
615             }
616         }
617         else {
618             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
619             {
620                 free( psz_value );
621                 break;
622             }
623         }
624
625         free( psz_value );
626     }
627
628     *psz_filename = FromT( dbuffer );
629     free( font_name_temp );
630     RegCloseKey( hKey );
631     return 0;
632 }
633
634
635 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
636                                      DWORD type, LPARAM lParam)
637 {
638     VLC_UNUSED( metric );
639     if( (type & RASTER_FONTTYPE) ) return 1;
640     // if( lpelfe->elfScript ) FIXME
641
642     return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
643 }
644
645 static char* Win32_Select( filter_t *p_filter, const char* family,
646                            bool b_bold, bool b_italic, int i_size, int *i_idx )
647 {
648     VLC_UNUSED( i_size );
649
650     if( !family || strlen( family ) < 1 )
651         goto fail;
652
653     /* */
654     LOGFONT lf;
655     lf.lfCharSet = DEFAULT_CHARSET;
656     if( b_italic )
657         lf.lfItalic = true;
658     if( b_bold )
659         lf.lfWeight = FW_BOLD;
660
661     LPTSTR psz_fbuffer = ToT( family );
662     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
663     free( psz_fbuffer );
664
665     /* */
666     char *psz_filename = NULL;
667     HDC hDC = GetDC( NULL );
668     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
669     ReleaseDC(NULL, hDC);
670
671     /* */
672     if( psz_filename != NULL )
673     {
674         /* FIXME: increase i_idx, when concatenated strings  */
675         i_idx = 0;
676
677         /* Prepend the Windows Font path, when only a filename was provided */
678         if( strchr( psz_filename, DIR_SEP_CHAR ) )
679             return psz_filename;
680         else
681         {
682             char *psz_tmp;
683             if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
684             {
685                 free( psz_filename );
686                 return NULL;
687             }
688             free( psz_filename );
689             return psz_tmp;
690         }
691     }
692     else /* Let's take any font we can */
693 fail:
694     {
695         char *psz_tmp;
696         if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
697             return NULL;
698         else
699             return psz_tmp;
700     }
701 }
702 #endif /* _WIN32 */
703
704 #ifdef __APPLE__
705 #if !TARGET_OS_IPHONE
706 static char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
707                           bool b_bold, bool b_italic, int i_size, int *i_idx )
708 {
709     VLC_UNUSED( b_bold );
710     VLC_UNUSED( b_italic );
711     VLC_UNUSED( i_size );
712     FSRef ref;
713     unsigned char path[MAXPATHLEN];
714     char * psz_path;
715
716     CFStringRef  cf_fontName;
717     ATSFontRef   ats_font_id;
718
719     *i_idx = 0;
720
721     if( psz_fontname == NULL )
722         return NULL;
723
724     msg_Dbg( p_filter, "looking for %s", psz_fontname );
725     cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
726
727     ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
728
729     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
730     {
731         msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
732         ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
733
734         if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
735         {
736             msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
737             ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
738
739             if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
740             {
741                 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
742                 CFRelease( cf_fontName );
743                 return NULL;
744             }
745         }
746     }
747     CFRelease( cf_fontName );
748
749     if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
750     {
751         msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
752         return NULL;
753     }
754
755     /* i_idx calculation by searching preceding fontIDs */
756     /* with same FSRef                                       */
757     {
758         ATSFontRef  id2 = ats_font_id - 1;
759         FSRef       ref2;
760
761         while ( id2 > 0 )
762         {
763             if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
764                 break;
765             if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
766                 break;
767
768             id2 --;
769         }
770         *i_idx = ats_font_id - ( id2 + 1 );
771     }
772
773     if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
774     {
775         msg_Err( p_filter, "failure when getting path from FSRef" );
776         return NULL;
777     }
778     msg_Dbg( p_filter, "found %s", path );
779
780     psz_path = strdup( (char *)path );
781
782     return psz_path;
783 }
784 #endif
785 #endif
786
787 #endif /* HAVE_STYLES */
788
789
790 /*****************************************************************************
791  * RenderYUVP: place string in picture
792  *****************************************************************************
793  * This function merges the previously rendered freetype glyphs into a picture
794  *****************************************************************************/
795 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
796                        line_desc_t *p_line,
797                        FT_BBox *p_bbox )
798 {
799     VLC_UNUSED(p_filter);
800     static const uint8_t pi_gamma[16] =
801         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
802           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
803
804     uint8_t *p_dst;
805     video_format_t fmt;
806     int i, x, y, i_pitch;
807     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
808
809     /* Create a new subpicture region */
810     video_format_Init( &fmt, VLC_CODEC_YUVP );
811     fmt.i_width          =
812     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
813     fmt.i_height         =
814     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
815
816     assert( !p_region->p_picture );
817     p_region->p_picture = picture_NewFromFormat( &fmt );
818     if( !p_region->p_picture )
819         return VLC_EGENERIC;
820     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
821     p_region->fmt = fmt;
822
823     /* Calculate text color components
824      * Only use the first color */
825     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
826     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
827
828     /* Build palette */
829     fmt.p_palette->i_entries = 16;
830     for( i = 0; i < 8; i++ )
831     {
832         fmt.p_palette->palette[i][0] = 0;
833         fmt.p_palette->palette[i][1] = 0x80;
834         fmt.p_palette->palette[i][2] = 0x80;
835         fmt.p_palette->palette[i][3] = pi_gamma[i];
836         fmt.p_palette->palette[i][3] =
837             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
838     }
839     for( i = 8; i < fmt.p_palette->i_entries; i++ )
840     {
841         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
842         fmt.p_palette->palette[i][1] = i_u;
843         fmt.p_palette->palette[i][2] = i_v;
844         fmt.p_palette->palette[i][3] = pi_gamma[i];
845         fmt.p_palette->palette[i][3] =
846             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
847     }
848
849     p_dst = p_region->p_picture->Y_PIXELS;
850     i_pitch = p_region->p_picture->Y_PITCH;
851
852     /* Initialize the region pixels */
853     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
854
855     for( ; p_line != NULL; p_line = p_line->p_next )
856     {
857         int i_align_left = 0;
858         if( p_line->i_width < (int)fmt.i_visible_width )
859         {
860             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
861                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
862             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
863                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
864         }
865         int i_align_top = 0;
866
867         for( i = 0; i < p_line->i_character_count; i++ )
868         {
869             const line_character_t *ch = &p_line->p_character[i];
870             FT_BitmapGlyph p_glyph = ch->p_glyph;
871
872             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
873             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
874
875             for( y = 0; y < p_glyph->bitmap.rows; y++ )
876             {
877                 for( x = 0; x < p_glyph->bitmap.width; x++ )
878                 {
879                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
880                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
881                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
882                 }
883             }
884         }
885     }
886
887     /* Outlining (find something better than nearest neighbour filtering ?) */
888     if( 1 )
889     {
890         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
891         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
892         uint8_t left, current;
893
894         for( y = 1; y < (int)fmt.i_height - 1; y++ )
895         {
896             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
897             p_dst += p_region->p_picture->Y_PITCH;
898             left = 0;
899
900             for( x = 1; x < (int)fmt.i_width - 1; x++ )
901             {
902                 current = p_dst[x];
903                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
904                              p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
905                 left = current;
906             }
907         }
908         memset( p_top, 0, fmt.i_width );
909     }
910
911     return VLC_SUCCESS;
912 }
913
914 /*****************************************************************************
915  * RenderYUVA: place string in picture
916  *****************************************************************************
917  * This function merges the previously rendered freetype glyphs into a picture
918  *****************************************************************************/
919 static void FillYUVAPicture( picture_t *p_picture,
920                              int i_a, int i_y, int i_u, int i_v )
921 {
922     memset( p_picture->p[0].p_pixels, i_y,
923             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
924     memset( p_picture->p[1].p_pixels, i_u,
925             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
926     memset( p_picture->p[2].p_pixels, i_v,
927             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
928     memset( p_picture->p[3].p_pixels, i_a,
929             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
930 }
931
932 static inline void BlendYUVAPixel( picture_t *p_picture,
933                                    int i_picture_x, int i_picture_y,
934                                    int i_a, int i_y, int i_u, int i_v,
935                                    int i_alpha )
936 {
937     int i_an = i_a * i_alpha / 255;
938
939     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
940     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
941     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
942     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
943
944     int i_ao = *p_a;
945     if( i_ao == 0 )
946     {
947         *p_y = i_y;
948         *p_u = i_u;
949         *p_v = i_v;
950         *p_a = i_an;
951     }
952     else
953     {
954         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
955         if( *p_a != 0 )
956         {
957             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
958             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
959             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
960         }
961     }
962 }
963
964 static void FillRGBAPicture( picture_t *p_picture,
965                              int i_a, int i_r, int i_g, int i_b )
966 {
967     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
968     {
969         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
970         {
971             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
972             p_rgba[0] = i_r;
973             p_rgba[1] = i_g;
974             p_rgba[2] = i_b;
975             p_rgba[3] = i_a;
976         }
977     }
978 }
979
980 static inline void BlendRGBAPixel( picture_t *p_picture,
981                                    int i_picture_x, int i_picture_y,
982                                    int i_a, int i_r, int i_g, int i_b,
983                                    int i_alpha )
984 {
985     int i_an = i_a * i_alpha / 255;
986
987     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
988
989     int i_ao = p_rgba[3];
990     if( i_ao == 0 )
991     {
992         p_rgba[0] = i_r;
993         p_rgba[1] = i_g;
994         p_rgba[2] = i_b;
995         p_rgba[3] = i_an;
996     }
997     else
998     {
999         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
1000         if( p_rgba[3] != 0 )
1001         {
1002             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
1003             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
1004             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
1005         }
1006     }
1007 }
1008
1009 static void FillARGBPicture(picture_t *pic, int a, int r, int g, int b)
1010 {
1011     if (a == 0)
1012         r = g = b = 0;
1013     if (a == r && a == b && a == g)
1014     {   /* fast path */
1015         memset(pic->p->p_pixels, a, pic->p->i_visible_lines * pic->p->i_pitch);
1016         return;
1017     }
1018
1019     uint_fast32_t pixel = VLC_FOURCC(a, r, g, b);
1020     uint8_t *line = pic->p->p_pixels;
1021
1022     for (unsigned lines = pic->p->i_visible_lines; lines > 0; lines--)
1023     {
1024         uint32_t *pixels = (uint32_t *)line;
1025         for (unsigned cols = pic->p->i_visible_pitch; cols > 0; cols -= 4)
1026             *(pixels++) = pixel;
1027         line += pic->p->i_pitch;
1028     }
1029 }
1030
1031 static inline void BlendARGBPixel(picture_t *pic, int pic_x, int pic_y,
1032                                   int a, int r, int g, int b, int alpha)
1033 {
1034     uint8_t *rgba = &pic->p->p_pixels[pic_y * pic->p->i_pitch + 4 * pic_x];
1035     int an = a * alpha / 255;
1036     int ao = rgba[3];
1037
1038     if (ao == 0)
1039     {
1040         rgba[0] = an;
1041         rgba[1] = r;
1042         rgba[2] = g;
1043         rgba[3] = b;
1044     }
1045     else
1046     {
1047         rgba[0] = 255 - (255 - rgba[0]) * (255 - an) / 255;
1048         if (rgba[0] != 0)
1049         {
1050             rgba[1] = (rgba[1] * ao * (255 - an) / 255 + r * an ) / rgba[0];
1051             rgba[2] = (rgba[2] * ao * (255 - an) / 255 + g * an ) / rgba[0];
1052             rgba[3] = (rgba[3] * ao * (255 - an) / 255 + b * an ) / rgba[0];
1053         }
1054     }
1055 }
1056
1057 static inline void BlendAXYZGlyph( picture_t *p_picture,
1058                                    int i_picture_x, int i_picture_y,
1059                                    int i_a, int i_x, int i_y, int i_z,
1060                                    FT_BitmapGlyph p_glyph,
1061                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1062
1063 {
1064     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
1065     {
1066         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1067             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
1068                         i_a, i_x, i_y, i_z,
1069                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
1070     }
1071 }
1072
1073 static inline void BlendAXYZLine( picture_t *p_picture,
1074                                   int i_picture_x, int i_picture_y,
1075                                   int i_a, int i_x, int i_y, int i_z,
1076                                   const line_character_t *p_current,
1077                                   const line_character_t *p_next,
1078                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1079 {
1080     int i_line_width = p_current->p_glyph->bitmap.width;
1081     if( p_next )
1082         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1083
1084     for( int dx = 0; dx < i_line_width; dx++ )
1085     {
1086         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
1087             BlendPixel( p_picture,
1088                         i_picture_x + dx,
1089                         i_picture_y + p_current->i_line_offset + dy,
1090                         i_a, i_x, i_y, i_z, 0xff );
1091     }
1092 }
1093
1094 static inline void RenderBackground( subpicture_region_t *p_region,
1095                                      line_desc_t *p_line_head,
1096                                      FT_BBox *p_bbox,
1097                                      int i_margin,
1098                                      picture_t *p_picture,
1099                                      int i_text_width,
1100                                      void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1101                                      void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1102 {
1103     for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1104     {
1105         int i_align_left = i_margin;
1106         int i_align_top = i_margin;
1107         int line_start = 0;
1108         int line_end = 0;
1109         unsigned line_top = 0;
1110         int line_bottom = 0;
1111         int max_height = 0;
1112
1113         if( p_line->i_width < i_text_width )
1114         {
1115             /* Left offset to take into account alignment */
1116             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1117                 i_align_left += ( i_text_width - p_line->i_width );
1118             else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1119                 i_align_left = i_margin; /* Keep it the way it is */
1120             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1121                 i_align_left += ( i_text_width - p_line->i_width ) / 2;
1122         }
1123
1124         /* Find the tallest character in the line */
1125         for( int i = 0; i < p_line->i_character_count; i++ ) {
1126             const line_character_t *ch = &p_line->p_character[i];
1127             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1128             if (p_glyph->top > max_height)
1129                 max_height = p_glyph->top;
1130         }
1131
1132         /* Compute the background for the line (identify leading/trailing space) */
1133         for( int i = 0; i < p_line->i_character_count; i++ ) {
1134             const line_character_t *ch = &p_line->p_character[i];
1135             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1136             if (p_glyph && p_glyph->bitmap.rows > 0) {
1137                 // Found a non-whitespace character
1138                 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
1139                 break;
1140             }
1141         }
1142
1143         /* Fudge factor to make sure caption background edges are left aligned
1144            despite variable font width */
1145         if (line_start < 12)
1146             line_start = 0;
1147
1148         /* Find right boundary for bounding box for background */
1149         for( int i = p_line->i_character_count; i > 0; i-- ) {
1150             const line_character_t *ch = &p_line->p_character[i - 1];
1151             FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
1152             if (p_glyph && p_glyph->bitmap.rows > 0) {
1153                 // Found a non-whitespace character
1154                 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
1155                 break;
1156             }
1157         }
1158
1159         /* Setup color for the background */
1160         uint8_t i_x, i_y, i_z;
1161         ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
1162
1163         /* Compute the upper boundary for the background */
1164         if ((i_align_top + p_line->i_base_line - max_height) < 0)
1165             line_top = i_align_top + p_line->i_base_line;
1166         else
1167             line_top = i_align_top + p_line->i_base_line - max_height;
1168
1169         /* Compute lower boundary for the background */
1170         line_bottom =  __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
1171
1172         /* Render the actual background */
1173         for( int dy = line_top; dy < line_bottom; dy++ )
1174         {
1175             for( int dx = line_start; dx < line_end; dx++ )
1176                 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
1177         }
1178     }
1179 }
1180
1181 static inline int RenderAXYZ( filter_t *p_filter,
1182                               subpicture_region_t *p_region,
1183                               line_desc_t *p_line_head,
1184                               FT_BBox *p_bbox,
1185                               int i_margin,
1186                               vlc_fourcc_t i_chroma,
1187                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1188                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
1189                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1190 {
1191     filter_sys_t *p_sys = p_filter->p_sys;
1192
1193     /* Create a new subpicture region */
1194     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
1195     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
1196     video_format_t fmt;
1197     video_format_Init( &fmt, i_chroma );
1198     fmt.i_width          =
1199     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
1200     fmt.i_height         =
1201     fmt.i_visible_height = i_text_height + 2 * i_margin;
1202
1203     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
1204     if( !p_region->p_picture )
1205         return VLC_EGENERIC;
1206     p_region->fmt = fmt;
1207
1208     /* Initialize the picture background */
1209     uint8_t i_a = var_InheritInteger( p_filter, "freetype-background-opacity" );
1210     i_a = VLC_CLIP( i_a, 0, 255 );
1211     uint8_t i_x, i_y, i_z;
1212
1213     if (p_region->b_renderbg) {
1214         /* Render the background just under the text */
1215         FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
1216         RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
1217                          ExtractComponents, BlendPixel);
1218     } else {
1219         /* Render background under entire subpicture block */
1220         int i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
1221         i_background_color = VLC_CLIP( i_background_color, 0, 0xFFFFFF );
1222         ExtractComponents( i_background_color, &i_x, &i_y, &i_z );
1223         FillPicture( p_picture, i_a, i_x, i_y, i_z );
1224     }
1225
1226     /* Render shadow then outline and then normal glyphs */
1227     for( int g = 0; g < 3; g++ )
1228     {
1229         /* Render all lines */
1230         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1231         {
1232             int i_align_left = i_margin;
1233             if( p_line->i_width < i_text_width )
1234             {
1235                 /* Left offset to take into account alignment */
1236                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1237                     i_align_left += ( i_text_width - p_line->i_width );
1238                 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1239                     i_align_left = i_margin; /* Keep it the way it is */
1240                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1241                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
1242             }
1243             int i_align_top = i_margin;
1244
1245             /* Render all glyphs and underline/strikethrough */
1246             for( int i = 0; i < p_line->i_character_count; i++ )
1247             {
1248                 const line_character_t *ch = &p_line->p_character[i];
1249                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
1250                 if( !p_glyph )
1251                     continue;
1252
1253                 i_a = (ch->i_color >> 24) & 0xff;
1254                 uint32_t i_color;
1255                 switch (g) {
1256                 case 0:
1257                     i_a     = i_a * p_sys->style.i_shadow_alpha / 255;
1258                     i_color = p_sys->style.i_shadow_color;
1259                     break;
1260                 case 1:
1261                     i_a     = i_a * p_sys->style.i_outline_alpha / 255;
1262                     i_color = p_sys->style.i_outline_color;
1263                     break;
1264                 default:
1265                     i_color = ch->i_color;
1266                     break;
1267                 }
1268                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
1269
1270                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
1271                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
1272
1273                 BlendAXYZGlyph( p_picture,
1274                                 i_glyph_x, i_glyph_y,
1275                                 i_a, i_x, i_y, i_z,
1276                                 p_glyph,
1277                                 BlendPixel );
1278
1279                 /* underline/strikethrough are only rendered for the normal glyph */
1280                 if( g == 2 && ch->i_line_thickness > 0 )
1281                     BlendAXYZLine( p_picture,
1282                                    i_glyph_x, i_glyph_y + p_glyph->top,
1283                                    i_a, i_x, i_y, i_z,
1284                                    &ch[0],
1285                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
1286                                    BlendPixel );
1287             }
1288         }
1289     }
1290
1291     return VLC_SUCCESS;
1292 }
1293
1294
1295
1296 static void FreeLine( line_desc_t *p_line )
1297 {
1298     for( int i = 0; i < p_line->i_character_count; i++ )
1299     {
1300         line_character_t *ch = &p_line->p_character[i];
1301         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1302         if( ch->p_outline )
1303             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1304         if( ch->p_shadow )
1305             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1306     }
1307
1308     free( p_line->p_character );
1309     free( p_line );
1310 }
1311
1312 static void FreeLines( line_desc_t *p_lines )
1313 {
1314     for( line_desc_t *p_line = p_lines; p_line != NULL; )
1315     {
1316         line_desc_t *p_next = p_line->p_next;
1317         FreeLine( p_line );
1318         p_line = p_next;
1319     }
1320 }
1321
1322 static line_desc_t *NewLine( int i_count )
1323 {
1324     line_desc_t *p_line = malloc( sizeof(*p_line) );
1325
1326     if( !p_line )
1327         return NULL;
1328
1329     p_line->p_next = NULL;
1330     p_line->i_width = 0;
1331     p_line->i_base_line = 0;
1332     p_line->i_character_count = 0;
1333
1334     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1335     if( !p_line->p_character )
1336     {
1337         free( p_line );
1338         return NULL;
1339     }
1340     return p_line;
1341 }
1342
1343 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1344 {
1345     for( int k = 0; k < p_sys->i_font_attachments; k++ )
1346     {
1347         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
1348         int                 i_font_idx = 0;
1349         FT_Face             p_face = NULL;
1350
1351         while( 0 == FT_New_Memory_Face( p_sys->p_library,
1352                                         p_attach->p_data,
1353                                         p_attach->i_data,
1354                                         i_font_idx,
1355                                         &p_face ))
1356         {
1357             if( p_face )
1358             {
1359                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
1360                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1361                 if( p_face->family_name != NULL
1362                  && !strcasecmp( p_face->family_name, p_style->psz_fontname )
1363                  && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
1364                                                           == i_style_received )
1365                     return p_face;
1366
1367                 FT_Done_Face( p_face );
1368             }
1369             i_font_idx++;
1370         }
1371     }
1372     return NULL;
1373 }
1374
1375 static FT_Face LoadFace( filter_t *p_filter,
1376                          const text_style_t *p_style )
1377 {
1378     filter_sys_t *p_sys = p_filter->p_sys;
1379
1380     /* Look for a match amongst our attachments first */
1381     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1382
1383     /* Load system wide font otheriwse */
1384     if( !p_face )
1385     {
1386         int  i_idx = 0;
1387         char *psz_fontfile = NULL;
1388 #ifdef HAVE_FONTCONFIG
1389         psz_fontfile = FontConfig_Select( NULL,
1390                                           p_style->psz_fontname,
1391                                           (p_style->i_style_flags & STYLE_BOLD) != 0,
1392                                           (p_style->i_style_flags & STYLE_ITALIC) != 0,
1393                                           -1,
1394                                           &i_idx );
1395 #elif defined( __APPLE__ )
1396 #if !TARGET_OS_IPHONE
1397         psz_fontfile = MacLegacy_Select( p_filter, p_style->psz_fontname, false, false, -1, &i_idx );
1398 #endif
1399 #elif defined( _WIN32 )
1400         psz_fontfile = Win32_Select( p_filter,
1401                                     p_style->psz_fontname,
1402                                     (p_style->i_style_flags & STYLE_BOLD) != 0,
1403                                     (p_style->i_style_flags & STYLE_ITALIC) != 0,
1404                                     -1,
1405                                     &i_idx );
1406 #else
1407         psz_fontfile = NULL;
1408 #endif
1409         if( !psz_fontfile )
1410             return NULL;
1411
1412         if( *psz_fontfile == '\0' )
1413         {
1414             msg_Warn( p_filter,
1415                       "We were not able to find a matching font: \"%s\" (%s %s),"
1416                       " so using default font",
1417                       p_style->psz_fontname,
1418                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
1419                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1420             p_face = NULL;
1421         }
1422         else
1423         {
1424             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1425                 p_face = NULL;
1426         }
1427         free( psz_fontfile );
1428     }
1429     if( !p_face )
1430         return NULL;
1431
1432     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1433     {
1434         /* We've loaded a font face which is unhelpful for actually
1435          * rendering text - fallback to the default one.
1436          */
1437         FT_Done_Face( p_face );
1438         return NULL;
1439     }
1440     return p_face;
1441 }
1442
1443 static int GetGlyph( filter_t *p_filter,
1444                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
1445                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1446                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
1447
1448                      FT_Face  p_face,
1449                      int i_glyph_index,
1450                      int i_style_flags,
1451                      FT_Vector *p_pen,
1452                      FT_Vector *p_pen_shadow )
1453 {
1454     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1455         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1456     {
1457         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1458         return VLC_EGENERIC;
1459     }
1460
1461     /* Do synthetic styling now that Freetype supports it;
1462      * ie. if the font we have loaded is NOT already in the
1463      * style that the tags want, then switch it on; if they
1464      * are then don't. */
1465     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1466         FT_GlyphSlot_Embolden( p_face->glyph );
1467     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1468         FT_GlyphSlot_Oblique( p_face->glyph );
1469
1470     FT_Glyph glyph;
1471     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1472     {
1473         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1474         return VLC_EGENERIC;
1475     }
1476
1477     FT_Glyph outline = NULL;
1478     if( p_filter->p_sys->p_stroker )
1479     {
1480         outline = glyph;
1481         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1482             outline = NULL;
1483     }
1484
1485     FT_Glyph shadow = NULL;
1486     if( p_filter->p_sys->style.i_shadow_alpha > 0 )
1487     {
1488         shadow = outline ? outline : glyph;
1489         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
1490         {
1491             shadow = NULL;
1492         }
1493         else
1494         {
1495             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1496         }
1497     }
1498     *pp_shadow = shadow;
1499
1500     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1501     {
1502         FT_Done_Glyph( glyph );
1503         if( outline )
1504             FT_Done_Glyph( outline );
1505         if( shadow )
1506             FT_Done_Glyph( shadow );
1507         return VLC_EGENERIC;
1508     }
1509     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1510     *pp_glyph = glyph;
1511
1512     if( outline )
1513     {
1514         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1515         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1516     }
1517     *pp_outline = outline;
1518
1519     return VLC_SUCCESS;
1520 }
1521
1522 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1523 {
1524     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1525     if( p_bbox->xMin >= p_bbox->xMax )
1526     {
1527         p_bbox->xMin = FT_CEIL(p_pen->x);
1528         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1529         glyph_bmp->left = p_bbox->xMin;
1530     }
1531     if( p_bbox->yMin >= p_bbox->yMax )
1532     {
1533         p_bbox->yMax = FT_CEIL(p_pen->y);
1534         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1535         glyph_bmp->top  = p_bbox->yMax;
1536     }
1537 }
1538
1539 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1540 {
1541     p_max->xMin = __MIN(p_max->xMin, p->xMin);
1542     p_max->yMin = __MIN(p_max->yMin, p->yMin);
1543     p_max->xMax = __MAX(p_max->xMax, p->xMax);
1544     p_max->yMax = __MAX(p_max->yMax, p->yMax);
1545 }
1546
1547 static int ProcessLines( filter_t *p_filter,
1548                          line_desc_t **pp_lines,
1549                          FT_BBox     *p_bbox,
1550                          int         *pi_max_face_height,
1551
1552                          uni_char_t *psz_text,
1553                          text_style_t **pp_styles,
1554                          uint32_t *pi_k_dates,
1555                          int i_len )
1556 {
1557     filter_sys_t   *p_sys = p_filter->p_sys;
1558     uni_char_t     *p_fribidi_string = NULL;
1559     text_style_t   **pp_fribidi_styles = NULL;
1560     int            *p_new_positions = NULL;
1561
1562 #if defined(HAVE_FRIBIDI)
1563     {
1564         int    *p_old_positions;
1565         int start_pos, pos = 0;
1566
1567         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1568
1569         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1570         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1571         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1572
1573         if( ! pp_fribidi_styles ||
1574             ! p_fribidi_string ||
1575             ! p_old_positions ||
1576             ! p_new_positions )
1577         {
1578             free( p_old_positions );
1579             free( p_new_positions );
1580             free( p_fribidi_string );
1581             free( pp_fribidi_styles );
1582             return VLC_ENOMEM;
1583         }
1584
1585         /* Do bidi conversion line-by-line */
1586         while(pos < i_len)
1587         {
1588             while(pos < i_len) {
1589                 if (psz_text[pos] != '\n')
1590                     break;
1591                 p_fribidi_string[pos] = psz_text[pos];
1592                 pp_fribidi_styles[pos] = pp_styles[pos];
1593                 p_new_positions[pos] = pos;
1594                 ++pos;
1595             }
1596             start_pos = pos;
1597             while(pos < i_len) {
1598                 if (psz_text[pos] == '\n')
1599                     break;
1600                 ++pos;
1601             }
1602             if (pos > start_pos)
1603             {
1604 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1605                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1606 #else
1607                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1608 #endif
1609                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1610                         pos - start_pos, &base_dir,
1611                         (FriBidiChar*)p_fribidi_string + start_pos,
1612                         p_new_positions + start_pos,
1613                         p_old_positions,
1614                         NULL );
1615                 for( int j = start_pos; j < pos; j++ )
1616                 {
1617                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1618                     p_new_positions[ j ] += start_pos;
1619                 }
1620             }
1621         }
1622         p_fribidi_string[ i_len ] = 0;
1623         free( p_old_positions );
1624
1625         pp_styles = pp_fribidi_styles;
1626         psz_text = p_fribidi_string;
1627     }
1628 #endif
1629     /* Work out the karaoke */
1630     uint8_t *pi_karaoke_bar = NULL;
1631     if( pi_k_dates )
1632     {
1633         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1634         if( pi_karaoke_bar )
1635         {
1636             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1637             for( int i = 0; i < i_len; i++ )
1638             {
1639                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1640                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1641             }
1642         }
1643     }
1644     free( p_new_positions );
1645
1646     *pi_max_face_height = 0;
1647     *pp_lines = NULL;
1648     line_desc_t **pp_line_next = pp_lines;
1649
1650     FT_BBox bbox = {
1651         .xMin = INT_MAX,
1652         .yMin = INT_MAX,
1653         .xMax = INT_MIN,
1654         .yMax = INT_MIN,
1655     };
1656     int i_face_height_previous = 0;
1657     int i_base_line = 0;
1658     const text_style_t *p_previous_style = NULL;
1659     FT_Face p_face = NULL;
1660     for( int i_start = 0; i_start < i_len; )
1661     {
1662         /* Compute the length of the current text line */
1663         int i_length = 0;
1664         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1665             i_length++;
1666
1667         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1668         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1669         int i_index = i_start;
1670         FT_Vector pen = {
1671             .x = 0,
1672             .y = 0,
1673         };
1674         int i_face_height = 0;
1675         FT_BBox line_bbox = {
1676             .xMin = INT_MAX,
1677             .yMin = INT_MAX,
1678             .xMax = INT_MIN,
1679             .yMax = INT_MIN,
1680         };
1681         int i_ul_offset = 0;
1682         int i_ul_thickness = 0;
1683         typedef struct {
1684             int       i_index;
1685             FT_Vector pen;
1686             FT_BBox   line_bbox;
1687             int i_face_height;
1688             int i_ul_offset;
1689             int i_ul_thickness;
1690         } break_point_t;
1691         break_point_t break_point;
1692         break_point_t break_point_fallback;
1693
1694 #define SAVE_BP(dst) do { \
1695         dst.i_index = i_index; \
1696         dst.pen = pen; \
1697         dst.line_bbox = line_bbox; \
1698         dst.i_face_height = i_face_height; \
1699         dst.i_ul_offset = i_ul_offset; \
1700         dst.i_ul_thickness = i_ul_thickness; \
1701     } while(0)
1702
1703         SAVE_BP( break_point );
1704         SAVE_BP( break_point_fallback );
1705
1706         while( i_index < i_start + i_length )
1707         {
1708             /* Split by common FT_Face + Size */
1709             const text_style_t *p_current_style = pp_styles[i_index];
1710             int i_part_length = 0;
1711             while( i_index + i_part_length < i_start + i_length )
1712             {
1713                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1714                 if( !FaceStyleEquals( p_style, p_current_style ) ||
1715                     p_style->i_font_size != p_current_style->i_font_size )
1716                     break;
1717                 i_part_length++;
1718             }
1719
1720             /* (Re)load/reconfigure the face if needed */
1721             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
1722             {
1723                 if( p_face )
1724                     FT_Done_Face( p_face );
1725                 p_previous_style = NULL;
1726
1727                 p_face = LoadFace( p_filter, p_current_style );
1728             }
1729             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
1730             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
1731             {
1732                 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
1733                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
1734                 if( p_sys->p_stroker )
1735                 {
1736                     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1737                     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1738                     int i_radius = (p_current_style->i_font_size << 6) * f_outline_thickness;
1739                     FT_Stroker_Set( p_sys->p_stroker,
1740                                     i_radius,
1741                                     FT_STROKER_LINECAP_ROUND,
1742                                     FT_STROKER_LINEJOIN_ROUND, 0 );
1743                 }
1744             }
1745             p_previous_style = p_current_style;
1746
1747             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
1748                                                                    p_current_face->size->metrics.y_scale)));
1749
1750             /* Render the part */
1751             bool b_break_line = false;
1752             int i_glyph_last = 0;
1753             while( i_part_length > 0 )
1754             {
1755                 const text_style_t *p_glyph_style = pp_styles[i_index];
1756                 uni_char_t character = psz_text[i_index];
1757                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
1758
1759                 /* Get kerning vector */
1760                 FT_Vector kerning = { .x = 0, .y = 0 };
1761                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
1762                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
1763
1764                 /* Get the glyph bitmap and its bounding box and all the associated properties */
1765                 FT_Vector pen_new = {
1766                     .x = pen.x + kerning.x,
1767                     .y = pen.y + kerning.y,
1768                 };
1769                 FT_Vector pen_shadow_new = {
1770                     .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
1771                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
1772                 };
1773                 FT_Glyph glyph;
1774                 FT_BBox  glyph_bbox;
1775                 FT_Glyph outline;
1776                 FT_BBox  outline_bbox;
1777                 FT_Glyph shadow;
1778                 FT_BBox  shadow_bbox;
1779
1780                 if( GetGlyph( p_filter,
1781                               &glyph, &glyph_bbox,
1782                               &outline, &outline_bbox,
1783                               &shadow, &shadow_bbox,
1784                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
1785                               &pen_new, &pen_shadow_new ) )
1786                     goto next;
1787
1788                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
1789                 if( outline )
1790                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
1791                 if( shadow )
1792                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
1793
1794                 /* FIXME and what about outline */
1795
1796                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
1797                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
1798                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
1799                                              : (p_glyph_style->i_font_color |
1800                                                 (p_glyph_style->i_font_alpha << 24));
1801                 int i_line_offset    = 0;
1802                 int i_line_thickness = 0;
1803                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1804                 {
1805                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
1806                                                             p_current_face->size->metrics.y_scale)) );
1807
1808                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
1809                                                               p_current_face->size->metrics.y_scale)) );
1810
1811                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
1812                     {
1813                         /* Move the baseline to make it strikethrough instead of
1814                          * underline. That means that strikethrough takes precedence
1815                          */
1816                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
1817                                                                  p_current_face->size->metrics.y_scale)) );
1818                     }
1819                     else if( i_line_thickness > 0 )
1820                     {
1821                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
1822
1823                         /* The real underline thickness and position are
1824                          * updated once the whole line has been parsed */
1825                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
1826                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
1827                         i_line_thickness = -1;
1828                     }
1829                 }
1830                 FT_BBox line_bbox_new = line_bbox;
1831                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
1832                 if( outline )
1833                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
1834                 if( shadow )
1835                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
1836
1837                 b_break_line = i_index > i_start &&
1838                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
1839                 if( b_break_line )
1840                 {
1841                     FT_Done_Glyph( glyph );
1842                     if( outline )
1843                         FT_Done_Glyph( outline );
1844                     if( shadow )
1845                         FT_Done_Glyph( shadow );
1846
1847                     break_point_t *p_bp = NULL;
1848                     if( break_point.i_index > i_start )
1849                         p_bp = &break_point;
1850                     else if( break_point_fallback.i_index > i_start )
1851                         p_bp = &break_point_fallback;
1852
1853                     if( p_bp )
1854                     {
1855                         msg_Dbg( p_filter, "Breaking line");
1856                         for( int i = p_bp->i_index; i < i_index; i++ )
1857                         {
1858                             line_character_t *ch = &p_line->p_character[i - i_start];
1859                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1860                             if( ch->p_outline )
1861                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1862                             if( ch->p_shadow )
1863                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1864                         }
1865                         p_line->i_character_count = p_bp->i_index - i_start;
1866
1867                         i_index = p_bp->i_index;
1868                         pen = p_bp->pen;
1869                         line_bbox = p_bp->line_bbox;
1870                         i_face_height = p_bp->i_face_height;
1871                         i_ul_offset = p_bp->i_ul_offset;
1872                         i_ul_thickness = p_bp->i_ul_thickness;
1873                     }
1874                     else
1875                     {
1876                         msg_Err( p_filter, "Breaking unbreakable line");
1877                     }
1878                     break;
1879                 }
1880
1881                 assert( p_line->i_character_count == i_index - i_start);
1882                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
1883                     .p_glyph = (FT_BitmapGlyph)glyph,
1884                     .p_outline = (FT_BitmapGlyph)outline,
1885                     .p_shadow = (FT_BitmapGlyph)shadow,
1886                     .i_color = i_color,
1887                     .i_line_offset = i_line_offset,
1888                     .i_line_thickness = i_line_thickness,
1889                 };
1890
1891                 pen.x = pen_new.x + p_current_face->glyph->advance.x;
1892                 pen.y = pen_new.y + p_current_face->glyph->advance.y;
1893                 line_bbox = line_bbox_new;
1894             next:
1895                 i_glyph_last = i_glyph_index;
1896                 i_part_length--;
1897                 i_index++;
1898
1899                 if( character == ' ' || character == '\t' )
1900                     SAVE_BP( break_point );
1901                 else if( character == 160 )
1902                     SAVE_BP( break_point_fallback );
1903             }
1904             if( b_break_line )
1905                 break;
1906         }
1907 #undef SAVE_BP
1908         /* Update our baseline */
1909         if( i_face_height_previous > 0 )
1910             i_base_line += __MAX(i_face_height, i_face_height_previous);
1911         if( i_face_height > 0 )
1912             i_face_height_previous = i_face_height;
1913
1914         /* Update the line bbox with the actual base line */
1915         if (line_bbox.yMax > line_bbox.yMin) {
1916             line_bbox.yMin -= i_base_line;
1917             line_bbox.yMax -= i_base_line;
1918         }
1919         BBoxEnlarge( &bbox, &line_bbox );
1920
1921         /* Terminate and append the line */
1922         if( p_line )
1923         {
1924             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
1925             p_line->i_base_line = i_base_line;
1926             p_line->i_height = __MAX(i_face_height, i_face_height_previous);
1927             if( i_ul_thickness > 0 )
1928             {
1929                 for( int i = 0; i < p_line->i_character_count; i++ )
1930                 {
1931                     line_character_t *ch = &p_line->p_character[i];
1932                     if( ch->i_line_thickness < 0 )
1933                     {
1934                         ch->i_line_offset    = i_ul_offset;
1935                         ch->i_line_thickness = i_ul_thickness;
1936                     }
1937                 }
1938             }
1939
1940             *pp_line_next = p_line;
1941             pp_line_next = &p_line->p_next;
1942         }
1943
1944         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
1945
1946         /* Skip what we have rendered and the line delimitor if present */
1947         i_start = i_index;
1948         if( i_start < i_len && psz_text[i_start] == '\n' )
1949             i_start++;
1950
1951         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
1952         {
1953             msg_Err( p_filter, "Truncated too high subtitle" );
1954             break;
1955         }
1956     }
1957     if( p_face )
1958         FT_Done_Face( p_face );
1959
1960     free( pp_fribidi_styles );
1961     free( p_fribidi_string );
1962     free( pi_karaoke_bar );
1963
1964     *p_bbox = bbox;
1965     return VLC_SUCCESS;
1966 }
1967
1968 /**
1969  * This function renders a text subpicture region into another one.
1970  * It also calculates the size needed for this string, and renders the
1971  * needed glyphs into memory. It is used as pf_add_string callback in
1972  * the vout method by this module
1973  */
1974 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
1975                          subpicture_region_t *p_region_in, bool b_html,
1976                          const vlc_fourcc_t *p_chroma_list )
1977 {
1978     filter_sys_t *p_sys = p_filter->p_sys;
1979
1980     if( !p_region_in )
1981         return VLC_EGENERIC;
1982     if( b_html && !p_region_in->psz_html )
1983         return VLC_EGENERIC;
1984     if( !b_html && !p_region_in->psz_text )
1985         return VLC_EGENERIC;
1986
1987     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
1988                                              : p_region_in->psz_text );
1989
1990     uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
1991     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
1992     if( !psz_text || !pp_styles )
1993     {
1994         free( psz_text );
1995         free( pp_styles );
1996         return VLC_EGENERIC;
1997     }
1998
1999     /* Reset the default fontsize in case screen metrics have changed */
2000     p_filter->p_sys->style.i_font_size = GetFontSize( p_filter );
2001
2002     /* */
2003     int rv = VLC_SUCCESS;
2004     int i_text_length = 0;
2005     FT_BBox bbox;
2006     int i_max_face_height;
2007     line_desc_t *p_lines = NULL;
2008
2009     uint32_t *pi_k_durations   = NULL;
2010
2011     if( b_html )
2012     {
2013         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2014                                             (uint8_t *) p_region_in->psz_html,
2015                                             strlen( p_region_in->psz_html ),
2016                                             true );
2017         if( unlikely(p_sub == NULL) )
2018             return VLC_SUCCESS;
2019
2020         xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2021         if( !p_xml_reader )
2022             p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2023         else
2024             p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2025         p_filter->p_sys->p_xml = p_xml_reader;
2026
2027         if( !p_xml_reader )
2028             rv = VLC_EGENERIC;
2029
2030         if( !rv )
2031         {
2032             /* Look for Root Node */
2033             const char *node;
2034
2035             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2036             {
2037                 if( strcasecmp( "karaoke", node ) == 0 )
2038                 {
2039                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2040                 }
2041                 else if( strcasecmp( "text", node ) != 0 )
2042                 {
2043                     /* Only text and karaoke tags are supported */
2044                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2045                              node );
2046                     rv = VLC_EGENERIC;
2047                 }
2048             }
2049             else
2050             {
2051                 msg_Err( p_filter, "Malformed HTML subtitle" );
2052                 rv = VLC_EGENERIC;
2053             }
2054         }
2055         if( !rv )
2056         {
2057             rv = ProcessNodes( p_filter,
2058                                psz_text, pp_styles, pi_k_durations, &i_text_length,
2059                                p_xml_reader, p_region_in->p_style, &p_filter->p_sys->style );
2060         }
2061
2062         if( p_xml_reader )
2063             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2064
2065         stream_Delete( p_sub );
2066     }
2067     else
2068     {
2069         text_style_t *p_style;
2070         if( p_region_in->p_style )
2071             p_style = CreateStyle( p_region_in->p_style->psz_fontname ? p_region_in->p_style->psz_fontname
2072                                                                       : p_sys->style.psz_fontname,
2073                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2074                                                                          : p_sys->style.i_font_size,
2075                                    (p_region_in->p_style->i_font_color & 0xffffff) |
2076                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2077                                    0x00ffffff,
2078                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2079                                                                           STYLE_ITALIC |
2080                                                                           STYLE_UNDERLINE |
2081                                                                           STYLE_STRIKEOUT) );
2082         else
2083         {
2084             uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2085             i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
2086             p_style = CreateStyle( p_sys->style.psz_fontname,
2087                                    p_sys->style.i_font_size,
2088                                    (i_font_color & 0xffffff) |
2089                                    ((p_sys->style.i_font_alpha & 0xff) << 24),
2090                                    0x00ffffff, 0);
2091         }
2092         if( p_sys->style.i_style_flags & STYLE_BOLD )
2093             p_style->i_style_flags |= STYLE_BOLD;
2094
2095         i_text_length = SetupText( p_filter,
2096                                    psz_text,
2097                                    pp_styles,
2098                                    NULL,
2099                                    p_region_in->psz_text, p_style, 0 );
2100     }
2101
2102     if( !rv && i_text_length > 0 )
2103     {
2104         rv = ProcessLines( p_filter,
2105                            &p_lines, &bbox, &i_max_face_height,
2106                            psz_text, pp_styles, pi_k_durations, i_text_length );
2107     }
2108
2109     p_region_out->i_x = p_region_in->i_x;
2110     p_region_out->i_y = p_region_in->i_y;
2111
2112     /* Don't attempt to render text that couldn't be layed out
2113      * properly. */
2114     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2115     {
2116         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2117         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2118
2119         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2120             p_chroma_list = p_chroma_list_yuvp;
2121         else if( !p_chroma_list || *p_chroma_list == 0 )
2122             p_chroma_list = p_chroma_list_rgba;
2123
2124         uint8_t i_background_opacity = var_InheritInteger( p_filter, "freetype-background-opacity" );
2125         i_background_opacity = VLC_CLIP( i_background_opacity, 0, 255 );
2126         const int i_margin = i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2127         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2128         {
2129             rv = VLC_EGENERIC;
2130             if( *p_chroma == VLC_CODEC_YUVP )
2131                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2132             else if( *p_chroma == VLC_CODEC_YUVA )
2133                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2134                                  VLC_CODEC_YUVA,
2135                                  YUVFromRGB,
2136                                  FillYUVAPicture,
2137                                  BlendYUVAPixel );
2138             else if( *p_chroma == VLC_CODEC_RGBA )
2139                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2140                                  VLC_CODEC_RGBA,
2141                                  RGBFromRGB,
2142                                  FillRGBAPicture,
2143                                  BlendRGBAPixel );
2144             else if( *p_chroma == VLC_CODEC_ARGB )
2145                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox,
2146                                  i_margin, *p_chroma, RGBFromRGB,
2147                                  FillARGBPicture, BlendARGBPixel );
2148
2149             if( !rv )
2150                 break;
2151         }
2152
2153         /* With karaoke, we're going to have to render the text a number
2154          * of times to show the progress marker on the text.
2155          */
2156         if( pi_k_durations )
2157             var_SetBool( p_filter, "text-rerender", true );
2158     }
2159
2160     FreeLines( p_lines );
2161
2162     free( psz_text );
2163     for( int i = 0; i < i_text_length; i++ )
2164     {
2165         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2166             text_style_Delete( pp_styles[i] );
2167     }
2168     free( pp_styles );
2169     free( pi_k_durations );
2170
2171     return rv;
2172 }
2173
2174 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2175                        subpicture_region_t *p_region_in,
2176                        const vlc_fourcc_t *p_chroma_list )
2177 {
2178     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2179 }
2180
2181 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2182                        subpicture_region_t *p_region_in,
2183                        const vlc_fourcc_t *p_chroma_list )
2184 {
2185     return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2186 }
2187
2188 /*****************************************************************************
2189  * Create: allocates osd-text video thread output method
2190  *****************************************************************************
2191  * This function allocates and initializes a Clone vout method.
2192  *****************************************************************************/
2193 static int Create( vlc_object_t *p_this )
2194 {
2195     filter_t      *p_filter = (filter_t *)p_this;
2196     filter_sys_t  *p_sys;
2197     char          *psz_fontfile   = NULL;
2198     char          *psz_fontname = NULL;
2199     char          *psz_monofontfile   = NULL;
2200     char          *psz_monofontfamily = NULL;
2201     int            i_error = 0, fontindex = 0, monofontindex = 0;
2202
2203     /* Allocate structure */
2204     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2205     if( !p_sys )
2206         return VLC_ENOMEM;
2207
2208     p_sys->style.psz_fontname   = NULL;
2209     p_sys->p_xml            = NULL;
2210     p_sys->p_face           = 0;
2211     p_sys->p_library        = 0;
2212     p_sys->style.i_font_size      = 0;
2213
2214     /*
2215      * The following variables should not be cached, as they might be changed on-the-fly:
2216      * freetype-rel-fontsize, freetype-background-opacity, freetype-background-color,
2217      * freetype-outline-thickness, freetype-color
2218      *
2219      */
2220
2221     psz_fontname = var_InheritString( p_filter, "freetype-font" );
2222     psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
2223     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2224     p_sys->style.i_font_alpha = var_InheritInteger( p_filter,"freetype-opacity" );
2225     p_sys->style.i_font_alpha = VLC_CLIP( p_sys->style.i_font_alpha, 0, 255 );
2226     if( var_InheritBool( p_filter, "freetype-bold" ) )
2227         p_sys->style.i_style_flags |= STYLE_BOLD;
2228
2229     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2230     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
2231     p_sys->style.i_outline_alpha = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2232     p_sys->style.i_outline_alpha = VLC_CLIP( p_sys->style.i_outline_alpha, 0, 255 );
2233     p_sys->style.i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2234     p_sys->style.i_outline_color = VLC_CLIP( p_sys->style.i_outline_color, 0, 0xFFFFFF );
2235
2236     p_sys->style.i_shadow_alpha = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2237     p_sys->style.i_shadow_alpha = VLC_CLIP( p_sys->style.i_shadow_alpha, 0, 255 );
2238     p_sys->style.i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2239     p_sys->style.i_shadow_color = VLC_CLIP( p_sys->style.i_shadow_color, 0, 0xFFFFFF );
2240     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2241     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2242     f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
2243     p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2244     p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2245
2246 #ifdef _WIN32
2247     /* Get Windows Font folder */
2248     wchar_t wdir[MAX_PATH];
2249     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2250     {
2251         GetWindowsDirectoryW( wdir, MAX_PATH );
2252         wcscat( wdir, L"\\fonts" );
2253     }
2254     p_sys->psz_win_fonts_path = FromWide( wdir );
2255 #endif
2256
2257     /* Set default psz_fontname */
2258     if( !psz_fontname || !*psz_fontname )
2259     {
2260         free( psz_fontname );
2261 #ifdef HAVE_STYLES
2262         psz_fontname = strdup( DEFAULT_FAMILY );
2263 #else
2264 # ifdef _WIN32
2265         if( asprintf( &psz_fontname, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2266         {
2267             psz_fontname = NULL;
2268             goto error;
2269         }
2270 # else
2271         psz_fontname = strdup( DEFAULT_FONT_FILE );
2272 # endif
2273         msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontname );
2274 #endif
2275     }
2276
2277     /* Set the current font file */
2278     p_sys->style.psz_fontname = psz_fontname;
2279 #ifdef HAVE_STYLES
2280 #ifdef HAVE_FONTCONFIG
2281     FontConfig_BuildCache( p_filter );
2282
2283     /* */
2284     psz_fontfile = FontConfig_Select( NULL, psz_fontname, false, false,
2285                                       p_sys->i_default_font_size, &fontindex );
2286     psz_monofontfile = FontConfig_Select( NULL, psz_monofontfamily, false,
2287                                           false, p_sys->i_default_font_size,
2288                                           &monofontindex );
2289 #elif defined(__APPLE__)
2290 #if !TARGET_OS_IPHONE
2291     psz_fontfile = MacLegacy_Select( p_filter, psz_fontname, false, false, -1, &fontindex );
2292 #endif
2293 #elif defined(_WIN32)
2294     psz_fontfile = Win32_Select( p_filter, psz_fontname, false, false, -1, &fontindex );
2295
2296 #endif
2297     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontname, psz_fontfile );
2298
2299     /* If nothing is found, use the default family */
2300     if( !psz_fontfile )
2301         psz_fontfile = strdup( psz_fontname );
2302     if( !psz_monofontfile )
2303         psz_monofontfile = strdup( psz_monofontfamily );
2304
2305 #else /* !HAVE_STYLES */
2306     /* Use the default file */
2307     psz_fontfile = psz_fontname;
2308     psz_monofontfile = psz_monofontfamily;
2309 #endif
2310     p_sys->style.psz_monofontname = psz_monofontfamily;
2311
2312     /* */
2313     i_error = FT_Init_FreeType( &p_sys->p_library );
2314     if( i_error )
2315     {
2316         msg_Err( p_filter, "couldn't initialize freetype" );
2317         goto error;
2318     }
2319
2320     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2321                            fontindex, &p_sys->p_face );
2322
2323     if( i_error == FT_Err_Unknown_File_Format )
2324     {
2325         msg_Err( p_filter, "file %s have unknown format",
2326                  psz_fontfile ? psz_fontfile : "(null)" );
2327         goto error;
2328     }
2329     else if( i_error )
2330     {
2331         msg_Err( p_filter, "failed to load font file %s",
2332                  psz_fontfile ? psz_fontfile : "(null)" );
2333         goto error;
2334     }
2335
2336     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2337     if( i_error )
2338     {
2339         msg_Err( p_filter, "font has no unicode translation table" );
2340         goto error;
2341     }
2342
2343     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2344
2345     p_sys->p_stroker = NULL;
2346     if( f_outline_thickness > 0.001 )
2347     {
2348         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2349         if( i_error )
2350             msg_Err( p_filter, "Failed to create stroker for outlining" );
2351     }
2352
2353     p_sys->pp_font_attachments = NULL;
2354     p_sys->i_font_attachments = 0;
2355
2356     p_filter->pf_render_text = RenderText;
2357     p_filter->pf_render_html = RenderHtml;
2358
2359     LoadFontsFromAttachments( p_filter );
2360
2361 #ifdef HAVE_STYLES
2362     free( psz_fontfile );
2363     free( psz_monofontfile );
2364 #endif
2365
2366     return VLC_SUCCESS;
2367
2368 error:
2369     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2370     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2371 #ifdef HAVE_STYLES
2372     free( psz_fontfile );
2373     free( psz_monofontfile );
2374 #endif
2375     free( psz_fontname );
2376     free( psz_monofontfamily );
2377     free( p_sys );
2378     return VLC_EGENERIC;
2379 }
2380
2381 /*****************************************************************************
2382  * Destroy: destroy Clone video thread output method
2383  *****************************************************************************
2384  * Clean up all data and library connections
2385  *****************************************************************************/
2386 static void Destroy( vlc_object_t *p_this )
2387 {
2388     filter_t *p_filter = (filter_t *)p_this;
2389     filter_sys_t *p_sys = p_filter->p_sys;
2390
2391     if( p_sys->pp_font_attachments )
2392     {
2393         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2394             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2395
2396         free( p_sys->pp_font_attachments );
2397     }
2398
2399     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2400     free( p_sys->style.psz_fontname );
2401     free( p_sys->style.psz_monofontname );
2402
2403 #ifdef _WIN32
2404     free( p_sys->psz_win_fonts_path );
2405 #endif
2406
2407     /* FcFini asserts calling the subfunction FcCacheFini()
2408      * even if no other library functions have been made since FcInit(),
2409      * so don't call it. */
2410
2411     if( p_sys->p_stroker )
2412         FT_Stroker_Done( p_sys->p_stroker );
2413     FT_Done_Face( p_sys->p_face );
2414     FT_Done_FreeType( p_sys->p_library );
2415     free( p_sys );
2416 }