]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
Switcher: avoid use of unset variable
[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_("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_integer( "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, "", false )
196         change_safe()
197
198     add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
199                             BG_OPACITY_TEXT, "", false )
200         change_safe()
201     add_integer( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
202                  "", 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, "", false )
208         change_safe()
209     add_integer( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
210                  "", 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                  "", 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, "", false )
220         change_safe()
221     add_integer( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
222                  "", 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, "", false )
227         change_safe()
228     add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
229                           SHADOW_DISTANCE_TEXT, "", 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     char *psz_tmp;
620     if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
621         return NULL;
622     return psz_tmp;
623 }
624 #endif
625
626 #endif
627
628
629 /*****************************************************************************
630  * RenderYUVP: place string in picture
631  *****************************************************************************
632  * This function merges the previously rendered freetype glyphs into a picture
633  *****************************************************************************/
634 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
635                        line_desc_t *p_line,
636                        FT_BBox *p_bbox )
637 {
638     VLC_UNUSED(p_filter);
639     static const uint8_t pi_gamma[16] =
640         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
641           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
642
643     uint8_t *p_dst;
644     video_format_t fmt;
645     int i, x, y, i_pitch;
646     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
647
648     /* Create a new subpicture region */
649     video_format_Init( &fmt, VLC_CODEC_YUVP );
650     fmt.i_width          =
651     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
652     fmt.i_height         =
653     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
654
655     assert( !p_region->p_picture );
656     p_region->p_picture = picture_NewFromFormat( &fmt );
657     if( !p_region->p_picture )
658         return VLC_EGENERIC;
659     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
660     p_region->fmt = fmt;
661
662     /* Calculate text color components
663      * Only use the first color */
664     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
665     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
666
667     /* Build palette */
668     fmt.p_palette->i_entries = 16;
669     for( i = 0; i < 8; i++ )
670     {
671         fmt.p_palette->palette[i][0] = 0;
672         fmt.p_palette->palette[i][1] = 0x80;
673         fmt.p_palette->palette[i][2] = 0x80;
674         fmt.p_palette->palette[i][3] = pi_gamma[i];
675         fmt.p_palette->palette[i][3] =
676             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
677     }
678     for( i = 8; i < fmt.p_palette->i_entries; i++ )
679     {
680         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
681         fmt.p_palette->palette[i][1] = i_u;
682         fmt.p_palette->palette[i][2] = i_v;
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
688     p_dst = p_region->p_picture->Y_PIXELS;
689     i_pitch = p_region->p_picture->Y_PITCH;
690
691     /* Initialize the region pixels */
692     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
693
694     for( ; p_line != NULL; p_line = p_line->p_next )
695     {
696         int i_align_left = 0;
697         if( p_line->i_width < (int)fmt.i_visible_width )
698         {
699             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
700                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
701             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
702                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
703         }
704         int i_align_top = 0;
705
706         for( i = 0; i < p_line->i_character_count; i++ )
707         {
708             const line_character_t *ch = &p_line->p_character[i];
709             FT_BitmapGlyph p_glyph = ch->p_glyph;
710
711             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
712             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
713
714             for( y = 0; y < p_glyph->bitmap.rows; y++ )
715             {
716                 for( x = 0; x < p_glyph->bitmap.width; x++ )
717                 {
718                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
719                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
720                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
721                 }
722             }
723         }
724     }
725
726     /* Outlining (find something better than nearest neighbour filtering ?) */
727     if( 1 )
728     {
729         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
730         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
731         uint8_t left, current;
732
733         for( y = 1; y < (int)fmt.i_height - 1; y++ )
734         {
735             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
736             p_dst += p_region->p_picture->Y_PITCH;
737             left = 0;
738
739             for( x = 1; x < (int)fmt.i_width - 1; x++ )
740             {
741                 current = p_dst[x];
742                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
743                              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;
744                 left = current;
745             }
746         }
747         memset( p_top, 0, fmt.i_width );
748     }
749
750     return VLC_SUCCESS;
751 }
752
753 /*****************************************************************************
754  * RenderYUVA: place string in picture
755  *****************************************************************************
756  * This function merges the previously rendered freetype glyphs into a picture
757  *****************************************************************************/
758 static void FillYUVAPicture( picture_t *p_picture,
759                              int i_a, int i_y, int i_u, int i_v )
760 {
761     memset( p_picture->p[0].p_pixels, i_y,
762             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
763     memset( p_picture->p[1].p_pixels, i_u,
764             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
765     memset( p_picture->p[2].p_pixels, i_v,
766             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
767     memset( p_picture->p[3].p_pixels, i_a,
768             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
769 }
770
771 static inline void BlendYUVAPixel( picture_t *p_picture,
772                                    int i_picture_x, int i_picture_y,
773                                    int i_a, int i_y, int i_u, int i_v,
774                                    int i_alpha )
775 {
776     int i_an = i_a * i_alpha / 255;
777
778     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
779     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
780     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
781     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
782
783     int i_ao = *p_a;
784     if( i_ao == 0 )
785     {
786         *p_y = i_y;
787         *p_u = i_u;
788         *p_v = i_v;
789         *p_a = i_an;
790     }
791     else
792     {
793         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
794         if( *p_a != 0 )
795         {
796             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
797             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
798             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
799         }
800     }
801 }
802
803 static void FillRGBAPicture( picture_t *p_picture,
804                              int i_a, int i_r, int i_g, int i_b )
805 {
806     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
807     {
808         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
809         {
810             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
811             p_rgba[0] = i_r;
812             p_rgba[1] = i_g;
813             p_rgba[2] = i_b;
814             p_rgba[3] = i_a;
815         }
816     }
817 }
818
819 static inline void BlendRGBAPixel( picture_t *p_picture,
820                                    int i_picture_x, int i_picture_y,
821                                    int i_a, int i_r, int i_g, int i_b,
822                                    int i_alpha )
823 {
824     int i_an = i_a * i_alpha / 255;
825
826     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
827
828     int i_ao = p_rgba[3];
829     if( i_ao == 0 )
830     {
831         p_rgba[0] = i_r;
832         p_rgba[1] = i_g;
833         p_rgba[2] = i_b;
834         p_rgba[3] = i_an;
835     }
836     else
837     {
838         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
839         if( p_rgba[3] != 0 )
840         {
841             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
842             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
843             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
844         }
845     }
846 }
847
848 static inline void BlendAXYZGlyph( picture_t *p_picture,
849                                    int i_picture_x, int i_picture_y,
850                                    int i_a, int i_x, int i_y, int i_z,
851                                    FT_BitmapGlyph p_glyph,
852                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
853
854 {
855     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
856     {
857         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
858             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
859                         i_a, i_x, i_y, i_z,
860                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
861     }
862 }
863
864 static inline void BlendAXYZLine( picture_t *p_picture,
865                                   int i_picture_x, int i_picture_y,
866                                   int i_a, int i_x, int i_y, int i_z,
867                                   const line_character_t *p_current,
868                                   const line_character_t *p_next,
869                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
870 {
871     int i_line_width = p_current->p_glyph->bitmap.width;
872     if( p_next )
873         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
874
875     for( int dx = 0; dx < i_line_width; dx++ )
876     {
877         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
878             BlendPixel( p_picture,
879                         i_picture_x + dx,
880                         i_picture_y + p_current->i_line_offset + dy,
881                         i_a, i_x, i_y, i_z, 0xff );
882     }
883 }
884
885 static inline int RenderAXYZ( filter_t *p_filter,
886                               subpicture_region_t *p_region,
887                               line_desc_t *p_line_head,
888                               FT_BBox *p_bbox,
889                               int i_margin,
890                               vlc_fourcc_t i_chroma,
891                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
892                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
893                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
894 {
895     filter_sys_t *p_sys = p_filter->p_sys;
896
897     /* Create a new subpicture region */
898     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
899     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
900     video_format_t fmt;
901     video_format_Init( &fmt, i_chroma );
902     fmt.i_width          =
903     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
904     fmt.i_height         =
905     fmt.i_visible_height = i_text_height + 2 * i_margin;
906
907     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
908     if( !p_region->p_picture )
909         return VLC_EGENERIC;
910     p_region->fmt = fmt;
911
912     /* Initialize the picture background */
913     uint8_t i_a = p_sys->i_background_opacity;
914     uint8_t i_x, i_y, i_z;
915     ExtractComponents( p_sys->i_background_color, &i_x, &i_y, &i_z );
916
917     FillPicture( p_picture, i_a, i_x, i_y, i_z );
918
919     /* Render shadow then outline and then normal glyphs */
920     for( int g = 0; g < 3; g++ )
921     {
922         /* Render all lines */
923         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
924         {
925             int i_align_left = i_margin;
926             if( p_line->i_width < i_text_width )
927             {
928                 /* Left offset to take into account alignment */
929                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
930                     i_align_left += ( i_text_width - p_line->i_width );
931                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
932                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
933             }
934             int i_align_top = i_margin;
935
936             /* Render all glyphs and underline/strikethrough */
937             for( int i = 0; i < p_line->i_character_count; i++ )
938             {
939                 const line_character_t *ch = &p_line->p_character[i];
940                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
941                 if( !p_glyph )
942                     continue;
943
944                 i_a = (ch->i_color >> 24) & 0xff;
945                 uint32_t i_color;
946                 switch (g) {
947                 case 0:
948                     i_a     = i_a * p_sys->i_shadow_opacity / 255;
949                     i_color = p_sys->i_shadow_color;
950                     break;
951                 case 1:
952                     i_a     = i_a * p_sys->i_outline_opacity / 255;
953                     i_color = p_sys->i_outline_color;
954                     break;
955                 default:
956                     i_color = ch->i_color;
957                     break;
958                 }
959                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
960
961                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
962                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
963
964                 BlendAXYZGlyph( p_picture,
965                                 i_glyph_x, i_glyph_y,
966                                 i_a, i_x, i_y, i_z,
967                                 p_glyph,
968                                 BlendPixel );
969
970                 /* underline/strikethrough are only rendered for the normal glyph */
971                 if( g == 2 && ch->i_line_thickness > 0 )
972                     BlendAXYZLine( p_picture,
973                                    i_glyph_x, i_glyph_y + p_glyph->top,
974                                    i_a, i_x, i_y, i_z,
975                                    &ch[0],
976                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
977                                    BlendPixel );
978             }
979         }
980     }
981
982     return VLC_SUCCESS;
983 }
984
985 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
986                                   uint32_t i_font_color, uint32_t i_karaoke_bg_color,
987                                   int i_style_flags )
988 {
989     text_style_t *p_style = text_style_New();
990     if( !p_style )
991         return NULL;
992
993     p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
994     p_style->i_font_size  = i_font_size;
995     p_style->i_font_color = (i_font_color & 0x00ffffff) >>  0;
996     p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
997     p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >>  0;
998     p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
999     p_style->i_style_flags |= i_style_flags;
1000     return p_style;
1001 }
1002
1003 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1004                      uint32_t i_color, uint32_t i_karaoke_bg_color )
1005 {
1006     if( !p_font )
1007         return VLC_EGENERIC;
1008
1009     font_stack_t *p_new = malloc( sizeof(*p_new) );
1010     if( !p_new )
1011         return VLC_ENOMEM;
1012
1013     p_new->p_next = NULL;
1014
1015     if( psz_name )
1016         p_new->psz_name = strdup( psz_name );
1017     else
1018         p_new->psz_name = NULL;
1019
1020     p_new->i_size              = i_size;
1021     p_new->i_color             = i_color;
1022     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
1023
1024     if( !*p_font )
1025     {
1026         *p_font = p_new;
1027     }
1028     else
1029     {
1030         font_stack_t *p_last;
1031
1032         for( p_last = *p_font;
1033              p_last->p_next;
1034              p_last = p_last->p_next )
1035         ;
1036
1037         p_last->p_next = p_new;
1038     }
1039     return VLC_SUCCESS;
1040 }
1041
1042 static int PopFont( font_stack_t **p_font )
1043 {
1044     font_stack_t *p_last, *p_next_to_last;
1045
1046     if( !p_font || !*p_font )
1047         return VLC_EGENERIC;
1048
1049     p_next_to_last = NULL;
1050     for( p_last = *p_font;
1051          p_last->p_next;
1052          p_last = p_last->p_next )
1053     {
1054         p_next_to_last = p_last;
1055     }
1056
1057     if( p_next_to_last )
1058         p_next_to_last->p_next = NULL;
1059     else
1060         *p_font = NULL;
1061
1062     free( p_last->psz_name );
1063     free( p_last );
1064
1065     return VLC_SUCCESS;
1066 }
1067
1068 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1069                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1070 {
1071     font_stack_t *p_last;
1072
1073     if( !p_font || !*p_font )
1074         return VLC_EGENERIC;
1075
1076     for( p_last=*p_font;
1077          p_last->p_next;
1078          p_last=p_last->p_next )
1079     ;
1080
1081     *psz_name            = p_last->psz_name;
1082     *i_size              = p_last->i_size;
1083     *i_color             = p_last->i_color;
1084     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
1085
1086     return VLC_SUCCESS;
1087 }
1088
1089 static const struct {
1090     const char *psz_name;
1091     uint32_t   i_value;
1092 } p_html_colors[] = {
1093     /* Official html colors */
1094     { "Aqua",    0x00FFFF },
1095     { "Black",   0x000000 },
1096     { "Blue",    0x0000FF },
1097     { "Fuchsia", 0xFF00FF },
1098     { "Gray",    0x808080 },
1099     { "Green",   0x008000 },
1100     { "Lime",    0x00FF00 },
1101     { "Maroon",  0x800000 },
1102     { "Navy",    0x000080 },
1103     { "Olive",   0x808000 },
1104     { "Purple",  0x800080 },
1105     { "Red",     0xFF0000 },
1106     { "Silver",  0xC0C0C0 },
1107     { "Teal",    0x008080 },
1108     { "White",   0xFFFFFF },
1109     { "Yellow",  0xFFFF00 },
1110
1111     /* Common ones */
1112     { "AliceBlue", 0xF0F8FF },
1113     { "AntiqueWhite", 0xFAEBD7 },
1114     { "Aqua", 0x00FFFF },
1115     { "Aquamarine", 0x7FFFD4 },
1116     { "Azure", 0xF0FFFF },
1117     { "Beige", 0xF5F5DC },
1118     { "Bisque", 0xFFE4C4 },
1119     { "Black", 0x000000 },
1120     { "BlanchedAlmond", 0xFFEBCD },
1121     { "Blue", 0x0000FF },
1122     { "BlueViolet", 0x8A2BE2 },
1123     { "Brown", 0xA52A2A },
1124     { "BurlyWood", 0xDEB887 },
1125     { "CadetBlue", 0x5F9EA0 },
1126     { "Chartreuse", 0x7FFF00 },
1127     { "Chocolate", 0xD2691E },
1128     { "Coral", 0xFF7F50 },
1129     { "CornflowerBlue", 0x6495ED },
1130     { "Cornsilk", 0xFFF8DC },
1131     { "Crimson", 0xDC143C },
1132     { "Cyan", 0x00FFFF },
1133     { "DarkBlue", 0x00008B },
1134     { "DarkCyan", 0x008B8B },
1135     { "DarkGoldenRod", 0xB8860B },
1136     { "DarkGray", 0xA9A9A9 },
1137     { "DarkGrey", 0xA9A9A9 },
1138     { "DarkGreen", 0x006400 },
1139     { "DarkKhaki", 0xBDB76B },
1140     { "DarkMagenta", 0x8B008B },
1141     { "DarkOliveGreen", 0x556B2F },
1142     { "Darkorange", 0xFF8C00 },
1143     { "DarkOrchid", 0x9932CC },
1144     { "DarkRed", 0x8B0000 },
1145     { "DarkSalmon", 0xE9967A },
1146     { "DarkSeaGreen", 0x8FBC8F },
1147     { "DarkSlateBlue", 0x483D8B },
1148     { "DarkSlateGray", 0x2F4F4F },
1149     { "DarkSlateGrey", 0x2F4F4F },
1150     { "DarkTurquoise", 0x00CED1 },
1151     { "DarkViolet", 0x9400D3 },
1152     { "DeepPink", 0xFF1493 },
1153     { "DeepSkyBlue", 0x00BFFF },
1154     { "DimGray", 0x696969 },
1155     { "DimGrey", 0x696969 },
1156     { "DodgerBlue", 0x1E90FF },
1157     { "FireBrick", 0xB22222 },
1158     { "FloralWhite", 0xFFFAF0 },
1159     { "ForestGreen", 0x228B22 },
1160     { "Fuchsia", 0xFF00FF },
1161     { "Gainsboro", 0xDCDCDC },
1162     { "GhostWhite", 0xF8F8FF },
1163     { "Gold", 0xFFD700 },
1164     { "GoldenRod", 0xDAA520 },
1165     { "Gray", 0x808080 },
1166     { "Grey", 0x808080 },
1167     { "Green", 0x008000 },
1168     { "GreenYellow", 0xADFF2F },
1169     { "HoneyDew", 0xF0FFF0 },
1170     { "HotPink", 0xFF69B4 },
1171     { "IndianRed", 0xCD5C5C },
1172     { "Indigo", 0x4B0082 },
1173     { "Ivory", 0xFFFFF0 },
1174     { "Khaki", 0xF0E68C },
1175     { "Lavender", 0xE6E6FA },
1176     { "LavenderBlush", 0xFFF0F5 },
1177     { "LawnGreen", 0x7CFC00 },
1178     { "LemonChiffon", 0xFFFACD },
1179     { "LightBlue", 0xADD8E6 },
1180     { "LightCoral", 0xF08080 },
1181     { "LightCyan", 0xE0FFFF },
1182     { "LightGoldenRodYellow", 0xFAFAD2 },
1183     { "LightGray", 0xD3D3D3 },
1184     { "LightGrey", 0xD3D3D3 },
1185     { "LightGreen", 0x90EE90 },
1186     { "LightPink", 0xFFB6C1 },
1187     { "LightSalmon", 0xFFA07A },
1188     { "LightSeaGreen", 0x20B2AA },
1189     { "LightSkyBlue", 0x87CEFA },
1190     { "LightSlateGray", 0x778899 },
1191     { "LightSlateGrey", 0x778899 },
1192     { "LightSteelBlue", 0xB0C4DE },
1193     { "LightYellow", 0xFFFFE0 },
1194     { "Lime", 0x00FF00 },
1195     { "LimeGreen", 0x32CD32 },
1196     { "Linen", 0xFAF0E6 },
1197     { "Magenta", 0xFF00FF },
1198     { "Maroon", 0x800000 },
1199     { "MediumAquaMarine", 0x66CDAA },
1200     { "MediumBlue", 0x0000CD },
1201     { "MediumOrchid", 0xBA55D3 },
1202     { "MediumPurple", 0x9370D8 },
1203     { "MediumSeaGreen", 0x3CB371 },
1204     { "MediumSlateBlue", 0x7B68EE },
1205     { "MediumSpringGreen", 0x00FA9A },
1206     { "MediumTurquoise", 0x48D1CC },
1207     { "MediumVioletRed", 0xC71585 },
1208     { "MidnightBlue", 0x191970 },
1209     { "MintCream", 0xF5FFFA },
1210     { "MistyRose", 0xFFE4E1 },
1211     { "Moccasin", 0xFFE4B5 },
1212     { "NavajoWhite", 0xFFDEAD },
1213     { "Navy", 0x000080 },
1214     { "OldLace", 0xFDF5E6 },
1215     { "Olive", 0x808000 },
1216     { "OliveDrab", 0x6B8E23 },
1217     { "Orange", 0xFFA500 },
1218     { "OrangeRed", 0xFF4500 },
1219     { "Orchid", 0xDA70D6 },
1220     { "PaleGoldenRod", 0xEEE8AA },
1221     { "PaleGreen", 0x98FB98 },
1222     { "PaleTurquoise", 0xAFEEEE },
1223     { "PaleVioletRed", 0xD87093 },
1224     { "PapayaWhip", 0xFFEFD5 },
1225     { "PeachPuff", 0xFFDAB9 },
1226     { "Peru", 0xCD853F },
1227     { "Pink", 0xFFC0CB },
1228     { "Plum", 0xDDA0DD },
1229     { "PowderBlue", 0xB0E0E6 },
1230     { "Purple", 0x800080 },
1231     { "Red", 0xFF0000 },
1232     { "RosyBrown", 0xBC8F8F },
1233     { "RoyalBlue", 0x4169E1 },
1234     { "SaddleBrown", 0x8B4513 },
1235     { "Salmon", 0xFA8072 },
1236     { "SandyBrown", 0xF4A460 },
1237     { "SeaGreen", 0x2E8B57 },
1238     { "SeaShell", 0xFFF5EE },
1239     { "Sienna", 0xA0522D },
1240     { "Silver", 0xC0C0C0 },
1241     { "SkyBlue", 0x87CEEB },
1242     { "SlateBlue", 0x6A5ACD },
1243     { "SlateGray", 0x708090 },
1244     { "SlateGrey", 0x708090 },
1245     { "Snow", 0xFFFAFA },
1246     { "SpringGreen", 0x00FF7F },
1247     { "SteelBlue", 0x4682B4 },
1248     { "Tan", 0xD2B48C },
1249     { "Teal", 0x008080 },
1250     { "Thistle", 0xD8BFD8 },
1251     { "Tomato", 0xFF6347 },
1252     { "Turquoise", 0x40E0D0 },
1253     { "Violet", 0xEE82EE },
1254     { "Wheat", 0xF5DEB3 },
1255     { "White", 0xFFFFFF },
1256     { "WhiteSmoke", 0xF5F5F5 },
1257     { "Yellow", 0xFFFF00 },
1258     { "YellowGreen", 0x9ACD32 },
1259
1260     { NULL, 0 }
1261 };
1262
1263 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1264                                  font_stack_t **p_fonts )
1265 {
1266     int        rv;
1267     char      *psz_fontname = NULL;
1268     uint32_t   i_font_color = 0xffffff;
1269     int        i_font_alpha = 255;
1270     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1271     int        i_font_size  = 24;
1272
1273     /* Default all attributes to the top font in the stack -- in case not
1274      * all attributes are specified in the sub-font
1275      */
1276     if( VLC_SUCCESS == PeekFont( p_fonts,
1277                                  &psz_fontname,
1278                                  &i_font_size,
1279                                  &i_font_color,
1280                                  &i_karaoke_bg_color ))
1281     {
1282         psz_fontname = strdup( psz_fontname );
1283         i_font_size = i_font_size;
1284     }
1285     i_font_alpha = (i_font_color >> 24) & 0xff;
1286     i_font_color &= 0x00ffffff;
1287
1288     const char *name, *value;
1289     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1290     {
1291         if( !strcasecmp( "face", name ) )
1292         {
1293             free( psz_fontname );
1294             psz_fontname = strdup( value );
1295         }
1296         else if( !strcasecmp( "size", name ) )
1297         {
1298             if( ( *value == '+' ) || ( *value == '-' ) )
1299             {
1300                 int i_value = atoi( value );
1301
1302                 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1303                     i_font_size += ( i_value * i_font_size ) / 10;
1304                 else if( i_value < -5 )
1305                     i_font_size = - i_value;
1306                 else if( i_value > 5 )
1307                     i_font_size = i_value;
1308             }
1309             else
1310                 i_font_size = atoi( value );
1311         }
1312         else if( !strcasecmp( "color", name ) )
1313         {
1314             if( value[0] == '#' )
1315             {
1316                 i_font_color = strtol( value + 1, NULL, 16 );
1317                 i_font_color &= 0x00ffffff;
1318             }
1319             else
1320             {
1321                 char *end;
1322                 uint32_t i_value = strtol( value, &end, 16 );
1323                 if( *end == '\0' || *end == ' ' )
1324                     i_font_color = i_value & 0x00ffffff;
1325
1326                 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1327                 {
1328                     if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1329                     {
1330                         i_font_color = p_html_colors[i].i_value;
1331                         break;
1332                     }
1333                 }
1334             }
1335         }
1336         else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1337         {
1338             i_font_alpha = strtol( value + 1, NULL, 16 );
1339             i_font_alpha &= 0xff;
1340         }
1341     }
1342     rv = PushFont( p_fonts,
1343                    psz_fontname,
1344                    i_font_size,
1345                    (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1346                    i_karaoke_bg_color );
1347
1348     free( psz_fontname );
1349
1350     return rv;
1351 }
1352
1353 /* Turn any multiple-whitespaces into single spaces */
1354 static void HandleWhiteSpace( char *psz_node )
1355 {
1356     char *s = strpbrk( psz_node, "\t\r\n " );
1357     while( s )
1358     {
1359         int i_whitespace = strspn( s, "\t\r\n " );
1360
1361         if( i_whitespace > 1 )
1362             memmove( &s[1],
1363                      &s[i_whitespace],
1364                      strlen( s ) - i_whitespace + 1 );
1365         *s++ = ' ';
1366
1367         s = strpbrk( s, "\t\r\n " );
1368     }
1369 }
1370
1371
1372 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1373                                             font_stack_t **p_fonts,
1374                                             int i_style_flags )
1375 {
1376     char       *psz_fontname = NULL;
1377     uint32_t    i_font_color = p_sys->i_font_color & 0x00ffffff;
1378     uint32_t    i_karaoke_bg_color = i_font_color;
1379     int         i_font_size  = p_sys->i_font_size;
1380
1381     if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1382                   &i_font_color, &i_karaoke_bg_color ) )
1383         return NULL;
1384
1385     return CreateStyle( psz_fontname, i_font_size, i_font_color,
1386                         i_karaoke_bg_color,
1387                         i_style_flags );
1388 }
1389
1390 static unsigned SetupText( filter_t *p_filter,
1391                            uint32_t *psz_text_out,
1392                            text_style_t **pp_styles,
1393                            uint32_t *pi_k_dates,
1394
1395                            const char *psz_text_in,
1396                            text_style_t *p_style,
1397                            uint32_t i_k_date )
1398 {
1399     size_t i_string_length;
1400
1401     size_t i_string_bytes;
1402 #if defined(WORDS_BIGENDIAN)
1403     uint32_t *psz_tmp = ToCharset( "UCS-4BE", psz_text_in, &i_string_bytes );
1404 #else
1405     uint32_t *psz_tmp = ToCharset( "UCS-4LE", psz_text_in, &i_string_bytes );
1406 #endif
1407     if( psz_tmp )
1408     {
1409         memcpy( psz_text_out, psz_tmp, i_string_bytes );
1410         i_string_length = i_string_bytes / 4;
1411         free( psz_tmp );
1412     }
1413     else
1414     {
1415         msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1416         i_string_length = 0;
1417     }
1418
1419     if( i_string_length > 0 )
1420     {
1421         for( unsigned i = 0; i < i_string_length; i++ )
1422             pp_styles[i] = p_style;
1423     }
1424     else
1425     {
1426         text_style_Delete( p_style );
1427     }
1428     if( i_string_length > 0 && pi_k_dates )
1429     {
1430         for( unsigned i = 0; i < i_string_length; i++ )
1431             pi_k_dates[i] = i_k_date;
1432     }
1433     return i_string_length;
1434 }
1435
1436 static int ProcessNodes( filter_t *p_filter,
1437                          uint32_t *psz_text,
1438                          text_style_t **pp_styles,
1439                          uint32_t *pi_k_dates,
1440                          int *pi_len,
1441                          xml_reader_t *p_xml_reader,
1442                          text_style_t *p_font_style )
1443 {
1444     int           rv      = VLC_SUCCESS;
1445     filter_sys_t *p_sys   = p_filter->p_sys;
1446     int i_text_length     = 0;
1447     font_stack_t *p_fonts = NULL;
1448     uint32_t i_k_date     = 0;
1449
1450     int i_style_flags = 0;
1451
1452     if( p_font_style )
1453     {
1454         rv = PushFont( &p_fonts,
1455                p_font_style->psz_fontname,
1456                p_font_style->i_font_size > 0 ? p_font_style->i_font_size
1457                                              : p_sys->i_font_size,
1458                (p_font_style->i_font_color & 0xffffff) |
1459                    ((p_font_style->i_font_alpha & 0xff) << 24),
1460                (p_font_style->i_karaoke_background_color & 0xffffff) |
1461                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1462
1463         i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1464                                                        STYLE_ITALIC |
1465                                                        STYLE_UNDERLINE |
1466                                                        STYLE_STRIKEOUT);
1467     }
1468 #ifdef HAVE_STYLES
1469     else
1470     {
1471         rv = PushFont( &p_fonts,
1472                        p_sys->psz_fontfamily,
1473                        p_sys->i_font_size,
1474                        (p_sys->i_font_color & 0xffffff) |
1475                           ((p_sys->i_font_opacity & 0xff) << 24),
1476                        0x00ffffff );
1477     }
1478 #endif
1479     if( p_sys->b_font_bold )
1480         i_style_flags |= STYLE_BOLD;
1481
1482     if( rv != VLC_SUCCESS )
1483         return rv;
1484
1485     const char *node;
1486     int type;
1487
1488     while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1489     {
1490         switch ( type )
1491         {
1492             case XML_READER_ENDELEM:
1493                 if( !strcasecmp( "font", node ) )
1494                     PopFont( &p_fonts );
1495                 else if( !strcasecmp( "b", node ) )
1496                     i_style_flags &= ~STYLE_BOLD;
1497                else if( !strcasecmp( "i", node ) )
1498                     i_style_flags &= ~STYLE_ITALIC;
1499                 else if( !strcasecmp( "u", node ) )
1500                     i_style_flags &= ~STYLE_UNDERLINE;
1501                 else if( !strcasecmp( "s", node ) )
1502                     i_style_flags &= ~STYLE_STRIKEOUT;
1503                 break;
1504
1505             case XML_READER_STARTELEM:
1506                 if( !strcasecmp( "font", node ) )
1507                     HandleFontAttributes( p_xml_reader, &p_fonts );
1508                 else if( !strcasecmp( "b", node ) )
1509                     i_style_flags |= STYLE_BOLD;
1510                 else if( !strcasecmp( "i", node ) )
1511                     i_style_flags |= STYLE_ITALIC;
1512                 else if( !strcasecmp( "u", node ) )
1513                     i_style_flags |= STYLE_UNDERLINE;
1514                 else if( !strcasecmp( "s", node ) )
1515                     i_style_flags |= STYLE_STRIKEOUT;
1516                 else if( !strcasecmp( "br", node ) )
1517                 {
1518                     i_text_length += SetupText( p_filter,
1519                                                 &psz_text[i_text_length],
1520                                                 &pp_styles[i_text_length],
1521                                                 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1522                                                 "\n",
1523                                                 GetStyleFromFontStack( p_sys,
1524                                                                        &p_fonts,
1525                                                                        i_style_flags ),
1526                                                 i_k_date );
1527                 }
1528                 else if( !strcasecmp( "k", node ) )
1529                 {
1530                     /* Karaoke tags */
1531                     const char *name, *value;
1532                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1533                     {
1534                         if( !strcasecmp( "t", name ) && value )
1535                             i_k_date += atoi( value );
1536                     }
1537                 }
1538                 break;
1539
1540             case XML_READER_TEXT:
1541             {
1542                 char *psz_node = strdup( node );
1543                 if( unlikely(!psz_node) )
1544                     break;
1545
1546                 HandleWhiteSpace( psz_node );
1547                 resolve_xml_special_chars( psz_node );
1548
1549                 i_text_length += SetupText( p_filter,
1550                                             &psz_text[i_text_length],
1551                                             &pp_styles[i_text_length],
1552                                             pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1553                                             psz_node,
1554                                             GetStyleFromFontStack( p_sys,
1555                                                                    &p_fonts,
1556                                                                    i_style_flags ),
1557                                             i_k_date );
1558                 free( psz_node );
1559                 break;
1560             }
1561         }
1562     }
1563
1564     *pi_len = i_text_length;
1565
1566     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1567
1568     return VLC_SUCCESS;
1569 }
1570
1571 static void FreeLine( line_desc_t *p_line )
1572 {
1573     for( int i = 0; i < p_line->i_character_count; i++ )
1574     {
1575         line_character_t *ch = &p_line->p_character[i];
1576         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1577         if( ch->p_outline )
1578             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1579         if( ch->p_shadow )
1580             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1581     }
1582
1583     free( p_line->p_character );
1584     free( p_line );
1585 }
1586
1587 static void FreeLines( line_desc_t *p_lines )
1588 {
1589     for( line_desc_t *p_line = p_lines; p_line != NULL; )
1590     {
1591         line_desc_t *p_next = p_line->p_next;
1592         FreeLine( p_line );
1593         p_line = p_next;
1594     }
1595 }
1596
1597 static line_desc_t *NewLine( int i_count )
1598 {
1599     line_desc_t *p_line = malloc( sizeof(*p_line) );
1600
1601     if( !p_line )
1602         return NULL;
1603
1604     p_line->p_next = NULL;
1605     p_line->i_width = 0;
1606     p_line->i_base_line = 0;
1607     p_line->i_character_count = 0;
1608
1609     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1610     if( !p_line->p_character )
1611     {
1612         free( p_line );
1613         return NULL;
1614     }
1615     return p_line;
1616 }
1617
1618 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1619 {
1620     for( int k = 0; k < p_sys->i_font_attachments; k++ )
1621     {
1622         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
1623         int                 i_font_idx = 0;
1624         FT_Face             p_face = NULL;
1625
1626         while( 0 == FT_New_Memory_Face( p_sys->p_library,
1627                                         p_attach->p_data,
1628                                         p_attach->i_data,
1629                                         i_font_idx,
1630                                         &p_face ))
1631         {
1632             if( p_face )
1633             {
1634                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
1635                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1636                 if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
1637                     (p_style->i_style_flags & (STYLE_BOLD | STYLE_BOLD)) == i_style_received )
1638                     return p_face;
1639
1640                 FT_Done_Face( p_face );
1641             }
1642             i_font_idx++;
1643         }
1644     }
1645     return NULL;
1646 }
1647
1648 static FT_Face LoadFace( filter_t *p_filter,
1649                          const text_style_t *p_style )
1650 {
1651     filter_sys_t *p_sys = p_filter->p_sys;
1652
1653     /* Look for a match amongst our attachments first */
1654     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1655
1656     /* Load system wide font otheriwse */
1657     if( !p_face )
1658     {
1659         int  i_idx = 0;
1660         char *psz_fontfile;
1661 #ifdef HAVE_FONTCONFIG
1662         psz_fontfile = FontConfig_Select( NULL,
1663                                           p_style->psz_fontname,
1664                                           (p_style->i_style_flags & STYLE_BOLD) != 0,
1665                                           (p_style->i_style_flags & STYLE_ITALIC) != 0,
1666                                           -1,
1667                                           &i_idx );
1668 #elif defined( WIN32 )
1669         psz_fontfile = Win32_Select( p_filter,
1670                                     p_style->psz_fontname,
1671                                     (p_style->i_style_flags & STYLE_BOLD) != 0,
1672                                     (p_style->i_style_flags & STYLE_ITALIC) != 0,
1673                                     -1,
1674                                     &i_idx );
1675 #else
1676         psz_fontfile = NULL;
1677 #endif
1678         if( !psz_fontfile )
1679             return NULL;
1680
1681         if( *psz_fontfile == '\0' )
1682         {
1683             msg_Warn( p_filter,
1684                       "We were not able to find a matching font: \"%s\" (%s %s),"
1685                       " so using default font",
1686                       p_style->psz_fontname,
1687                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
1688                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1689             p_face = NULL;
1690         }
1691         else
1692         {
1693             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1694                 p_face = NULL;
1695         }
1696         free( psz_fontfile );
1697     }
1698     if( !p_face )
1699         return NULL;
1700
1701     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1702     {
1703         /* We've loaded a font face which is unhelpful for actually
1704          * rendering text - fallback to the default one.
1705          */
1706         FT_Done_Face( p_face );
1707         return NULL;
1708     }
1709     return p_face;
1710 }
1711
1712 static bool FaceStyleEquals( const text_style_t *p_style1,
1713                              const text_style_t *p_style2 )
1714 {
1715     if( !p_style1 || !p_style2 )
1716         return false;
1717     if( p_style1 == p_style2 )
1718         return true;
1719
1720     const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
1721     return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
1722            !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
1723 }
1724
1725 static int GetGlyph( filter_t *p_filter,
1726                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
1727                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1728                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
1729
1730                      FT_Face  p_face,
1731                      int i_glyph_index,
1732                      int i_style_flags,
1733                      FT_Vector *p_pen,
1734                      FT_Vector *p_pen_shadow )
1735 {
1736     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1737         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1738     {
1739         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1740         return VLC_EGENERIC;
1741     }
1742
1743     /* Do synthetic styling now that Freetype supports it;
1744      * ie. if the font we have loaded is NOT already in the
1745      * style that the tags want, then switch it on; if they
1746      * are then don't. */
1747     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1748         FT_GlyphSlot_Embolden( p_face->glyph );
1749     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1750         FT_GlyphSlot_Oblique( p_face->glyph );
1751
1752     FT_Glyph glyph;
1753     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1754     {
1755         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1756         return VLC_EGENERIC;
1757     }
1758
1759     FT_Glyph outline = NULL;
1760     if( p_filter->p_sys->p_stroker )
1761     {
1762         outline = glyph;
1763         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1764             outline = NULL;
1765     }
1766
1767     FT_Glyph shadow = NULL;
1768     if( p_filter->p_sys->i_shadow_opacity > 0 )
1769     {
1770         shadow = outline ? outline : glyph;
1771         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
1772         {
1773             shadow = NULL;
1774         }
1775         else
1776         {
1777             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1778         }
1779     }
1780     *pp_shadow = shadow;
1781
1782     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1783     {
1784         FT_Done_Glyph( glyph );
1785         if( outline )
1786             FT_Done_Glyph( outline );
1787         if( shadow )
1788             FT_Done_Glyph( shadow );
1789         return VLC_EGENERIC;
1790     }
1791     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1792     *pp_glyph = glyph;
1793
1794     if( outline )
1795     {
1796         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1797         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1798     }
1799     *pp_outline = outline;
1800
1801     return VLC_SUCCESS;
1802 }
1803
1804 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1805 {
1806     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1807     if( p_bbox->xMin >= p_bbox->xMax )
1808     {
1809         p_bbox->xMin = FT_CEIL(p_pen->x);
1810         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1811         glyph_bmp->left = p_bbox->xMin;
1812     }
1813     if( p_bbox->yMin >= p_bbox->yMax )
1814     {
1815         p_bbox->yMax = FT_CEIL(p_pen->y);
1816         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1817         glyph_bmp->top  = p_bbox->yMax;
1818     }
1819 }
1820
1821 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1822 {
1823     p_max->xMin = __MIN(p_max->xMin, p->xMin);
1824     p_max->yMin = __MIN(p_max->yMin, p->yMin);
1825     p_max->xMax = __MAX(p_max->xMax, p->xMax);
1826     p_max->yMax = __MAX(p_max->yMax, p->yMax);
1827 }
1828
1829 static int ProcessLines( filter_t *p_filter,
1830                          line_desc_t **pp_lines,
1831                          FT_BBox     *p_bbox,
1832                          int         *pi_max_face_height,
1833
1834                          uint32_t *psz_text,
1835                          text_style_t **pp_styles,
1836                          uint32_t *pi_k_dates,
1837                          int i_len )
1838 {
1839     filter_sys_t   *p_sys = p_filter->p_sys;
1840     uint32_t       *p_fribidi_string = NULL;
1841     text_style_t   **pp_fribidi_styles = NULL;
1842     int            *p_new_positions = NULL;
1843
1844 #if defined(HAVE_FRIBIDI)
1845     {
1846         int    *p_old_positions;
1847         int start_pos, pos = 0;
1848
1849         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1850
1851         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1852         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1853         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1854
1855         if( ! pp_fribidi_styles ||
1856             ! p_fribidi_string ||
1857             ! p_old_positions ||
1858             ! p_new_positions )
1859         {
1860             free( p_old_positions );
1861             free( p_new_positions );
1862             free( p_fribidi_string );
1863             free( pp_fribidi_styles );
1864             return VLC_ENOMEM;
1865         }
1866
1867         /* Do bidi conversion line-by-line */
1868         while(pos < i_len)
1869         {
1870             while(pos < i_len) {
1871                 if (psz_text[pos] != '\n')
1872                     break;
1873                 p_fribidi_string[pos] = psz_text[pos];
1874                 pp_fribidi_styles[pos] = pp_styles[pos];
1875                 p_new_positions[pos] = pos;
1876                 ++pos;
1877             }
1878             start_pos = pos;
1879             while(pos < i_len) {
1880                 if (psz_text[pos] == '\n')
1881                     break;
1882                 ++pos;
1883             }
1884             if (pos > start_pos)
1885             {
1886 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1887                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1888 #else
1889                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1890 #endif
1891                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1892                         pos - start_pos, &base_dir,
1893                         (FriBidiChar*)p_fribidi_string + start_pos,
1894                         p_new_positions + start_pos,
1895                         p_old_positions,
1896                         NULL );
1897                 for( int j = start_pos; j < pos; j++ )
1898                 {
1899                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1900                     p_new_positions[ j ] += start_pos;
1901                 }
1902             }
1903         }
1904         p_fribidi_string[ i_len ] = 0;
1905         free( p_old_positions );
1906
1907         pp_styles = pp_fribidi_styles;
1908         psz_text = p_fribidi_string;
1909     }
1910 #endif
1911     /* Work out the karaoke */
1912     uint8_t *pi_karaoke_bar = NULL;
1913     if( pi_k_dates )
1914     {
1915         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1916         if( pi_karaoke_bar )
1917         {
1918             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1919             for( int i = 0; i < i_len; i++ )
1920             {
1921                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1922                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1923             }
1924         }
1925     }
1926     free( p_new_positions );
1927
1928     *pi_max_face_height = 0;
1929     *pp_lines = NULL;
1930     line_desc_t **pp_line_next = pp_lines;
1931
1932     FT_BBox bbox = {
1933         .xMin = INT_MAX,
1934         .yMin = INT_MAX,
1935         .xMax = INT_MIN,
1936         .yMax = INT_MIN,
1937     };
1938     int i_face_height_previous = 0;
1939     int i_base_line = 0;
1940     const text_style_t *p_previous_style = NULL;
1941     FT_Face p_face = NULL;
1942     for( int i_start = 0; i_start < i_len; )
1943     {
1944         /* Compute the length of the current text line */
1945         int i_length = 0;
1946         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1947             i_length++;
1948
1949         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1950         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1951         int i_index = i_start;
1952         FT_Vector pen = {
1953             .x = 0,
1954             .y = 0,
1955         };
1956         int i_face_height = 0;
1957         FT_BBox line_bbox = {
1958             .xMin = INT_MAX,
1959             .yMin = INT_MAX,
1960             .xMax = INT_MIN,
1961             .yMax = INT_MIN,
1962         };
1963         int i_ul_offset = 0;
1964         int i_ul_thickness = 0;
1965         typedef struct {
1966             int       i_index;
1967             FT_Vector pen;
1968             FT_BBox   line_bbox;
1969             int i_face_height;
1970             int i_ul_offset;
1971             int i_ul_thickness;
1972         } break_point_t;
1973         break_point_t break_point;
1974         break_point_t break_point_fallback;
1975
1976 #define SAVE_BP(dst) do { \
1977         dst.i_index = i_index; \
1978         dst.pen = pen; \
1979         dst.line_bbox = line_bbox; \
1980         dst.i_face_height = i_face_height; \
1981         dst.i_ul_offset = i_ul_offset; \
1982         dst.i_ul_thickness = i_ul_thickness; \
1983     } while(0)
1984
1985         SAVE_BP( break_point );
1986         SAVE_BP( break_point_fallback );
1987
1988         while( i_index < i_start + i_length )
1989         {
1990             /* Split by common FT_Face + Size */
1991             const text_style_t *p_current_style = pp_styles[i_index];
1992             int i_part_length = 0;
1993             while( i_index + i_part_length < i_start + i_length )
1994             {
1995                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1996                 if( !FaceStyleEquals( p_style, p_current_style ) ||
1997                     p_style->i_font_size != p_current_style->i_font_size )
1998                     break;
1999                 i_part_length++;
2000             }
2001
2002             /* (Re)load/reconfigure the face if needed */
2003             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
2004             {
2005                 if( p_face )
2006                     FT_Done_Face( p_face );
2007                 p_previous_style = NULL;
2008
2009                 p_face = LoadFace( p_filter, p_current_style );
2010             }
2011             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
2012             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
2013             {
2014                 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
2015                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
2016                 if( p_sys->p_stroker )
2017                 {
2018                     int i_radius = (p_current_style->i_font_size << 6) * p_sys->f_outline_thickness;
2019                     FT_Stroker_Set( p_sys->p_stroker,
2020                                     i_radius,
2021                                     FT_STROKER_LINECAP_ROUND,
2022                                     FT_STROKER_LINEJOIN_ROUND, 0 );
2023                 }
2024             }
2025             p_previous_style = p_current_style;
2026
2027             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
2028                                                                    p_current_face->size->metrics.y_scale)));
2029
2030             /* Render the part */
2031             bool b_break_line = false;
2032             int i_glyph_last = 0;
2033             while( i_part_length > 0 )
2034             {
2035                 const text_style_t *p_glyph_style = pp_styles[i_index];
2036                 uint32_t character = psz_text[i_index];
2037                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
2038
2039                 /* Get kerning vector */
2040                 FT_Vector kerning = { .x = 0, .y = 0 };
2041                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
2042                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
2043
2044                 /* Get the glyph bitmap and its bounding box and all the associated properties */
2045                 FT_Vector pen_new = {
2046                     .x = pen.x + kerning.x,
2047                     .y = pen.y + kerning.y,
2048                 };
2049                 FT_Vector pen_shadow_new = {
2050                     .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
2051                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
2052                 };
2053                 FT_Glyph glyph;
2054                 FT_BBox  glyph_bbox;
2055                 FT_Glyph outline;
2056                 FT_BBox  outline_bbox;
2057                 FT_Glyph shadow;
2058                 FT_BBox  shadow_bbox;
2059
2060                 if( GetGlyph( p_filter,
2061                               &glyph, &glyph_bbox,
2062                               &outline, &outline_bbox,
2063                               &shadow, &shadow_bbox,
2064                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
2065                               &pen_new, &pen_shadow_new ) )
2066                     goto next;
2067
2068                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
2069                 if( outline )
2070                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
2071                 if( shadow )
2072                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
2073
2074                 /* FIXME and what about outline */
2075
2076                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
2077                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
2078                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
2079                                              : (p_glyph_style->i_font_color |
2080                                                 (p_glyph_style->i_font_alpha << 24));
2081                 int i_line_offset    = 0;
2082                 int i_line_thickness = 0;
2083                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
2084                 {
2085                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
2086                                                             p_current_face->size->metrics.y_scale)) );
2087
2088                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
2089                                                               p_current_face->size->metrics.y_scale)) );
2090
2091                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
2092                     {
2093                         /* Move the baseline to make it strikethrough instead of
2094                          * underline. That means that strikethrough takes precedence
2095                          */
2096                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
2097                                                                  p_current_face->size->metrics.y_scale)) );
2098                     }
2099                     else if( i_line_thickness > 0 )
2100                     {
2101                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
2102
2103                         /* The real underline thickness and position are
2104                          * updated once the whole line has been parsed */
2105                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
2106                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
2107                         i_line_thickness = -1;
2108                     }
2109                 }
2110                 FT_BBox line_bbox_new = line_bbox;
2111                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
2112                 if( outline )
2113                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
2114                 if( shadow )
2115                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
2116
2117                 b_break_line = i_index > i_start &&
2118                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
2119                 if( b_break_line )
2120                 {
2121                     FT_Done_Glyph( glyph );
2122                     if( outline )
2123                         FT_Done_Glyph( outline );
2124                     if( shadow )
2125                         FT_Done_Glyph( shadow );
2126
2127                     break_point_t *p_bp = NULL;
2128                     if( break_point.i_index > i_start )
2129                         p_bp = &break_point;
2130                     else if( break_point_fallback.i_index > i_start )
2131                         p_bp = &break_point_fallback;
2132
2133                     if( p_bp )
2134                     {
2135                         msg_Dbg( p_filter, "Breaking line");
2136                         for( int i = p_bp->i_index; i < i_index; i++ )
2137                         {
2138                             line_character_t *ch = &p_line->p_character[i - i_start];
2139                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
2140                             if( ch->p_outline )
2141                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
2142                             if( ch->p_shadow )
2143                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
2144                         }
2145                         p_line->i_character_count = p_bp->i_index - i_start;
2146
2147                         i_index = p_bp->i_index;
2148                         pen = p_bp->pen;
2149                         line_bbox = p_bp->line_bbox;
2150                         i_face_height = p_bp->i_face_height;
2151                         i_ul_offset = p_bp->i_ul_offset;
2152                         i_ul_thickness = p_bp->i_ul_thickness;
2153                     }
2154                     else
2155                     {
2156                         msg_Err( p_filter, "Breaking unbreakable line");
2157                     }
2158                     break;
2159                 }
2160
2161                 assert( p_line->i_character_count == i_index - i_start);
2162                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
2163                     .p_glyph = (FT_BitmapGlyph)glyph,
2164                     .p_outline = (FT_BitmapGlyph)outline,
2165                     .p_shadow = (FT_BitmapGlyph)shadow,
2166                     .i_color = i_color,
2167                     .i_line_offset = i_line_offset,
2168                     .i_line_thickness = i_line_thickness,
2169                 };
2170
2171                 pen.x = pen_new.x + p_current_face->glyph->advance.x;
2172                 pen.y = pen_new.y + p_current_face->glyph->advance.y;
2173                 line_bbox = line_bbox_new;
2174             next:
2175                 i_glyph_last = i_glyph_index;
2176                 i_part_length--;
2177                 i_index++;
2178
2179                 if( character == ' ' || character == '\t' )
2180                     SAVE_BP( break_point );
2181                 else if( character == 160 )
2182                     SAVE_BP( break_point_fallback );
2183             }
2184             if( b_break_line )
2185                 break;
2186         }
2187 #undef SAVE_BP
2188         /* Update our baseline */
2189         if( i_face_height_previous > 0 )
2190             i_base_line += __MAX(i_face_height, i_face_height_previous);
2191         if( i_face_height > 0 )
2192             i_face_height_previous = i_face_height;
2193
2194         /* Update the line bbox with the actual base line */
2195         if (line_bbox.yMax > line_bbox.yMin) {
2196             line_bbox.yMin -= i_base_line;
2197             line_bbox.yMax -= i_base_line;
2198         }
2199         BBoxEnlarge( &bbox, &line_bbox );
2200
2201         /* Terminate and append the line */
2202         if( p_line )
2203         {
2204             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
2205             p_line->i_base_line = i_base_line;
2206             if( i_ul_thickness > 0 )
2207             {
2208                 for( int i = 0; i < p_line->i_character_count; i++ )
2209                 {
2210                     line_character_t *ch = &p_line->p_character[i];
2211                     if( ch->i_line_thickness < 0 )
2212                     {
2213                         ch->i_line_offset    = i_ul_offset;
2214                         ch->i_line_thickness = i_ul_thickness;
2215                     }
2216                 }
2217             }
2218
2219             *pp_line_next = p_line;
2220             pp_line_next = &p_line->p_next;
2221         }
2222
2223         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
2224
2225         /* Skip what we have rendered and the line delimitor if present */
2226         i_start = i_index;
2227         if( i_start < i_len && psz_text[i_start] == '\n' )
2228             i_start++;
2229
2230         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
2231         {
2232             msg_Err( p_filter, "Truncated too high subtitle" );
2233             break;
2234         }
2235     }
2236     if( p_face )
2237         FT_Done_Face( p_face );
2238
2239     free( pp_fribidi_styles );
2240     free( p_fribidi_string );
2241     free( pi_karaoke_bar );
2242
2243     *p_bbox = bbox;
2244     return VLC_SUCCESS;
2245 }
2246
2247 /**
2248  * This function renders a text subpicture region into another one.
2249  * It also calculates the size needed for this string, and renders the
2250  * needed glyphs into memory. It is used as pf_add_string callback in
2251  * the vout method by this module
2252  */
2253 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2254                          subpicture_region_t *p_region_in, bool b_html,
2255                          const vlc_fourcc_t *p_chroma_list )
2256 {
2257     filter_sys_t *p_sys = p_filter->p_sys;
2258
2259     if( !p_region_in )
2260         return VLC_EGENERIC;
2261     if( b_html && !p_region_in->psz_html )
2262         return VLC_EGENERIC;
2263     if( !b_html && !p_region_in->psz_text )
2264         return VLC_EGENERIC;
2265
2266     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2267                                              : p_region_in->psz_text );
2268
2269     uint32_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2270     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2271     if( !psz_text || !pp_styles )
2272     {
2273         free( psz_text );
2274         free( pp_styles );
2275         return VLC_EGENERIC;
2276     }
2277
2278     /* Reset the default fontsize in case screen metrics have changed */
2279     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2280
2281     /* */
2282     int rv = VLC_SUCCESS;
2283     int i_text_length = 0;
2284     FT_BBox bbox;
2285     int i_max_face_height;
2286     line_desc_t *p_lines = NULL;
2287
2288     uint32_t *pi_k_durations   = NULL;
2289
2290 #ifdef HAVE_STYLES
2291     if( b_html )
2292     {
2293         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2294                                             (uint8_t *) p_region_in->psz_html,
2295                                             strlen( p_region_in->psz_html ),
2296                                             true );
2297         if( unlikely(p_sub == NULL) )
2298             return VLC_SUCCESS;
2299
2300         xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2301         if( !p_xml_reader )
2302             p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2303         else
2304             p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2305         p_filter->p_sys->p_xml = p_xml_reader;
2306
2307         if( !p_xml_reader )
2308             rv = VLC_EGENERIC;
2309
2310         if( !rv )
2311         {
2312             /* Look for Root Node */
2313             const char *node;
2314
2315             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2316             {
2317                 if( strcasecmp( "karaoke", node ) == 0 )
2318                 {
2319                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2320                 }
2321                 else if( strcasecmp( "text", node ) != 0 )
2322                 {
2323                     /* Only text and karaoke tags are supported */
2324                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2325                              node );
2326                     rv = VLC_EGENERIC;
2327                 }
2328             }
2329             else
2330             {
2331                 msg_Err( p_filter, "Malformed HTML subtitle" );
2332                 rv = VLC_EGENERIC;
2333             }
2334         }
2335         if( !rv )
2336         {
2337             rv = ProcessNodes( p_filter,
2338                                psz_text, pp_styles, pi_k_durations, &i_text_length,
2339                                p_xml_reader, p_region_in->p_style );
2340         }
2341
2342         if( p_xml_reader )
2343             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2344
2345         stream_Delete( p_sub );
2346     }
2347     else
2348 #endif
2349     {
2350         text_style_t *p_style;
2351         if( p_region_in->p_style )
2352             p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2353                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2354                                                                          : p_sys->i_font_size,
2355                                    (p_region_in->p_style->i_font_color & 0xffffff) |
2356                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2357                                    0x00ffffff,
2358                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2359                                                                           STYLE_ITALIC |
2360                                                                           STYLE_UNDERLINE |
2361                                                                           STYLE_STRIKEOUT) );
2362         else
2363             p_style = CreateStyle( p_sys->psz_fontfamily,
2364                                    p_sys->i_font_size,
2365                                    (p_sys->i_font_color & 0xffffff) |
2366                                    ((p_sys->i_font_opacity & 0xff) << 24),
2367                                    0x00ffffff, 0);
2368         if( p_sys->b_font_bold )
2369             p_style->i_style_flags |= STYLE_BOLD;
2370
2371         i_text_length = SetupText( p_filter,
2372                                    psz_text,
2373                                    pp_styles,
2374                                    NULL,
2375                                    p_region_in->psz_text, p_style, 0 );
2376     }
2377
2378     if( !rv && i_text_length > 0 )
2379     {
2380         rv = ProcessLines( p_filter,
2381                            &p_lines, &bbox, &i_max_face_height,
2382                            psz_text, pp_styles, pi_k_durations, i_text_length );
2383     }
2384
2385     p_region_out->i_x = p_region_in->i_x;
2386     p_region_out->i_y = p_region_in->i_y;
2387
2388     /* Don't attempt to render text that couldn't be layed out
2389      * properly. */
2390     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2391     {
2392         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2393         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2394
2395         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2396             p_chroma_list = p_chroma_list_yuvp;
2397         else if( !p_chroma_list || *p_chroma_list == 0 )
2398             p_chroma_list = p_chroma_list_rgba;
2399
2400         const int i_margin = p_sys->i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2401         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2402         {
2403             rv = VLC_EGENERIC;
2404             if( *p_chroma == VLC_CODEC_YUVP )
2405                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2406             else if( *p_chroma == VLC_CODEC_YUVA )
2407                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2408                                  VLC_CODEC_YUVA,
2409                                  YUVFromRGB,
2410                                  FillYUVAPicture,
2411                                  BlendYUVAPixel );
2412             else if( *p_chroma == VLC_CODEC_RGBA )
2413                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2414                                  VLC_CODEC_RGBA,
2415                                  RGBFromRGB,
2416                                  FillRGBAPicture,
2417                                  BlendRGBAPixel );
2418             if( !rv )
2419                 break;
2420         }
2421
2422         /* With karaoke, we're going to have to render the text a number
2423          * of times to show the progress marker on the text.
2424          */
2425         if( pi_k_durations )
2426             var_SetBool( p_filter, "text-rerender", true );
2427     }
2428
2429     FreeLines( p_lines );
2430
2431     free( psz_text );
2432     for( int i = 0; i < i_text_length; i++ )
2433     {
2434         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2435             text_style_Delete( pp_styles[i] );
2436     }
2437     free( pp_styles );
2438     free( pi_k_durations );
2439
2440     return rv;
2441 }
2442
2443 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2444                        subpicture_region_t *p_region_in,
2445                        const vlc_fourcc_t *p_chroma_list )
2446 {
2447     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2448 }
2449
2450 #ifdef HAVE_STYLES
2451
2452 static int RenderHtml( 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, true, p_chroma_list );
2457 }
2458
2459 #endif
2460
2461 /*****************************************************************************
2462  * Create: allocates osd-text video thread output method
2463  *****************************************************************************
2464  * This function allocates and initializes a Clone vout method.
2465  *****************************************************************************/
2466 static int Create( vlc_object_t *p_this )
2467 {
2468     filter_t      *p_filter = (filter_t *)p_this;
2469     filter_sys_t  *p_sys;
2470     char          *psz_fontfile   = NULL;
2471     char          *psz_fontfamily = NULL;
2472     int            i_error = 0, fontindex = 0;
2473
2474     /* Allocate structure */
2475     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2476     if( !p_sys )
2477         return VLC_ENOMEM;
2478
2479     p_sys->psz_fontfamily   = NULL;
2480 #ifdef HAVE_STYLES
2481     p_sys->p_xml            = NULL;
2482 #endif
2483     p_sys->p_face           = 0;
2484     p_sys->p_library        = 0;
2485     p_sys->i_font_size      = 0;
2486     p_sys->i_display_height = 0;
2487
2488     var_Create( p_filter, "freetype-rel-fontsize",
2489                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2490
2491     psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2492     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2493     p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2494     p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
2495     p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2496     p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
2497     p_sys->b_font_bold = var_InheritBool( p_filter, "freetype-bold" );
2498
2499     p_sys->i_background_opacity = var_InheritInteger( p_filter,"freetype-background-opacity" );;
2500     p_sys->i_background_opacity = __MAX( __MIN( p_sys->i_background_opacity, 255 ), 0 );
2501     p_sys->i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
2502     p_sys->i_background_color = __MAX( __MIN( p_sys->i_background_color, 0xFFFFFF ), 0 );
2503
2504     p_sys->f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2505     p_sys->f_outline_thickness = __MAX( __MIN( p_sys->f_outline_thickness, 0.5 ), 0.0 );
2506     p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2507     p_sys->i_outline_opacity = __MAX( __MIN( p_sys->i_outline_opacity, 255 ), 0 );
2508     p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2509     p_sys->i_outline_color = __MAX( __MIN( p_sys->i_outline_color, 0xFFFFFF ), 0 );
2510
2511     p_sys->i_shadow_opacity = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2512     p_sys->i_shadow_opacity = __MAX( __MIN( p_sys->i_shadow_opacity, 255 ), 0 );
2513     p_sys->i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2514     p_sys->i_shadow_color = __MAX( __MIN( p_sys->i_shadow_color, 0xFFFFFF ), 0 );
2515     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2516     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2517     f_shadow_distance = __MAX( __MIN( f_shadow_distance, 1 ), 0 );
2518     p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2519     p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2520
2521 #ifdef WIN32
2522     /* Get Windows Font folder */
2523     wchar_t wdir[MAX_PATH];
2524     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2525     {
2526         GetWindowsDirectoryW( wdir, MAX_PATH );
2527         wcscat( wdir, L"\\fonts" );
2528     }
2529     p_sys->psz_win_fonts_path = FromWide( wdir );
2530 #endif
2531
2532     /* Set default psz_fontfamily */
2533     if( !psz_fontfamily || !*psz_fontfamily )
2534     {
2535         free( psz_fontfamily );
2536 #ifdef HAVE_STYLES
2537         psz_fontfamily = strdup( DEFAULT_FAMILY );
2538 #else
2539 # ifdef WIN32
2540         if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2541             goto error;
2542 # else
2543         psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2544 # endif
2545         msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2546 #endif
2547     }
2548
2549     /* Set the current font file */
2550     p_sys->psz_fontfamily = psz_fontfamily;
2551 #ifdef HAVE_STYLES
2552 #ifdef HAVE_FONTCONFIG
2553     FontConfig_BuildCache( p_filter );
2554
2555     /* */
2556     psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2557                                       p_sys->i_default_font_size, &fontindex );
2558 #elif defined(WIN32)
2559     psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2560                                  p_sys->i_default_font_size, &fontindex );
2561
2562 #endif
2563     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2564
2565     /* If nothing is found, use the default family */
2566     if( !psz_fontfile )
2567         psz_fontfile = strdup( psz_fontfamily );
2568
2569 #else /* !HAVE_STYLES */
2570     /* Use the default file */
2571     psz_fontfile = psz_fontfamily;
2572 #endif
2573
2574     /* */
2575     i_error = FT_Init_FreeType( &p_sys->p_library );
2576     if( i_error )
2577     {
2578         msg_Err( p_filter, "couldn't initialize freetype" );
2579         goto error;
2580     }
2581
2582     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2583                            fontindex, &p_sys->p_face );
2584
2585     if( i_error == FT_Err_Unknown_File_Format )
2586     {
2587         msg_Err( p_filter, "file %s have unknown format",
2588                  psz_fontfile ? psz_fontfile : "(null)" );
2589         goto error;
2590     }
2591     else if( i_error )
2592     {
2593         msg_Err( p_filter, "failed to load font file %s",
2594                  psz_fontfile ? psz_fontfile : "(null)" );
2595         goto error;
2596     }
2597
2598     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2599     if( i_error )
2600     {
2601         msg_Err( p_filter, "font has no unicode translation table" );
2602         goto error;
2603     }
2604
2605     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2606
2607     p_sys->p_stroker = NULL;
2608     if( p_sys->f_outline_thickness > 0.001 )
2609     {
2610         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2611         if( i_error )
2612             msg_Err( p_filter, "Failed to create stroker for outlining" );
2613     }
2614
2615     p_sys->pp_font_attachments = NULL;
2616     p_sys->i_font_attachments = 0;
2617
2618     p_filter->pf_render_text = RenderText;
2619 #ifdef HAVE_STYLES
2620     p_filter->pf_render_html = RenderHtml;
2621 #else
2622     p_filter->pf_render_html = NULL;
2623 #endif
2624
2625     LoadFontsFromAttachments( p_filter );
2626
2627 #ifdef HAVE_STYLES
2628     free( psz_fontfile );
2629 #endif
2630
2631     return VLC_SUCCESS;
2632
2633 error:
2634     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2635     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2636 #ifdef HAVE_STYLES
2637     free( psz_fontfile );
2638 #endif
2639     free( psz_fontfamily );
2640     free( p_sys );
2641     return VLC_EGENERIC;
2642 }
2643
2644 /*****************************************************************************
2645  * Destroy: destroy Clone video thread output method
2646  *****************************************************************************
2647  * Clean up all data and library connections
2648  *****************************************************************************/
2649 static void Destroy( vlc_object_t *p_this )
2650 {
2651     filter_t *p_filter = (filter_t *)p_this;
2652     filter_sys_t *p_sys = p_filter->p_sys;
2653
2654     if( p_sys->pp_font_attachments )
2655     {
2656         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2657             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2658
2659         free( p_sys->pp_font_attachments );
2660     }
2661
2662 #ifdef HAVE_STYLES
2663     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2664 #endif
2665     free( p_sys->psz_fontfamily );
2666
2667     /* FcFini asserts calling the subfunction FcCacheFini()
2668      * even if no other library functions have been made since FcInit(),
2669      * so don't call it. */
2670
2671     if( p_sys->p_stroker )
2672         FT_Stroker_Done( p_sys->p_stroker );
2673     FT_Done_Face( p_sys->p_face );
2674     FT_Done_FreeType( p_sys->p_library );
2675     free( p_sys );
2676 }
2677
2678