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