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