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