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