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