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