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