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