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