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