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