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