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