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