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