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