]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
e9e81591a7bbceff90460644314ac6b7dab1778f
[vlc] / modules / misc / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <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
100 /*****************************************************************************
101  * Module descriptor
102  *****************************************************************************/
103 #define FONT_TEXT N_("Font")
104 #define FONT_LONGTEXT N_("Filename for the font you want to use")
105 #define FONTSIZE_TEXT N_("Font size in pixels")
106 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
107     "that will be rendered on the video. " \
108     "If set to something different than 0 this option will override the " \
109     "relative font size." )
110 #define OPACITY_TEXT N_("Opacity")
111 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
112     "text that will be rendered on the video. 0 = transparent, " \
113     "255 = totally opaque. " )
114 #define COLOR_TEXT N_("Text default color")
115 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
116     "the video. This must be an hexadecimal (like HTML colors). The first two "\
117     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
118     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
119 #define FONTSIZER_TEXT N_("Relative font size")
120 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
121     "fonts that will be rendered on the video. If absolute font size is set, "\
122     "relative size will be overriden." )
123
124 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
125 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
126                                          N_("Large"), N_("Larger") };
127 #define YUVP_TEXT N_("Use YUVP renderer")
128 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
129   "This option is only needed if you want to encode into DVB subtitles" )
130 #define EFFECT_TEXT N_("Font Effect")
131 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
132 "text to improve its readability." )
133
134 #define EFFECT_BACKGROUND  1
135 #define EFFECT_OUTLINE     2
136 #define EFFECT_OUTLINE_FAT 3
137
138 static int   pi_effects[] = { 1, 2, 3 };
139 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
140                                            N_("Fat Outline") };
141 static int pi_color_values[] = {
142   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
145
146 static const char *ppsz_color_descriptions[] = {
147   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
150
151 vlc_module_begin();
152     set_shortname( _("Text renderer"));
153     set_description( _("Freetype2 font renderer") );
154     set_category( CAT_VIDEO );
155     set_subcategory( SUBCAT_VIDEO_SUBPIC );
156
157     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
158               VLC_FALSE );
159
160     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
161                  FONTSIZE_LONGTEXT, VLC_TRUE );
162
163     /* opacity valid on 0..255, with default 255 = fully opaque */
164     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
165         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
166
167     /* hook to the color values list, with default 0x00ffffff = white */
168     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
169                  COLOR_LONGTEXT, VLC_FALSE );
170         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
171
172     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
173                  FONTSIZER_LONGTEXT, VLC_FALSE );
174         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
175     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
176                  EFFECT_LONGTEXT, VLC_FALSE );
177         change_integer_list( pi_effects, ppsz_effects_text, 0 );
178
179     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
180               YUVP_LONGTEXT, VLC_TRUE );
181     set_capability( "text renderer", 100 );
182     add_shortcut( "text" );
183     set_callbacks( Create, Destroy );
184 vlc_module_end();
185
186 struct line_desc_t
187 {
188     /** NULL-terminated list of glyphs making the string */
189     FT_BitmapGlyph *pp_glyphs;
190     /** list of relative positions for the glyphs */
191     FT_Vector      *p_glyph_pos;
192     /** list of RGB information for styled text
193      * -- if the rendering mode supports it (RenderYUVA) and
194      *  b_new_color_mode is set, then it becomes possible to
195      *  have multicoloured text within the subtitles. */
196     uint32_t       *p_rgb;
197     vlc_bool_t      b_new_color_mode;
198     /** underline information -- only supplied if text should be underlined */
199     uint16_t       *pi_underline_offset;
200     uint16_t       *pi_underline_thickness;
201
202     int             i_height;
203     int             i_width;
204     int             i_red, i_green, i_blue;
205     int             i_alpha;
206
207     line_desc_t    *p_next;
208 };
209
210 typedef struct font_stack_t font_stack_t;
211 struct font_stack_t
212 {
213     char          *psz_name;
214     int            i_size;
215     int            i_color;
216     int            i_alpha;
217
218     font_stack_t  *p_next;
219 };
220
221 typedef struct
222 {
223     int         i_font_size;
224     uint32_t    i_font_color; /* ARGB */
225     vlc_bool_t  b_italic;
226     vlc_bool_t  b_bold;
227     vlc_bool_t  b_underline;
228     char       *psz_fontname;
229 } ft_style_t;
230
231 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
232 static void FreeLines( line_desc_t * );
233 static void FreeLine( line_desc_t * );
234
235 /*****************************************************************************
236  * filter_sys_t: freetype local data
237  *****************************************************************************
238  * This structure is part of the video output thread descriptor.
239  * It describes the freetype specific properties of an output thread.
240  *****************************************************************************/
241 struct filter_sys_t
242 {
243     FT_Library     p_library;   /* handle to library     */
244     FT_Face        p_face;      /* handle to face object */
245     vlc_bool_t     i_use_kerning;
246     uint8_t        i_font_opacity;
247     int            i_font_color;
248     int            i_font_size;
249     int            i_effect;
250
251     int            i_default_font_size;
252     int            i_display_height;
253 #ifdef HAVE_FONTCONFIG
254     FcConfig      *p_fontconfig;
255 #endif
256 };
257
258 /*****************************************************************************
259  * Create: allocates osd-text video thread output method
260  *****************************************************************************
261  * This function allocates and initializes a Clone vout method.
262  *****************************************************************************/
263 static int Create( vlc_object_t *p_this )
264 {
265     filter_t *p_filter = (filter_t *)p_this;
266     filter_sys_t *p_sys;
267     char *psz_fontfile = NULL;
268     int i_error;
269     vlc_value_t val;
270
271     /* Allocate structure */
272     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
273     if( !p_sys )
274     {
275         msg_Err( p_filter, "out of memory" );
276         return VLC_ENOMEM;
277     }
278     p_sys->p_face = 0;
279     p_sys->p_library = 0;
280     p_sys->i_font_size = 0;
281     p_sys->i_display_height = 0;
282
283     var_Create( p_filter, "freetype-font",
284                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
285     var_Create( p_filter, "freetype-fontsize",
286                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
287     var_Create( p_filter, "freetype-rel-fontsize",
288                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
289     var_Create( p_filter, "freetype-opacity",
290                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
291     var_Create( p_filter, "freetype-effect",
292                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
293     var_Get( p_filter, "freetype-opacity", &val );
294     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
295     var_Create( p_filter, "freetype-color",
296                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
297     var_Get( p_filter, "freetype-color", &val );
298     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
299     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
300
301     /* Look what method was requested */
302     var_Get( p_filter, "freetype-font", &val );
303     psz_fontfile = val.psz_string;
304     if( !psz_fontfile || !*psz_fontfile )
305     {
306         if( psz_fontfile ) free( psz_fontfile );
307         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
308         if( !psz_fontfile )
309         {
310             msg_Err( p_filter, "out of memory" );
311             goto error;
312         }
313 #ifdef WIN32
314         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
315         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
316 #elif defined(__APPLE__)
317         strcpy( psz_fontfile, DEFAULT_FONT );
318 #else
319         msg_Err( p_filter, "user didn't specify a font" );
320         goto error;
321 #endif
322     }
323
324 #ifdef HAVE_FONTCONFIG
325     if( FcInit() )
326         p_sys->p_fontconfig = FcConfigGetCurrent();
327     else
328         p_sys->p_fontconfig = NULL;
329 #endif
330     i_error = FT_Init_FreeType( &p_sys->p_library );
331     if( i_error )
332     {
333         msg_Err( p_filter, "couldn't initialize freetype" );
334         goto error;
335     }
336     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
337                            0, &p_sys->p_face );
338     if( i_error == FT_Err_Unknown_File_Format )
339     {
340         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
341         goto error;
342     }
343     else if( i_error )
344     {
345         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
346         goto error;
347     }
348
349     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
350     if( i_error )
351     {
352         msg_Err( p_filter, "font has no unicode translation table" );
353         goto error;
354     }
355
356     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
357
358     var_Get( p_filter, "freetype-fontsize", &val );
359     p_sys->i_default_font_size = val.i_int;
360     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
361
362     if( psz_fontfile ) free( psz_fontfile );
363     p_filter->pf_render_text = RenderText;
364 #ifdef HAVE_FONTCONFIG
365     p_filter->pf_render_html = RenderHtml;
366 #else
367     p_filter->pf_render_html = NULL;
368 #endif
369     return VLC_SUCCESS;
370
371  error:
372     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
373     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
374     if( psz_fontfile ) free( psz_fontfile );
375     free( p_sys );
376     return VLC_EGENERIC;
377 }
378
379 /*****************************************************************************
380  * Destroy: destroy Clone video thread output method
381  *****************************************************************************
382  * Clean up all data and library connections
383  *****************************************************************************/
384 static void Destroy( vlc_object_t *p_this )
385 {
386     filter_t *p_filter = (filter_t *)p_this;
387     filter_sys_t *p_sys = p_filter->p_sys;
388
389 #ifdef HAVE_FONTCONFIG
390     FcConfigDestroy( p_sys->p_fontconfig );
391     p_sys->p_fontconfig = NULL;
392     /* FcFini asserts calling the subfunction FcCacheFini()
393      * even if no other library functions have been made since FcInit(),
394      * so don't call it. */
395 #endif
396     FT_Done_Face( p_sys->p_face );
397     FT_Done_FreeType( p_sys->p_library );
398     free( p_sys );
399 }
400
401 /*****************************************************************************
402  * Render: place string in picture
403  *****************************************************************************
404  * This function merges the previously rendered freetype glyphs into a picture
405  *****************************************************************************/
406 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
407                    line_desc_t *p_line, int i_width, int i_height )
408 {
409     static uint8_t pi_gamma[16] =
410         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
411           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
412
413     uint8_t *p_dst;
414     video_format_t fmt;
415     int i, x, y, i_pitch;
416     uint8_t i_y; /* YUV values, derived from incoming RGB */
417     int8_t i_u, i_v;
418     subpicture_region_t *p_region_tmp;
419
420     /* Create a new subpicture region */
421     memset( &fmt, 0, sizeof(video_format_t) );
422     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
423     fmt.i_aspect = 0;
424     fmt.i_width = fmt.i_visible_width = i_width + 4;
425     fmt.i_height = fmt.i_visible_height = i_height + 4;
426     if( p_region->fmt.i_visible_width > 0 )
427         fmt.i_visible_width = p_region->fmt.i_visible_width;
428     if( p_region->fmt.i_visible_height > 0 )
429         fmt.i_visible_height = p_region->fmt.i_visible_height;
430     fmt.i_x_offset = fmt.i_y_offset = 0;
431     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
432     if( !p_region_tmp )
433     {
434         msg_Err( p_filter, "cannot allocate SPU region" );
435         return VLC_EGENERIC;
436     }
437
438     p_region->fmt = p_region_tmp->fmt;
439     p_region->picture = p_region_tmp->picture;
440     free( p_region_tmp );
441
442     /* Calculate text color components */
443     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
444                       25 * p_line->i_blue + 128) >> 8) +  16;
445     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
446                      112 * p_line->i_blue + 128) >> 8) + 128;
447     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
448                       18 * p_line->i_blue + 128) >> 8) + 128;
449
450     /* Build palette */
451     fmt.p_palette->i_entries = 16;
452     for( i = 0; i < 8; i++ )
453     {
454         fmt.p_palette->palette[i][0] = 0;
455         fmt.p_palette->palette[i][1] = 0x80;
456         fmt.p_palette->palette[i][2] = 0x80;
457         fmt.p_palette->palette[i][3] = pi_gamma[i];
458         fmt.p_palette->palette[i][3] =
459             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
460     }
461     for( i = 8; i < fmt.p_palette->i_entries; i++ )
462     {
463         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
464         fmt.p_palette->palette[i][1] = i_u;
465         fmt.p_palette->palette[i][2] = i_v;
466         fmt.p_palette->palette[i][3] = pi_gamma[i];
467         fmt.p_palette->palette[i][3] =
468             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
469     }
470
471     p_dst = p_region->picture.Y_PIXELS;
472     i_pitch = p_region->picture.Y_PITCH;
473
474     /* Initialize the region pixels */
475     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
476
477     for( ; p_line != NULL; p_line = p_line->p_next )
478     {
479         int i_glyph_tmax = 0;
480         int i_bitmap_offset, i_offset, i_align_offset = 0;
481         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
482         {
483             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
484             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
485         }
486
487         if( p_line->i_width < i_width )
488         {
489             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
490                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
491             {
492                 i_align_offset = i_width - p_line->i_width;
493             }
494             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
495                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
496             {
497                 i_align_offset = ( i_width - p_line->i_width ) / 2;
498             }
499         }
500
501         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
502         {
503             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
504
505             i_offset = ( p_line->p_glyph_pos[ i ].y +
506                 i_glyph_tmax - p_glyph->top + 2 ) *
507                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
508                 i_align_offset;
509
510             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
511             {
512                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
513                 {
514                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
515                         p_dst[i_offset+x] =
516                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
517                 }
518                 i_offset += i_pitch;
519             }
520         }
521     }
522
523     /* Outlining (find something better than nearest neighbour filtering ?) */
524     if( 1 )
525     {
526         uint8_t *p_dst = p_region->picture.Y_PIXELS;
527         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
528         uint8_t left, current;
529
530         for( y = 1; y < (int)fmt.i_height - 1; y++ )
531         {
532             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
533             p_dst += p_region->picture.Y_PITCH;
534             left = 0;
535
536             for( x = 1; x < (int)fmt.i_width - 1; x++ )
537             {
538                 current = p_dst[x];
539                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
540                              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;
541                 left = current;
542             }
543         }
544         memset( p_top, 0, fmt.i_width );
545     }
546
547     return VLC_SUCCESS;
548 }
549
550 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
551                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
552                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
553                                 int i_glyph_tmax, int i_align_offset,
554                                 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
555                                 subpicture_region_t *p_region)
556 {
557     int y, x, z;
558     int i_pitch;
559     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
560
561     p_dst_y = p_region->picture.Y_PIXELS;
562     p_dst_u = p_region->picture.U_PIXELS;
563     p_dst_v = p_region->picture.V_PIXELS;
564     p_dst_a = p_region->picture.A_PIXELS;
565     i_pitch = p_region->picture.A_PITCH;
566
567     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
568                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
569
570     for( y = 0; y < i_line_thickness; y++ )
571     {
572         int i_extra = p_this_glyph->bitmap.width;
573
574         if( b_ul_next_char )
575         {
576             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
577                       (p_this_glyph_pos->x + p_this_glyph->left);
578         }
579         for( x = 0; x < i_extra; x++ )
580         {
581             vlc_bool_t b_ok = VLC_TRUE;
582
583             /* break the underline around the tails of any glyphs which cross it */
584             for( z = x - i_line_thickness;
585                  z < x + i_line_thickness && b_ok;
586                  z++ )
587             {
588                 if( p_next_glyph && ( z >= i_extra ) )
589                 {
590                     int i_row = i_line_offset + p_next_glyph->top + y;
591
592                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
593                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
594                     {
595                         b_ok = VLC_FALSE;
596                     }
597                 }
598                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
599                 {
600                     int i_row = i_line_offset + p_this_glyph->top + y;
601
602                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
603                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
604                     {
605                         b_ok = VLC_FALSE;
606                     }
607                 }
608             }
609
610             if( b_ok )
611             {
612                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
613                 p_dst_u[i_offset+x] = i_u;
614                 p_dst_v[i_offset+x] = i_v;
615                 p_dst_a[i_offset+x] = 255;
616             }
617         }
618         i_offset += i_pitch;
619     }
620 }
621
622 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
623 {
624     uint8_t *p_dst = p_region->picture.A_PIXELS;
625     int i_pitch = p_region->picture.A_PITCH;
626     int x,y;
627
628     for( ; p_line != NULL; p_line = p_line->p_next )
629     {
630         int i_glyph_tmax=0, i = 0;
631         int i_bitmap_offset, i_offset, i_align_offset = 0;
632         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
633         {
634             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
635             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
636         }
637
638         if( p_line->i_width < i_width )
639         {
640             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
641                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
642             {
643                 i_align_offset = i_width - p_line->i_width;
644             }
645             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
646                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
647             {
648                 i_align_offset = ( i_width - p_line->i_width ) / 2;
649             }
650         }
651
652         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
653         {
654             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
655
656             i_offset = ( p_line->p_glyph_pos[ i ].y +
657                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
658                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
659                 i_align_offset +xoffset;
660
661             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
662             {
663                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
664                 {
665                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
666                         if( p_dst[i_offset+x] <
667                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
668                             p_dst[i_offset+x] =
669                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
670                 }
671                 i_offset += i_pitch;
672             }
673         }
674     }
675
676 }
677
678 /*****************************************************************************
679  * Render: place string in picture
680  *****************************************************************************
681  * This function merges the previously rendered freetype glyphs into a picture
682  *****************************************************************************/
683 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
684                    line_desc_t *p_line, int i_width, int i_height )
685 {
686     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
687     video_format_t fmt;
688     int i, x, y, i_pitch, i_alpha;
689     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
690     subpicture_region_t *p_region_tmp;
691
692     if( i_width == 0 || i_height == 0 )
693         return VLC_SUCCESS;
694
695     /* Create a new subpicture region */
696     memset( &fmt, 0, sizeof(video_format_t) );
697     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
698     fmt.i_aspect = 0;
699     fmt.i_width = fmt.i_visible_width = i_width + 6;
700     fmt.i_height = fmt.i_visible_height = i_height + 6;
701     if( p_region->fmt.i_visible_width > 0 )
702         fmt.i_visible_width = p_region->fmt.i_visible_width;
703     if( p_region->fmt.i_visible_height > 0 )
704         fmt.i_visible_height = p_region->fmt.i_visible_height;
705     fmt.i_x_offset = fmt.i_y_offset = 0;
706     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
707     if( !p_region_tmp )
708     {
709         msg_Err( p_filter, "cannot allocate SPU region" );
710         return VLC_EGENERIC;
711     }
712
713     p_region->fmt = p_region_tmp->fmt;
714     p_region->picture = p_region_tmp->picture;
715     free( p_region_tmp );
716
717     /* Calculate text color components */
718     i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red  + 4130 * p_line->i_green +
719                       802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
720     i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red  + -2384 * p_line->i_green +
721                      3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
722     i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
723                       -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
724     i_alpha = p_line->i_alpha;
725
726     p_dst_y = p_region->picture.Y_PIXELS;
727     p_dst_u = p_region->picture.U_PIXELS;
728     p_dst_v = p_region->picture.V_PIXELS;
729     p_dst_a = p_region->picture.A_PIXELS;
730     i_pitch = p_region->picture.A_PITCH;
731
732     /* Initialize the region pixels */
733     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
734     {
735         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
736         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
737         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
738         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
739     }
740     else
741     {
742         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
743         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
744         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
745         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
746     }
747     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
748         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
749     {
750         DrawBlack( p_line, i_width, p_region,  0,  0);
751         DrawBlack( p_line, i_width, p_region, -1,  0);
752         DrawBlack( p_line, i_width, p_region,  0, -1);
753         DrawBlack( p_line, i_width, p_region,  1,  0);
754         DrawBlack( p_line, i_width, p_region,  0,  1);
755     }
756
757     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
758     {
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         DrawBlack( p_line, i_width, p_region,  1,  1);
763
764         DrawBlack( p_line, i_width, p_region, -2,  0);
765         DrawBlack( p_line, i_width, p_region,  0, -2);
766         DrawBlack( p_line, i_width, p_region,  2,  0);
767         DrawBlack( p_line, i_width, p_region,  0,  2);
768
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         DrawBlack( p_line, i_width, p_region,  2,  2);
773
774         DrawBlack( p_line, i_width, p_region, -3,  0);
775         DrawBlack( p_line, i_width, p_region,  0, -3);
776         DrawBlack( p_line, i_width, p_region,  3,  0);
777         DrawBlack( p_line, i_width, p_region,  0,  3);
778     }
779
780     for( ; p_line != NULL; p_line = p_line->p_next )
781     {
782         int i_glyph_tmax = 0;
783         int i_bitmap_offset, i_offset, i_align_offset = 0;
784         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
785         {
786             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
787             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
788         }
789
790         if( p_line->i_width < i_width )
791         {
792             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
793                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
794             {
795                 i_align_offset = i_width - p_line->i_width;
796             }
797             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
798                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
799             {
800                 i_align_offset = ( i_width - p_line->i_width ) / 2;
801             }
802         }
803
804         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
805         {
806             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
807
808             i_offset = ( p_line->p_glyph_pos[ i ].y +
809                 i_glyph_tmax - p_glyph->top + 3 ) *
810                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
811                 i_align_offset;
812
813             if( p_line->b_new_color_mode )
814             {
815                 /* Every glyph can (and in fact must) have its own color */
816                 int i_red   = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
817                 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >>  8;
818                 int i_blue  = ( p_line->p_rgb[ i ] & 0x000000ff );
819
820                 i_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
821                                   802 * i_blue + 4096 + 131072 ) >> 13, 235);
822                 i_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
823                                  3598 * i_blue + 4096 + 1048576) >> 13, 240);
824                 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
825                                   -585 * i_blue + 4096 + 1048576) >> 13, 240);
826             }
827
828             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
829             {
830                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
831                 {
832                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
833                     {
834                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
835                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
836
837                         p_dst_u[i_offset+x] = i_u;
838                         p_dst_v[i_offset+x] = i_v;
839
840                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
841                             p_dst_a[i_offset+x] = 0xff;
842                     }
843                 }
844                 i_offset += i_pitch;
845             }
846
847             if( p_line->pi_underline_thickness[ i ] )
848             {
849                 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
850                                     p_line->pi_underline_offset[ i ],
851                                    (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
852                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
853                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
854                                     i_glyph_tmax, i_align_offset,
855                                     i_y, i_u, i_v, i_alpha,
856                                     p_region);
857             }
858         }
859     }
860
861     /* Apply the alpha setting */
862     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
863         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
864
865     return VLC_SUCCESS;
866 }
867
868 /**
869  * This function renders a text subpicture region into another one.
870  * It also calculates the size needed for this string, and renders the
871  * needed glyphs into memory. It is used as pf_add_string callback in
872  * the vout method by this module
873  */
874 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
875                        subpicture_region_t *p_region_in )
876 {
877     filter_sys_t *p_sys = p_filter->p_sys;
878     line_desc_t  *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
879     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
880     uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
881     int i_string_length;
882     char *psz_string;
883     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
884     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
885
886     FT_BBox line;
887     FT_BBox glyph_size;
888     FT_Vector result;
889     FT_Glyph tmp_glyph;
890
891     /* Sanity check */
892     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
893     psz_string = p_region_in->psz_text;
894     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
895
896     if( p_region_in->p_style )
897     {
898         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
899         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
900         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
901     }
902     else
903     {
904         i_font_color = p_sys->i_font_color;
905         i_font_alpha = 255 - p_sys->i_font_opacity;
906         i_font_size  = p_sys->i_default_font_size;
907     }
908
909     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
910     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
911     SetFontSize( p_filter, i_font_size );
912
913     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
914     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
915     i_blue  =   i_font_color & 0x000000FF;
916
917     result.x =  result.y = 0;
918     line.xMin = line.xMax = line.yMin = line.yMax = 0;
919
920     psz_unicode = psz_unicode_orig =
921         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
922     if( psz_unicode == NULL )
923     {
924         msg_Err( p_filter, "out of memory" );
925         goto error;
926     }
927 #if defined(WORDS_BIGENDIAN)
928     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
929 #else
930     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
931 #endif
932     if( iconv_handle == (vlc_iconv_t)-1 )
933     {
934         msg_Warn( p_filter, "unable to do conversion" );
935         goto error;
936     }
937
938     {
939         char *p_out_buffer;
940         const char *p_in_buffer = psz_string;
941         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
942         i_in_bytes = strlen( psz_string );
943         i_out_bytes = i_in_bytes * sizeof( uint32_t );
944         i_out_bytes_left = i_out_bytes;
945         p_out_buffer = (char *)psz_unicode;
946         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
947                            &p_out_buffer, &i_out_bytes_left );
948
949         vlc_iconv_close( iconv_handle );
950
951         if( i_in_bytes )
952         {
953             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
954                       "bytes left %d", strerror(errno), (int)i_in_bytes );
955             goto error;
956         }
957         *(uint32_t*)p_out_buffer = 0;
958         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
959     }
960
961 #if defined(HAVE_FRIBIDI)
962     {
963         uint32_t *p_fribidi_string;
964         int start_pos, pos = 0;
965
966         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
967         if( !p_fribidi_string )
968         {
969             msg_Err( p_filter, "out of memory" );
970             goto error;
971         }
972
973         /* Do bidi conversion line-by-line */
974         while(pos < i_string_length)
975         {
976             while(pos < i_string_length) {
977                 i_char = psz_unicode[pos];
978                 if (i_char != '\r' && i_char != '\n')
979                     break;
980                 p_fribidi_string[pos] = i_char;
981                 ++pos;
982             }
983             start_pos = pos;
984             while(pos < i_string_length) {
985                 i_char = psz_unicode[pos];
986                 if (i_char == '\r' || i_char == '\n')
987                     break;
988                 ++pos;
989             }
990             if (pos > start_pos)
991             {
992                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
993                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
994                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
995             }
996         }
997
998         free( psz_unicode_orig );
999         psz_unicode = psz_unicode_orig = p_fribidi_string;
1000         p_fribidi_string[ i_string_length ] = 0;
1001     }
1002 #endif
1003
1004     /* Calculate relative glyph positions and a bounding box for the
1005      * entire string */
1006     if( !(p_line = NewLine( strlen( psz_string ))) )
1007     {
1008         msg_Err( p_filter, "out of memory" );
1009         goto error;
1010     }
1011     p_lines = p_line;
1012     i_pen_x = i_pen_y = 0;
1013     i_previous = i = 0;
1014     psz_line_start = psz_unicode;
1015
1016 #define face p_sys->p_face
1017 #define glyph face->glyph
1018
1019     while( *psz_unicode )
1020     {
1021         i_char = *psz_unicode++;
1022         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1023         {
1024             continue;
1025         }
1026
1027         if( i_char == '\n' )
1028         {
1029             psz_line_start = psz_unicode;
1030             if( !(p_next = NewLine( strlen( psz_string ))) )
1031             {
1032                 msg_Err( p_filter, "out of memory" );
1033                 goto error;
1034             }
1035             p_line->p_next = p_next;
1036             p_line->i_width = line.xMax;
1037             p_line->i_height = face->size->metrics.height >> 6;
1038             p_line->pp_glyphs[ i ] = NULL;
1039             p_line->i_alpha = i_font_alpha;
1040             p_line->i_red = i_red;
1041             p_line->i_green = i_green;
1042             p_line->i_blue = i_blue;
1043             p_prev = p_line;
1044             p_line = p_next;
1045             result.x = __MAX( result.x, line.xMax );
1046             result.y += face->size->metrics.height >> 6;
1047             i_pen_x = 0;
1048             i_previous = i = 0;
1049             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1050             i_pen_y += face->size->metrics.height >> 6;
1051 #if 0
1052             msg_Dbg( p_filter, "Creating new line, i is %d", i );
1053 #endif
1054             continue;
1055         }
1056
1057         i_glyph_index = FT_Get_Char_Index( face, i_char );
1058         if( p_sys->i_use_kerning && i_glyph_index
1059             && i_previous )
1060         {
1061             FT_Vector delta;
1062             FT_Get_Kerning( face, i_previous, i_glyph_index,
1063                             ft_kerning_default, &delta );
1064             i_pen_x += delta.x >> 6;
1065
1066         }
1067         p_line->p_glyph_pos[ i ].x = i_pen_x;
1068         p_line->p_glyph_pos[ i ].y = i_pen_y;
1069         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1070         if( i_error )
1071         {
1072             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1073                                " %d", i_error );
1074             goto error;
1075         }
1076         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1077         if( i_error )
1078         {
1079             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1080                                "%d", i_error );
1081             goto error;
1082         }
1083         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1084         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1085         if( i_error )
1086         {
1087             FT_Done_Glyph( tmp_glyph );
1088             continue;
1089         }
1090         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1091
1092         /* Do rest */
1093         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1094             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1095         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1096         {
1097             p_line->pp_glyphs[ i ] = NULL;
1098             FreeLine( p_line );
1099             p_line = NewLine( strlen( psz_string ));
1100             if( p_prev ) p_prev->p_next = p_line;
1101             else p_lines = p_line;
1102
1103             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1104             {
1105                 psz_unicode--;
1106             }
1107             if( psz_unicode == psz_line_start )
1108             {
1109                 msg_Warn( p_filter, "unbreakable string" );
1110                 goto error;
1111             }
1112             else
1113             {
1114                 *psz_unicode = '\n';
1115             }
1116             psz_unicode = psz_line_start;
1117             i_pen_x = 0;
1118             i_previous = i = 0;
1119             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1120             continue;
1121         }
1122         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1123         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1124
1125         i_previous = i_glyph_index;
1126         i_pen_x += glyph->advance.x >> 6;
1127         i++;
1128     }
1129
1130     p_line->i_width = line.xMax;
1131     p_line->i_height = face->size->metrics.height >> 6;
1132     p_line->pp_glyphs[ i ] = NULL;
1133     p_line->i_alpha = i_font_alpha;
1134     p_line->i_red = i_red;
1135     p_line->i_green = i_green;
1136     p_line->i_blue = i_blue;
1137     result.x = __MAX( result.x, line.xMax );
1138     result.y += line.yMax - line.yMin;
1139
1140 #undef face
1141 #undef glyph
1142
1143     p_region_out->i_x = p_region_in->i_x;
1144     p_region_out->i_y = p_region_in->i_y;
1145
1146     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1147         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1148     else
1149         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1150
1151     if( psz_unicode_orig ) free( psz_unicode_orig );
1152     FreeLines( p_lines );
1153     return VLC_SUCCESS;
1154
1155  error:
1156     if( psz_unicode_orig ) free( psz_unicode_orig );
1157     FreeLines( p_lines );
1158     return VLC_EGENERIC;
1159 }
1160
1161 #ifdef HAVE_FONTCONFIG
1162 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1163         int i_font_color, int i_font_alpha, vlc_bool_t b_bold,
1164         vlc_bool_t b_italic, vlc_bool_t b_uline )
1165 {
1166     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
1167
1168     if( p_style )
1169     {
1170         p_style->i_font_size  = i_font_size;
1171         p_style->i_font_color = ( i_font_color & 0x00ffffff )
1172                                 | (( i_font_alpha & 0xff ) << 24 );
1173         p_style->b_italic     = b_italic;
1174         p_style->b_bold       = b_bold;
1175         p_style->b_underline  = b_uline;
1176         /* p_style has just been malloc'ed in this function -
1177          * it CAN'T have a previous assignment, and hence we
1178          * don't need to do a free() for any previous value -
1179          * which will in fact be undefined. */
1180         p_style->psz_fontname = strdup( psz_fontname );
1181     }
1182     return p_style;
1183 }
1184
1185 static void DeleteStyle( ft_style_t *p_style )
1186 {
1187     if( p_style )
1188     {
1189         if( p_style->psz_fontname )
1190             free( p_style->psz_fontname );
1191         free( p_style );
1192     }
1193 }
1194
1195 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1196 {
1197     if( !s1 || !s2 )
1198         return VLC_FALSE;
1199     if( s1 == s2 )
1200         return VLC_TRUE;
1201
1202     if(( s1->i_font_size  == s2->i_font_size ) &&
1203        ( s1->i_font_color == s2->i_font_color ) &&
1204        ( s1->b_italic     == s2->b_italic ) &&
1205        ( s1->b_bold       == s2->b_bold ) &&
1206        ( s1->b_underline  == s2->b_underline ) &&
1207        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1208     {
1209         return VLC_TRUE;
1210     }
1211     return VLC_FALSE;
1212 }
1213
1214 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1215                      int i_color, int i_alpha )
1216 {
1217     font_stack_t *p_new;
1218
1219     if( !p_font )
1220         return VLC_EGENERIC;
1221
1222     p_new = malloc( sizeof( font_stack_t ) );
1223     if( ! p_new )
1224         return VLC_ENOMEM;
1225
1226     p_new->p_next = NULL;
1227
1228     if( psz_name )
1229         p_new->psz_name = strdup( psz_name );
1230     else
1231         p_new->psz_name = NULL;
1232
1233     p_new->i_size   = i_size;
1234     p_new->i_color  = i_color;
1235     p_new->i_alpha  = i_alpha;
1236
1237     if( !*p_font )
1238     {
1239         *p_font = p_new;
1240     }
1241     else
1242     {
1243         font_stack_t *p_last;
1244
1245         for( p_last = *p_font;
1246              p_last->p_next;
1247              p_last = p_last->p_next )
1248         ;
1249
1250         p_last->p_next = p_new;
1251     }
1252     return VLC_SUCCESS;
1253 }
1254
1255 static int PopFont( font_stack_t **p_font )
1256 {
1257     font_stack_t *p_last, *p_next_to_last;
1258
1259     if( !p_font || !*p_font )
1260         return VLC_EGENERIC;
1261
1262     p_next_to_last = NULL;
1263     for( p_last = *p_font;
1264          p_last->p_next;
1265          p_last = p_last->p_next )
1266     {
1267         p_next_to_last = p_last;
1268     }
1269
1270     if( p_next_to_last )
1271         p_next_to_last->p_next = NULL;
1272     else
1273         *p_font = NULL;
1274
1275     free( p_last->psz_name );
1276     free( p_last );
1277
1278     return VLC_SUCCESS;
1279 }
1280
1281 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1282                      int *i_color, int *i_alpha )
1283 {
1284     font_stack_t *p_last;
1285
1286     if( !p_font || !*p_font )
1287         return VLC_EGENERIC;
1288
1289     for( p_last=*p_font;
1290          p_last->p_next;
1291          p_last=p_last->p_next )
1292     ;
1293
1294     *psz_name = p_last->psz_name;
1295     *i_size   = p_last->i_size;
1296     *i_color  = p_last->i_color;
1297     *i_alpha  = p_last->i_alpha;
1298
1299     return VLC_SUCCESS;
1300 }
1301
1302 static void IconvText( filter_t *p_filter, const char *psz_string,
1303                        uint32_t *i_string_length, uint32_t **ppsz_unicode )
1304 {
1305     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1306
1307     /* If memory hasn't been allocated for our output string, allocate it here
1308      * - the calling function must now be responsible for freeing it.
1309      */
1310     if( !*ppsz_unicode )
1311         *ppsz_unicode = (uint32_t *)
1312             malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1313
1314     /* We don't need to handle a NULL pointer in *ppsz_unicode
1315      * if we are instead testing for a non NULL value like we are here */
1316
1317     if( *ppsz_unicode )
1318     {
1319 #if defined(WORDS_BIGENDIAN)
1320         iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1321 #else
1322         iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1323 #endif
1324         if( iconv_handle != (vlc_iconv_t)-1 )
1325         {
1326             char *p_in_buffer, *p_out_buffer;
1327             size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1328             i_in_bytes = strlen( psz_string );
1329             i_out_bytes = i_in_bytes * sizeof( uint32_t );
1330             i_out_bytes_left = i_out_bytes;
1331             p_in_buffer = (char *) psz_string;
1332             p_out_buffer = (char *) *ppsz_unicode;
1333             i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1334                     &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1335
1336             vlc_iconv_close( iconv_handle );
1337
1338             if( i_in_bytes )
1339             {
1340                 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1341                           "bytes left %d", strerror(errno), (int)i_in_bytes );
1342             }
1343             else
1344             {
1345                 *(uint32_t*)p_out_buffer = 0;
1346                 *i_string_length =
1347                     (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1348             }
1349         }
1350         else
1351         {
1352             msg_Warn( p_filter, "unable to do conversion" );
1353         }
1354     }
1355 }
1356
1357 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1358         font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic, 
1359         vlc_bool_t b_uline )
1360 {
1361     ft_style_t   *p_style = NULL;
1362
1363     char  *psz_fontname = NULL;
1364     int    i_font_color = p_sys->i_font_color;
1365     int    i_font_alpha = 0;
1366     int    i_font_size  = p_sys->i_font_size;
1367
1368     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1369                                  &i_font_color, &i_font_alpha ) )
1370     {
1371         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1372                 i_font_alpha, b_bold, b_italic, b_uline );
1373     }
1374     return p_style;
1375 }
1376
1377 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1378                       vlc_bool_t b_uline, line_desc_t *p_line,
1379                       uint32_t *psz_unicode, int *pi_pen_x, int i_pen_y,
1380                       int *pi_start, FT_Vector *p_result )
1381 {
1382     FT_BBox      line;
1383     int          i_yMin, i_yMax;
1384     int          i;
1385     vlc_bool_t   b_first_on_line = VLC_TRUE;
1386
1387     int          i_previous = 0;
1388     int          i_pen_x_start = *pi_pen_x;
1389
1390     uint32_t *psz_unicode_start = psz_unicode;
1391
1392     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1393
1394     /* Account for part of line already in position */
1395     for( i=0; i<*pi_start; i++ )
1396     {
1397         FT_BBox glyph_size;
1398
1399         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1400                             ft_glyph_bbox_pixels, &glyph_size );
1401
1402         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1403             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1404         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1405         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1406     }
1407     i_yMin = line.yMin;
1408     i_yMax = line.yMax;
1409
1410     if( line.xMax > 0 )
1411         b_first_on_line = VLC_FALSE;
1412
1413     while( *psz_unicode && ( *psz_unicode != '\n' ) )
1414     {
1415         FT_BBox glyph_size;
1416         FT_Glyph tmp_glyph;
1417         int i_error;
1418
1419         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1420         if( FT_HAS_KERNING( p_face ) && i_glyph_index
1421             && i_previous )
1422         {
1423             FT_Vector delta;
1424             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1425                             ft_kerning_default, &delta );
1426             *pi_pen_x += delta.x >> 6;
1427         }
1428         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1429         p_line->p_glyph_pos[ i ].y = i_pen_y;
1430
1431         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1432         if( i_error )
1433         {
1434             msg_Err( p_filter,
1435                    "unable to render text FT_Load_Glyph returned %d", i_error );
1436             p_line->pp_glyphs[ i ] = NULL;
1437             return VLC_EGENERIC;
1438         }
1439         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1440         if( i_error )
1441         {
1442             msg_Err( p_filter,
1443                     "unable to render text FT_Get_Glyph returned %d", i_error );
1444             p_line->pp_glyphs[ i ] = NULL;
1445             return VLC_EGENERIC;
1446         }
1447         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1448         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1449         if( i_error )
1450         {
1451             FT_Done_Glyph( tmp_glyph );
1452             continue;
1453         }
1454         if( b_uline )
1455         {
1456             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1457                                                p_face->size->metrics.y_scale));
1458             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1459                                             p_face->size->metrics.y_scale));
1460
1461             p_line->pi_underline_offset[ i ]  =
1462                                        ( aOffset < 0 ) ? -aOffset : aOffset;
1463             p_line->pi_underline_thickness[ i ] =
1464                                        ( aSize < 0 ) ? -aSize   : aSize;
1465         }
1466         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1467         p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1468
1469         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1470                     glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1471         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1472         {
1473             while( --i > *pi_start )
1474             {
1475                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1476             }
1477
1478             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1479             {
1480                 psz_unicode--;
1481             }
1482             if( psz_unicode == psz_unicode_start )
1483             {
1484                 if( b_first_on_line )
1485                 {
1486                     msg_Warn( p_filter, "unbreakable string" );
1487                     p_line->pp_glyphs[ i ] = NULL;
1488                     return VLC_EGENERIC;
1489                 }
1490                 *pi_pen_x = i_pen_x_start;
1491
1492                 p_line->i_width = line.xMax;
1493                 p_line->i_height = __MAX( p_line->i_height,
1494                                           p_face->size->metrics.height >> 6 );
1495                 p_line->pp_glyphs[ i ] = NULL;
1496
1497                 p_result->x = __MAX( p_result->x, line.xMax );
1498                 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1499                                                          i_yMax - i_yMin ) );
1500
1501                 *pi_start = i;
1502                 return VLC_SUCCESS;
1503             }
1504             else
1505             {
1506                 *psz_unicode = '\n';
1507             }
1508             psz_unicode = psz_unicode_start;
1509             *pi_pen_x = i_pen_x_start;
1510             i_previous = 0;
1511
1512             line.yMax = i_yMax;
1513             line.yMin = i_yMin;
1514
1515             continue;
1516         }
1517         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1518         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1519
1520         i_previous = i_glyph_index;
1521         *pi_pen_x += p_face->glyph->advance.x >> 6;
1522         i++;
1523     }
1524     p_line->i_width = line.xMax;
1525     p_line->i_height = __MAX( p_line->i_height,
1526                               p_face->size->metrics.height >> 6 );
1527     p_line->pp_glyphs[ i ] = NULL;
1528
1529     p_result->x = __MAX( p_result->x, line.xMax );
1530     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1531                          line.yMax - line.yMin ) );
1532
1533     *pi_start = i;
1534
1535     /* Get rid of any text processed - if necessary repositioning
1536      * at the start of a new line of text
1537      */
1538     if( !*psz_unicode )
1539     {
1540         *psz_unicode_start = '\0';
1541     }
1542     else
1543     {
1544         psz_unicode++;
1545         for( i=0; psz_unicode[ i ]; i++ )
1546             psz_unicode_start[ i ] = psz_unicode[ i ];
1547         psz_unicode_start[ i ] = '\0';
1548     }
1549
1550     return VLC_SUCCESS;
1551 }
1552
1553 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1554                                   font_stack_t **p_fonts )
1555 {
1556     int   rv;
1557     char *psz_fontname = NULL;
1558     int   i_font_color = 0xffffff;
1559     int   i_font_alpha = 0;
1560     int   i_font_size  = 24;
1561
1562     /* Default all attributes to the top font in the stack -- in case not
1563      * all attributes are specified in the sub-font
1564      */
1565     if( VLC_SUCCESS == PeekFont( p_fonts,
1566                                  &psz_fontname,
1567                                  &i_font_size,
1568                                  &i_font_color,
1569                                  &i_font_alpha ))
1570     {
1571         psz_fontname = strdup( psz_fontname );
1572     }
1573
1574     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1575     {
1576         char *psz_name = xml_ReaderName ( p_xml_reader );
1577         char *psz_value = xml_ReaderValue ( p_xml_reader );
1578
1579         if( psz_name && psz_value )
1580         {
1581             if( !strcasecmp( "face", psz_name ) )
1582             {
1583                 if( psz_fontname ) free( psz_fontname );
1584                 psz_fontname = strdup( psz_value );
1585             }
1586             else if( !strcasecmp( "size", psz_name ) )
1587             {
1588                 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1589                 {
1590                     int i_value = atoi( psz_value );
1591
1592                     if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1593                         i_font_size += ( i_value * i_font_size ) / 10;
1594                     else if( i_value < -5 )
1595                         i_font_size = - i_value;
1596                     else if( i_value > 5 )
1597                         i_font_size = i_value;
1598                 }
1599                 else
1600                     i_font_size = atoi( psz_value );
1601             }
1602             else if( !strcasecmp( "color", psz_name )  &&
1603                      ( psz_value[0] == '#' ) )
1604             {
1605                 i_font_color = strtol( psz_value + 1, NULL, 16 );
1606                 i_font_color &= 0x00ffffff;
1607             }
1608             else if( !strcasecmp( "alpha", psz_name ) &&
1609                      ( psz_value[0] == '#' ) )
1610             {
1611                 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1612                 i_font_alpha &= 0xff;
1613             }
1614             free( psz_name );
1615             free( psz_value );
1616         }
1617     }
1618     rv = PushFont( p_fonts,
1619                    psz_fontname,
1620                    i_font_size,
1621                    i_font_color,
1622                    i_font_alpha );
1623
1624     free( psz_fontname );
1625
1626     return rv;
1627 }
1628
1629 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1630                        uint32_t **psz_text_out, uint32_t *pi_runs,
1631                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1632                        ft_style_t *p_style )
1633 {
1634     uint32_t      i_string_length = 0;
1635
1636     IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1637     *psz_text_out += i_string_length;
1638
1639     if( ppp_styles && ppi_run_lengths )
1640     {
1641         (*pi_runs)++;
1642
1643         if( *ppp_styles )
1644         {
1645             *ppp_styles = (ft_style_t **)
1646                 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1647         }
1648         else if( *pi_runs == 1 )
1649         {
1650             *ppp_styles = (ft_style_t **)
1651                 malloc( *pi_runs * sizeof( ft_style_t * ) );
1652         }
1653
1654         /* We have just malloc'ed this memory successfully -
1655          * *pi_runs HAS to be within the memory area of *ppp_styles */
1656         if( *ppp_styles )
1657         {
1658             (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1659             p_style = NULL;
1660         }
1661
1662         if( *ppi_run_lengths )
1663         {
1664             *ppi_run_lengths = (uint32_t *)
1665                 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1666         }
1667         else if( *pi_runs == 1 )
1668         {
1669             *ppi_run_lengths = (uint32_t *)
1670                 malloc( *pi_runs * sizeof( uint32_t ) );
1671         }
1672
1673         /* same remarks here */
1674         if( *ppi_run_lengths )
1675         {
1676             (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1677         }
1678     }
1679     /* If we couldn't use the p_style argument due to memory allocation
1680      * problems above, release it here.
1681      */
1682     if( p_style ) DeleteStyle( p_style );
1683 }
1684
1685 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader,
1686                          text_style_t *p_font_style, uint32_t *psz_text,
1687                          int *pi_len, uint32_t *pi_runs,
1688                          uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles)
1689 {
1690     int           rv             = VLC_SUCCESS;
1691     filter_sys_t *p_sys          = p_filter->p_sys;
1692     uint32_t     *psz_text_orig  = psz_text;
1693     font_stack_t *p_fonts        = NULL;
1694
1695     char *psz_node  = NULL;
1696
1697     vlc_bool_t b_italic = VLC_FALSE;
1698     vlc_bool_t b_bold   = VLC_FALSE;
1699     vlc_bool_t b_uline  = VLC_FALSE;
1700
1701     if( p_font_style )
1702     {
1703         rv = PushFont( &p_fonts,
1704                        p_font_style->psz_fontname,
1705                        p_font_style->i_font_size,
1706                        p_font_style->i_font_color,
1707                        p_font_style->i_font_alpha );
1708
1709         if( p_font_style->i_style_flags & STYLE_BOLD )
1710             b_bold = VLC_TRUE;
1711         if( p_font_style->i_style_flags & STYLE_ITALIC )
1712             b_italic = VLC_TRUE;
1713         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1714             b_uline = VLC_TRUE;
1715     }
1716     else
1717     {
1718         rv = PushFont( &p_fonts,
1719                        FC_DEFAULT_FONT,
1720                        p_sys->i_font_size,
1721                        0xffffff,
1722                        0 );
1723     }
1724     if( rv != VLC_SUCCESS )
1725         return rv;
1726
1727     while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1728     {
1729         switch ( xml_ReaderNodeType( p_xml_reader ) )
1730         {
1731             case XML_READER_NONE:
1732                 break;
1733             case XML_READER_ENDELEM:
1734                 psz_node = xml_ReaderName( p_xml_reader );
1735
1736                 if( psz_node )
1737                 {
1738                     if( !strcasecmp( "font", psz_node ) )
1739                         PopFont( &p_fonts );
1740                     else if( !strcasecmp( "b", psz_node ) )
1741                         b_bold   = VLC_FALSE;
1742                     else if( !strcasecmp( "i", psz_node ) )
1743                         b_italic = VLC_FALSE;
1744                     else if( !strcasecmp( "u", psz_node ) )
1745                         b_uline  = VLC_FALSE;
1746
1747                     free( psz_node );
1748                 }
1749                 break;
1750             case XML_READER_STARTELEM:
1751                 psz_node = xml_ReaderName( p_xml_reader );
1752                 if( psz_node )
1753                 {
1754                     if( !strcasecmp( "font", psz_node ) )
1755                         rv = HandleFontAttributes( p_xml_reader, &p_fonts );
1756                     else if( !strcasecmp( "b", psz_node ) )
1757                         b_bold = VLC_TRUE;
1758                     else if( !strcasecmp( "i", psz_node ) )
1759                         b_italic = VLC_TRUE;
1760                     else if( !strcasecmp( "u", psz_node ) )
1761                         b_uline = VLC_TRUE;
1762                     else if( !strcasecmp( "br", psz_node ) )
1763                     {
1764                         SetupLine( p_filter, "\n", &psz_text,
1765                                    pi_runs, ppi_run_lengths, ppp_styles,
1766                                    GetStyleFromFontStack( p_sys,
1767                                                           &p_fonts,
1768                                                           b_bold,
1769                                                           b_italic,
1770                                                           b_uline ) );
1771                     }
1772
1773                     free( psz_node );
1774                 }
1775                 break;
1776             case XML_READER_TEXT:
1777                 psz_node = xml_ReaderValue( p_xml_reader );
1778                 if( psz_node )
1779                 {
1780                     /* Turn any multiple-whitespaces into single spaces */
1781                     char *s = strpbrk( psz_node, "\t\r\n " );
1782                     while( s )
1783                     {
1784                         int i_whitespace = strspn( s, "\t\r\n " );
1785
1786                         if( i_whitespace > 1 )
1787                             memmove( s + 1,
1788                                      s + i_whitespace,
1789                                      strlen( s ) - i_whitespace + 1 );
1790                         *s = ' ';
1791
1792                         s = strpbrk( s+1, "\t\r\n " );
1793                     }
1794                     SetupLine( p_filter, psz_node, &psz_text,
1795                                pi_runs, ppi_run_lengths, ppp_styles,
1796                                GetStyleFromFontStack( p_sys,
1797                                                       &p_fonts,
1798                                                       b_bold,
1799                                                       b_italic,
1800                                                       b_uline ) );
1801                     free( psz_node );
1802                 }
1803                 break;
1804         }
1805         if( rv != VLC_SUCCESS )
1806         {
1807             psz_text = psz_text_orig;
1808             break;
1809         }
1810     }
1811
1812     *pi_len = psz_text - psz_text_orig;
1813
1814     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1815
1816     return rv;
1817 }
1818
1819 static int ProcessLines( filter_t *p_filter, uint32_t *psz_text,
1820                          int i_len, uint32_t i_runs,
1821                          uint32_t *pi_run_lengths, ft_style_t **pp_styles,
1822                          line_desc_t **pp_lines, FT_Vector *p_result )
1823 {
1824     filter_sys_t   *p_sys = p_filter->p_sys;
1825     ft_style_t     **pp_char_styles;
1826     uint32_t        i, j, k;
1827     int             i_prev;
1828
1829     /* Assign each character in the text string its style explicitly, so that
1830      * after the characters have been shuffled around by Fribidi, we can re-apply
1831      * the styles, and to simplify the calculation of runs within a line.
1832      */
1833     pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1834     if( !pp_char_styles )
1835         return VLC_ENOMEM;
1836
1837     i = 0;
1838     for( j = 0; j < i_runs; j++ )
1839         for( k = 0; k < pi_run_lengths[ j ]; k++ )
1840             pp_char_styles[ i++ ] = pp_styles[ j ];
1841
1842 #if defined(HAVE_FRIBIDI)
1843     {
1844         ft_style_t  **pp_char_styles_new;
1845         int         *p_positions;
1846         uint32_t    *p_fribidi_string;
1847         int start_pos, pos = 0;
1848
1849         p_fribidi_string = malloc( (i_len + 1) * sizeof(uint32_t) );
1850         if(! p_fribidi_string )
1851         {
1852             msg_Err( p_filter, "out of memory" );
1853             free( pp_char_styles );
1854             return VLC_ENOMEM;
1855         }
1856         pp_char_styles_new  = (ft_style_t **)
1857                                        malloc( i_len * sizeof( ft_style_t * ));
1858         if(! pp_char_styles_new )
1859         {
1860             msg_Err( p_filter, "out of memory" );
1861             free( p_fribidi_string );
1862             free( pp_char_styles );
1863             return VLC_ENOMEM;
1864         }
1865         p_positions = (int *) malloc( (i_len + 1) * sizeof( int ) );
1866         if(! p_positions )
1867         {
1868             msg_Err( p_filter, "out of memory" );
1869             free( pp_char_styles_new );
1870             free( p_fribidi_string );
1871             free( pp_char_styles );
1872             return VLC_ENOMEM;
1873         }
1874
1875         /* Do bidi conversion line-by-line */
1876         while(pos < i_len)
1877         {
1878             while(pos < i_len) {
1879                 if (psz_text[pos] != '\n')
1880                     break;
1881                 p_fribidi_string[pos] = psz_text[pos];
1882                 pp_char_styles_new[pos] = pp_char_styles[pos];
1883                 ++pos;
1884             }
1885             start_pos = pos;
1886             while(pos < i_len) {
1887                 if (psz_text[pos] == '\n')
1888                     break;
1889                 ++pos;
1890             }
1891             if (pos > start_pos)
1892             {
1893                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1894                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1895                         pos - start_pos, &base_dir,
1896                         (FriBidiChar*)p_fribidi_string + start_pos,
1897                         0,
1898                         p_positions, 0);
1899
1900                 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1901                 {
1902                     pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1903                                                 p_positions[ j - start_pos ] ];
1904                 }
1905             }
1906         }
1907         free( p_positions );
1908         free( pp_char_styles );
1909         pp_char_styles = pp_char_styles_new;
1910         psz_text = p_fribidi_string;
1911         p_fribidi_string[ i_len ] = 0;
1912     }
1913 #endif
1914     FT_Vector tmp_result;
1915
1916     line_desc_t *p_line = NULL;
1917     line_desc_t *p_prev = NULL;
1918
1919     int i_pen_x = 0;
1920     int i_pen_y = 0;
1921     int i_posn  = 0;
1922
1923     p_result->x = p_result->y = 0;
1924     tmp_result.x = tmp_result.y = 0;
1925
1926     i_prev = 0;
1927     for( k = 0; k <= (uint32_t) i_len; k++ )
1928     {
1929         if( ( k == (uint32_t) i_len ) ||
1930           ( ( k > 0 ) &&
1931             !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1932         {
1933             ft_style_t *p_style = pp_char_styles[ k - 1 ];
1934
1935             /* End of the current style run */
1936             FT_Face p_face = NULL;
1937             int      i_idx = 0;
1938             char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
1939                                                     p_style->psz_fontname,
1940                                                     p_style->b_bold,
1941                                                     p_style->b_italic,
1942                                                     &i_idx );
1943             if( psz_fontfile )
1944             {
1945                 if( FT_New_Face( p_sys->p_library,
1946                             psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
1947                 {
1948                     free( psz_fontfile );
1949                     free( pp_char_styles );
1950 #if defined(HAVE_FRIBIDI)
1951                     free( psz_text );
1952 #endif
1953                     return VLC_EGENERIC;
1954                 }
1955                 free( psz_fontfile );
1956             }
1957
1958             if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1959                         ft_encoding_unicode ) ||
1960                 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1961                     p_style->i_font_size ) )
1962             {
1963                 if( p_face ) FT_Done_Face( p_face );
1964                 free( pp_char_styles );
1965 #if defined(HAVE_FRIBIDI)
1966                 free( psz_text );
1967 #endif
1968                 return VLC_EGENERIC;
1969             }
1970             p_sys->i_use_kerning =
1971                         FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
1972
1973
1974             uint32_t *psz_unicode = (uint32_t *)
1975                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1976             if( !psz_unicode )
1977             {
1978                 msg_Err( p_filter, "out of memory" );
1979                 if( p_face ) FT_Done_Face( p_face );
1980                 free( pp_char_styles );
1981                 free( psz_unicode );
1982 #if defined(HAVE_FRIBIDI)
1983                 free( psz_text );
1984 #endif
1985                 return VLC_ENOMEM;
1986             }
1987             memcpy( psz_unicode, psz_text + i_prev,
1988                                         sizeof( uint32_t ) * ( k - i_prev ) );
1989             psz_unicode[ k - i_prev ] = 0;
1990             while( *psz_unicode )
1991             {
1992                 if( !p_line )
1993                 {
1994                     if( !(p_line = NewLine( i_len - i_prev)) )
1995                     {
1996                         msg_Err( p_filter, "out of memory" );
1997                         if( p_face ) FT_Done_Face( p_face );
1998                         free( pp_char_styles );
1999                         free( psz_unicode );
2000 #if defined(HAVE_FRIBIDI)
2001                         free( psz_text );
2002 #endif
2003                         return VLC_ENOMEM;
2004                     }
2005                     /* New Color mode only works in YUVA rendering mode --
2006                      * (RGB mode has palette constraints on it). We therefore
2007                      * need to populate the legacy colour fields also.
2008                      */
2009                     p_line->b_new_color_mode = VLC_TRUE;
2010                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2011                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2012                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
2013                     p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
2014                     p_line->p_next = NULL;
2015                     i_pen_x = 0;
2016                     i_pen_y += tmp_result.y;
2017                     tmp_result.x = 0;
2018                     tmp_result.y = 0;
2019                     i_posn = 0;
2020                     if( p_prev ) p_prev->p_next = p_line;
2021                     else *pp_lines = p_line;
2022                 }
2023                 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2024                                p_style->i_font_color, p_style->b_underline,
2025                                p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2026                                &tmp_result ) != VLC_SUCCESS )
2027                 {
2028                     if( p_face ) FT_Done_Face( p_face );
2029                     free( pp_char_styles );
2030                     free( psz_unicode );
2031 #if defined(HAVE_FRIBIDI)
2032                     free( psz_text );
2033 #endif
2034                     return VLC_EGENERIC;
2035                 }
2036                 if( *psz_unicode )
2037                 {
2038                     p_result->x = __MAX( p_result->x, tmp_result.x );
2039                     p_result->y += tmp_result.y;
2040
2041                     p_prev = p_line;
2042                     p_line = NULL;
2043                 }
2044             }
2045             free( psz_unicode );
2046             if( p_face ) FT_Done_Face( p_face );
2047             i_prev = k;
2048         }
2049     }
2050     free( pp_char_styles );
2051 #if defined(HAVE_FRIBIDI)
2052     free( psz_text );
2053 #endif
2054
2055     if( p_line )
2056     {
2057         p_result->x = __MAX( p_result->x, tmp_result.x );
2058         p_result->y += tmp_result.y;
2059     }
2060     return VLC_SUCCESS;
2061 }
2062
2063 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2064                        subpicture_region_t *p_region_in )
2065 {
2066     int          rv = VLC_SUCCESS;
2067     stream_t     *p_sub = NULL;
2068     xml_t        *p_xml = NULL;
2069     xml_reader_t *p_xml_reader = NULL;
2070
2071     if( !p_region_in || !p_region_in->psz_html )
2072         return VLC_EGENERIC;
2073
2074     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2075                               (uint8_t *) p_region_in->psz_html,
2076                               strlen( p_region_in->psz_html ),
2077                               VLC_TRUE );
2078     if( p_sub )
2079     {
2080         p_xml = xml_Create( p_filter );
2081         if( p_xml )
2082         {
2083             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2084             if( p_xml_reader )
2085             {
2086                 uint32_t   *psz_text;
2087                 int         i_len;
2088                 uint32_t    i_runs = 0;
2089                 uint32_t   *pi_run_lengths = NULL;
2090                 ft_style_t  **pp_styles = NULL;
2091                 FT_Vector    result;
2092                 line_desc_t  *p_lines = NULL;
2093
2094                 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2095                                                 sizeof( uint32_t ) );
2096                 if( psz_text )
2097                 {
2098                     uint32_t k;
2099
2100                     rv = ProcessNodes( p_filter, p_xml_reader,
2101                                   p_region_in->p_style, psz_text, &i_len,
2102                                   &i_runs, &pi_run_lengths, &pp_styles );
2103
2104                     p_region_out->i_x = p_region_in->i_x;
2105                     p_region_out->i_y = p_region_in->i_y;
2106
2107                     if( rv == VLC_SUCCESS )
2108                     {
2109                         rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2110                                 pi_run_lengths, pp_styles, &p_lines, &result );
2111                     }
2112
2113                     for( k=0; k<i_runs; k++)
2114                         DeleteStyle( pp_styles[k] );
2115                     free( pp_styles );
2116                     free( pi_run_lengths );
2117                     free( psz_text );
2118
2119                     /* Don't attempt to render text that couldn't be layed out
2120                      * properly.
2121                      */
2122                     if( rv == VLC_SUCCESS )
2123                     {
2124                         if( config_GetInt( p_filter, "freetype-yuvp" ) )
2125                         {
2126                             Render( p_filter, p_region_out, p_lines,
2127                                     result.x, result.y );
2128                         }
2129                         else
2130                         {
2131                             RenderYUVA( p_filter, p_region_out, p_lines,
2132                                     result.x, result.y );
2133                         }
2134                     }
2135                 }
2136                 FreeLines( p_lines );
2137
2138                 xml_ReaderDelete( p_xml, p_xml_reader );
2139             }
2140             xml_Delete( p_xml );
2141         }
2142         stream_Delete( p_sub );
2143     }
2144
2145     return rv;
2146 }
2147
2148 static char* FontConfig_Select( FcConfig* priv, const char* family,
2149                           vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2150 {
2151     FcResult result;
2152     FcPattern *pat, *p_pat;
2153     FcChar8* val_s;
2154     FcBool val_b;
2155
2156     pat = FcPatternCreate();
2157     if (!pat) return NULL;
2158
2159     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2160     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2161     FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2162     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2163
2164     FcDefaultSubstitute( pat );
2165
2166     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2167     {
2168         FcPatternDestroy( pat );
2169         return NULL;
2170     }
2171
2172     p_pat = FcFontMatch( priv, pat, &result );
2173     FcPatternDestroy( pat );
2174     if( !p_pat ) return NULL;
2175
2176     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2177         || ( val_b != FcTrue ) )
2178     {
2179         FcPatternDestroy( p_pat );
2180         return NULL;
2181     }
2182     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2183     {
2184         *i_idx = 0;
2185     }
2186
2187     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2188     {
2189         FcPatternDestroy( p_pat );
2190         return NULL;
2191     }
2192
2193     /*
2194     if( strcasecmp((const char*)val_s, family ) != 0 )
2195         msg_Warn( p_filter, "fontconfig: selected font family is not"
2196                             "the requested one: '%s' != '%s'\n",
2197                             (const char*)val_s, family );
2198     */
2199
2200     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2201     {
2202         FcPatternDestroy( p_pat );
2203         return NULL;
2204     }
2205
2206     FcPatternDestroy( p_pat );
2207     return strdup( (const char*)val_s );
2208 }
2209 #endif
2210
2211 static void FreeLine( line_desc_t *p_line )
2212 {
2213     unsigned int i;
2214     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2215     {
2216         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2217     }
2218     free( p_line->pp_glyphs );
2219     free( p_line->p_glyph_pos );
2220     free( p_line->p_rgb );
2221     free( p_line->pi_underline_offset );
2222     free( p_line->pi_underline_thickness );
2223     free( p_line );
2224 }
2225
2226 static void FreeLines( line_desc_t *p_lines )
2227 {
2228     line_desc_t *p_line, *p_next;
2229
2230     if( !p_lines ) return;
2231
2232     for( p_line = p_lines; p_line != NULL; p_line = p_next )
2233     {
2234         p_next = p_line->p_next;
2235         FreeLine( p_line );
2236     }
2237 }
2238
2239 static line_desc_t *NewLine( int i_count )
2240 {
2241     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2242
2243     if( !p_line ) return NULL;
2244     p_line->i_height = 0;
2245     p_line->i_width = 0;
2246     p_line->p_next = NULL;
2247
2248     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2249     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2250     p_line->p_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2251     p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2252     p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2253     if( ( p_line->pp_glyphs == NULL ) ||
2254         ( p_line->p_glyph_pos == NULL ) ||
2255         ( p_line->p_rgb == NULL ) ||
2256         ( p_line->pi_underline_offset == NULL ) ||
2257         ( p_line->pi_underline_thickness == NULL ) )
2258     {
2259         if( p_line->pi_underline_thickness )
2260             free( p_line->pi_underline_thickness );
2261         if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2262         if( p_line->p_rgb ) free( p_line->p_rgb );
2263         if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2264         if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2265         free( p_line );
2266         return NULL;
2267     }
2268     p_line->pp_glyphs[0] = NULL;
2269     p_line->b_new_color_mode = VLC_FALSE;
2270
2271     return p_line;
2272 }
2273
2274 static int SetFontSize( filter_t *p_filter, int i_size )
2275 {
2276     filter_sys_t *p_sys = p_filter->p_sys;
2277
2278     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
2279
2280     if( !i_size )
2281     {
2282         vlc_value_t val;
2283
2284         if( !p_sys->i_default_font_size &&
2285             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
2286             return VLC_SUCCESS;
2287
2288         if( p_sys->i_default_font_size )
2289         {
2290             i_size = p_sys->i_default_font_size;
2291         }
2292         else
2293         {
2294             var_Get( p_filter, "freetype-rel-fontsize", &val );
2295             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2296             p_filter->p_sys->i_display_height =
2297                 p_filter->fmt_out.video.i_height;
2298         }
2299         if( i_size <= 0 )
2300         {
2301             msg_Warn( p_filter, "invalid fontsize, using 12" );
2302             i_size = 12;
2303         }
2304
2305         msg_Dbg( p_filter, "using fontsize: %i", i_size );
2306     }
2307
2308     p_sys->i_font_size = i_size;
2309
2310     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2311     {
2312         msg_Err( p_filter, "couldn't set font size to %d", i_size );
2313         return VLC_EGENERIC;
2314     }
2315
2316     return VLC_SUCCESS;
2317 }