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