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