]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
* [PATCH] Memory hole fix by Bernie Purcell
[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( byte_t * );
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 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
222 static void FreeLines( line_desc_t * );
223 static void FreeLine( line_desc_t * );
224
225 /*****************************************************************************
226  * filter_sys_t: freetype local data
227  *****************************************************************************
228  * This structure is part of the video output thread descriptor.
229  * It describes the freetype specific properties of an output thread.
230  *****************************************************************************/
231 struct filter_sys_t
232 {
233     FT_Library     p_library;   /* handle to library     */
234     FT_Face        p_face;      /* handle to face object */
235     vlc_bool_t     i_use_kerning;
236     uint8_t        i_font_opacity;
237     int            i_font_color;
238     int            i_font_size;
239     int            i_effect;
240
241     int            i_default_font_size;
242     int            i_display_height;
243 #ifdef HAVE_FONTCONFIG
244     FcConfig      *p_fontconfig;
245 #endif
246 };
247
248 /*****************************************************************************
249  * Create: allocates osd-text video thread output method
250  *****************************************************************************
251  * This function allocates and initializes a Clone vout method.
252  *****************************************************************************/
253 static int Create( vlc_object_t *p_this )
254 {
255     filter_t *p_filter = (filter_t *)p_this;
256     filter_sys_t *p_sys;
257     char *psz_fontfile = NULL;
258     int i_error;
259     vlc_value_t val;
260
261     /* Allocate structure */
262     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
263     if( !p_sys )
264     {
265         msg_Err( p_filter, "out of memory" );
266         return VLC_ENOMEM;
267     }
268     p_sys->p_face = 0;
269     p_sys->p_library = 0;
270     p_sys->i_font_size = 0;
271     p_sys->i_display_height = 0;
272
273     var_Create( p_filter, "freetype-font",
274                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
275     var_Create( p_filter, "freetype-fontsize",
276                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
277     var_Create( p_filter, "freetype-rel-fontsize",
278                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
279     var_Create( p_filter, "freetype-opacity",
280                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
281     var_Create( p_filter, "freetype-effect",
282                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
283     var_Get( p_filter, "freetype-opacity", &val );
284     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
285     var_Create( p_filter, "freetype-color",
286                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
287     var_Get( p_filter, "freetype-color", &val );
288     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
289     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
290
291     /* Look what method was requested */
292     var_Get( p_filter, "freetype-font", &val );
293     psz_fontfile = val.psz_string;
294     if( !psz_fontfile || !*psz_fontfile )
295     {
296         if( psz_fontfile ) free( psz_fontfile );
297         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
298 #ifdef WIN32
299         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
300         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
301 #elif defined(__APPLE__)
302         strcpy( psz_fontfile, DEFAULT_FONT );
303 #else
304         msg_Err( p_filter, "user didn't specify a font" );
305         goto error;
306 #endif
307     }
308
309 #ifdef HAVE_FONTCONFIG
310     if( FcInit() )
311         p_sys->p_fontconfig = FcConfigGetCurrent();
312     else
313         p_sys->p_fontconfig = NULL;
314 #endif
315     i_error = FT_Init_FreeType( &p_sys->p_library );
316     if( i_error )
317     {
318         msg_Err( p_filter, "couldn't initialize freetype" );
319         goto error;
320     }
321
322     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
323                            0, &p_sys->p_face );
324     if( i_error == FT_Err_Unknown_File_Format )
325     {
326         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
327         goto error;
328     }
329     else if( i_error )
330     {
331         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
332         goto error;
333     }
334
335     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
336     if( i_error )
337     {
338         msg_Err( p_filter, "font has no unicode translation table" );
339         goto error;
340     }
341
342     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
343
344     var_Get( p_filter, "freetype-fontsize", &val );
345     p_sys->i_default_font_size = val.i_int;
346     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
347
348     if( psz_fontfile ) free( psz_fontfile );
349     p_filter->pf_render_text = RenderText;
350 #ifdef HAVE_FONTCONFIG
351     p_filter->pf_render_html = RenderHtml;
352 #else
353     p_filter->pf_render_html = NULL;
354 #endif
355     return VLC_SUCCESS;
356
357  error:
358     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
359     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
360     if( psz_fontfile ) free( psz_fontfile );
361     free( p_sys );
362     return VLC_EGENERIC;
363 }
364
365 /*****************************************************************************
366  * Destroy: destroy Clone video thread output method
367  *****************************************************************************
368  * Clean up all data and library connections
369  *****************************************************************************/
370 static void Destroy( vlc_object_t *p_this )
371 {
372     filter_t *p_filter = (filter_t *)p_this;
373     filter_sys_t *p_sys = p_filter->p_sys;
374
375 #ifdef HAVE_FONTCONFIG
376     FcConfigDestroy( p_sys->p_fontconfig );
377     p_sys->p_fontconfig = NULL;
378     /* FcFini asserts calling the subfunction FcCacheFini()
379      * even if no other library functions have been made since FcInit(),
380      * so don't call it. */
381 #endif
382     FT_Done_Face( p_sys->p_face );
383     FT_Done_FreeType( p_sys->p_library );
384     free( p_sys );
385 }
386
387 /*****************************************************************************
388  * Render: place string in picture
389  *****************************************************************************
390  * This function merges the previously rendered freetype glyphs into a picture
391  *****************************************************************************/
392 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
393                    line_desc_t *p_line, int i_width, int i_height )
394 {
395     static uint8_t pi_gamma[16] =
396         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
397           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
398
399     uint8_t *p_dst;
400     video_format_t fmt;
401     int i, x, y, i_pitch;
402     uint8_t i_y; /* YUV values, derived from incoming RGB */
403     int8_t i_u, i_v;
404     subpicture_region_t *p_region_tmp;
405
406     /* Create a new subpicture region */
407     memset( &fmt, 0, sizeof(video_format_t) );
408     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
409     fmt.i_aspect = 0;
410     fmt.i_width = fmt.i_visible_width = i_width + 4;
411     fmt.i_height = fmt.i_visible_height = i_height + 4;
412     if( p_region->fmt.i_visible_width > 0 )
413         fmt.i_visible_width = p_region->fmt.i_visible_width;
414     if( p_region->fmt.i_visible_height > 0 )
415         fmt.i_visible_height = p_region->fmt.i_visible_height;
416     fmt.i_x_offset = fmt.i_y_offset = 0;
417     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
418     if( !p_region_tmp )
419     {
420         msg_Err( p_filter, "cannot allocate SPU region" );
421         return VLC_EGENERIC;
422     }
423
424     p_region->fmt = p_region_tmp->fmt;
425     p_region->picture = p_region_tmp->picture;
426     free( p_region_tmp );
427
428     /* Calculate text color components */
429     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
430                       25 * p_line->i_blue + 128) >> 8) +  16;
431     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
432                      112 * p_line->i_blue + 128) >> 8) + 128;
433     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
434                       18 * p_line->i_blue + 128) >> 8) + 128;
435
436     /* Build palette */
437     fmt.p_palette->i_entries = 16;
438     for( i = 0; i < 8; i++ )
439     {
440         fmt.p_palette->palette[i][0] = 0;
441         fmt.p_palette->palette[i][1] = 0x80;
442         fmt.p_palette->palette[i][2] = 0x80;
443         fmt.p_palette->palette[i][3] = pi_gamma[i];
444         fmt.p_palette->palette[i][3] =
445             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
446     }
447     for( i = 8; i < fmt.p_palette->i_entries; i++ )
448     {
449         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
450         fmt.p_palette->palette[i][1] = i_u;
451         fmt.p_palette->palette[i][2] = i_v;
452         fmt.p_palette->palette[i][3] = pi_gamma[i];
453         fmt.p_palette->palette[i][3] =
454             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
455     }
456
457     p_dst = p_region->picture.Y_PIXELS;
458     i_pitch = p_region->picture.Y_PITCH;
459
460     /* Initialize the region pixels */
461     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
462
463     for( ; p_line != NULL; p_line = p_line->p_next )
464     {
465         int i_glyph_tmax = 0;
466         int i_bitmap_offset, i_offset, i_align_offset = 0;
467         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
468         {
469             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
470             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
471         }
472
473         if( p_line->i_width < i_width )
474         {
475             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
476                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
477             {
478                 i_align_offset = i_width - p_line->i_width;
479             }
480             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
481                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
482             {
483                 i_align_offset = ( i_width - p_line->i_width ) / 2;
484             }
485         }
486
487         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
488         {
489             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
490
491             i_offset = ( p_line->p_glyph_pos[ i ].y +
492                 i_glyph_tmax - p_glyph->top + 2 ) *
493                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
494                 i_align_offset;
495
496             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
497             {
498                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
499                 {
500                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
501                         p_dst[i_offset+x] =
502                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
503                 }
504                 i_offset += i_pitch;
505             }
506         }
507     }
508
509     /* Outlining (find something better than nearest neighbour filtering ?) */
510     if( 1 )
511     {
512         uint8_t *p_dst = p_region->picture.Y_PIXELS;
513         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
514         uint8_t left, current;
515
516         for( y = 1; y < (int)fmt.i_height - 1; y++ )
517         {
518             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
519             p_dst += p_region->picture.Y_PITCH;
520             left = 0;
521
522             for( x = 1; x < (int)fmt.i_width - 1; x++ )
523             {
524                 current = p_dst[x];
525                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
526                              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;
527                 left = current;
528             }
529         }
530         memset( p_top, 0, fmt.i_width );
531     }
532
533     return VLC_SUCCESS;
534 }
535
536 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
537                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
538                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
539                                 int i_glyph_tmax, int i_align_offset,
540                                 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
541                                 subpicture_region_t *p_region)
542 {
543     int y, x, z;
544     int i_pitch;
545     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
546
547     p_dst_y = p_region->picture.Y_PIXELS;
548     p_dst_u = p_region->picture.U_PIXELS;
549     p_dst_v = p_region->picture.V_PIXELS;
550     p_dst_a = p_region->picture.A_PIXELS;
551     i_pitch = p_region->picture.A_PITCH;
552
553     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
554                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
555
556     for( y = 0; y < i_line_thickness; y++ )
557     {
558         int i_extra = p_this_glyph->bitmap.width;
559
560         if( b_ul_next_char )
561         {
562             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
563                       (p_this_glyph_pos->x + p_this_glyph->left);
564         }
565         for( x = 0; x < i_extra; x++ )
566         {
567             vlc_bool_t b_ok = VLC_TRUE;
568
569             /* break the underline around the tails of any glyphs which cross it */
570             for( z = x - i_line_thickness;
571                  z < x + i_line_thickness && b_ok;
572                  z++ )
573             {
574                 if( p_next_glyph && ( z >= i_extra ) )
575                 {
576                     int i_row = i_line_offset + p_next_glyph->top + y;
577
578                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
579                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
580                     {
581                         b_ok = VLC_FALSE;
582                     }
583                 }
584                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
585                 {
586                     int i_row = i_line_offset + p_this_glyph->top + y;
587
588                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
589                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
590                     {
591                         b_ok = VLC_FALSE;
592                     }
593                 }
594             }
595
596             if( b_ok )
597             {
598                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
599                 p_dst_u[i_offset+x] = i_u;
600                 p_dst_v[i_offset+x] = i_v;
601                 p_dst_a[i_offset+x] = 255;
602             }
603         }
604         i_offset += i_pitch;
605     }
606 }
607
608 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
609 {
610     uint8_t *p_dst = p_region->picture.A_PIXELS;
611     int i_pitch = p_region->picture.A_PITCH;
612     int x,y;
613
614     for( ; p_line != NULL; p_line = p_line->p_next )
615     {
616         int i_glyph_tmax=0, i = 0;
617         int i_bitmap_offset, i_offset, i_align_offset = 0;
618         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
619         {
620             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
621             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
622         }
623
624         if( p_line->i_width < i_width )
625         {
626             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
627                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
628             {
629                 i_align_offset = i_width - p_line->i_width;
630             }
631             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
632                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
633             {
634                 i_align_offset = ( i_width - p_line->i_width ) / 2;
635             }
636         }
637
638         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
639         {
640             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
641
642             i_offset = ( p_line->p_glyph_pos[ i ].y +
643                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
644                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
645                 i_align_offset +xoffset;
646
647             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
648             {
649                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
650                 {
651                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
652                         if( p_dst[i_offset+x] <
653                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
654                             p_dst[i_offset+x] =
655                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
656                 }
657                 i_offset += i_pitch;
658             }
659         }
660     }
661
662 }
663
664 /*****************************************************************************
665  * Render: place string in picture
666  *****************************************************************************
667  * This function merges the previously rendered freetype glyphs into a picture
668  *****************************************************************************/
669 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
670                    line_desc_t *p_line, int i_width, int i_height )
671 {
672     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
673     video_format_t fmt;
674     int i, x, y, i_pitch, i_alpha;
675     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
676     subpicture_region_t *p_region_tmp;
677
678     if( i_width == 0 || i_height == 0 )
679         return VLC_SUCCESS;
680
681     /* Create a new subpicture region */
682     memset( &fmt, 0, sizeof(video_format_t) );
683     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
684     fmt.i_aspect = 0;
685     fmt.i_width = fmt.i_visible_width = i_width + 6;
686     fmt.i_height = fmt.i_visible_height = i_height + 6;
687     if( p_region->fmt.i_visible_width > 0 )
688         fmt.i_visible_width = p_region->fmt.i_visible_width;
689     if( p_region->fmt.i_visible_height > 0 )
690         fmt.i_visible_height = p_region->fmt.i_visible_height;
691     fmt.i_x_offset = fmt.i_y_offset = 0;
692     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
693     if( !p_region_tmp )
694     {
695         msg_Err( p_filter, "cannot allocate SPU region" );
696         return VLC_EGENERIC;
697     }
698
699     p_region->fmt = p_region_tmp->fmt;
700     p_region->picture = p_region_tmp->picture;
701     free( p_region_tmp );
702
703     /* Calculate text color components */
704     i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red  + 4130 * p_line->i_green +
705                       802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
706     i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red  + -2384 * p_line->i_green +
707                      3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
708     i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
709                       -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
710     i_alpha = p_line->i_alpha;
711
712     p_dst_y = p_region->picture.Y_PIXELS;
713     p_dst_u = p_region->picture.U_PIXELS;
714     p_dst_v = p_region->picture.V_PIXELS;
715     p_dst_a = p_region->picture.A_PIXELS;
716     i_pitch = p_region->picture.A_PITCH;
717
718     /* Initialize the region pixels */
719     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
720     {
721         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
722         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
723         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
724         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
725     }
726     else
727     {
728         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
729         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
730         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
731         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
732     }
733     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
734         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
735     {
736         DrawBlack( p_line, i_width, p_region,  0,  0);
737         DrawBlack( p_line, i_width, p_region, -1,  0);
738         DrawBlack( p_line, i_width, p_region,  0, -1);
739         DrawBlack( p_line, i_width, p_region,  1,  0);
740         DrawBlack( p_line, i_width, p_region,  0,  1);
741     }
742
743     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
744     {
745         DrawBlack( p_line, i_width, p_region, -1, -1);
746         DrawBlack( p_line, i_width, p_region, -1,  1);
747         DrawBlack( p_line, i_width, p_region,  1, -1);
748         DrawBlack( p_line, i_width, p_region,  1,  1);
749
750         DrawBlack( p_line, i_width, p_region, -2,  0);
751         DrawBlack( p_line, i_width, p_region,  0, -2);
752         DrawBlack( p_line, i_width, p_region,  2,  0);
753         DrawBlack( p_line, i_width, p_region,  0,  2);
754
755         DrawBlack( p_line, i_width, p_region, -2, -2);
756         DrawBlack( p_line, i_width, p_region, -2,  2);
757         DrawBlack( p_line, i_width, p_region,  2, -2);
758         DrawBlack( p_line, i_width, p_region,  2,  2);
759
760         DrawBlack( p_line, i_width, p_region, -3,  0);
761         DrawBlack( p_line, i_width, p_region,  0, -3);
762         DrawBlack( p_line, i_width, p_region,  3,  0);
763         DrawBlack( p_line, i_width, p_region,  0,  3);
764     }
765
766     for( ; p_line != NULL; p_line = p_line->p_next )
767     {
768         int i_glyph_tmax = 0;
769         int i_bitmap_offset, i_offset, i_align_offset = 0;
770         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
771         {
772             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
773             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
774         }
775
776         if( p_line->i_width < i_width )
777         {
778             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
779                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
780             {
781                 i_align_offset = i_width - p_line->i_width;
782             }
783             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
784                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
785             {
786                 i_align_offset = ( i_width - p_line->i_width ) / 2;
787             }
788         }
789
790         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
791         {
792             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
793
794             i_offset = ( p_line->p_glyph_pos[ i ].y +
795                 i_glyph_tmax - p_glyph->top + 3 ) *
796                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
797                 i_align_offset;
798
799             if( p_line->b_new_color_mode )
800             {
801                 /* Every glyph can (and in fact must) have its own color */
802                 int i_red   = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
803                 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >>  8;
804                 int i_blue  = ( p_line->p_rgb[ i ] & 0x000000ff );
805
806                 i_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
807                                   802 * i_blue + 4096 + 131072 ) >> 13, 235);
808                 i_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
809                                  3598 * i_blue + 4096 + 1048576) >> 13, 240);
810                 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
811                                   -585 * i_blue + 4096 + 1048576) >> 13, 240);
812             }
813
814             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
815             {
816                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
817                 {
818                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
819                     {
820                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
821                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
822
823                         p_dst_u[i_offset+x] = i_u;
824                         p_dst_v[i_offset+x] = i_v;
825
826                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
827                             p_dst_a[i_offset+x] = 0xff;
828                     }
829                 }
830                 i_offset += i_pitch;
831             }
832
833             if( p_line->pi_underline_thickness[ i ] )
834             {
835                 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
836                                     p_line->pi_underline_offset[ i ],
837                                    (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
838                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
839                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
840                                     i_glyph_tmax, i_align_offset,
841                                     i_y, i_u, i_v, i_alpha,
842                                     p_region);
843             }
844         }
845     }
846
847     /* Apply the alpha setting */
848     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
849         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
850
851     return VLC_SUCCESS;
852 }
853
854 /**
855  * This function renders a text subpicture region into another one.
856  * It also calculates the size needed for this string, and renders the
857  * needed glyphs into memory. It is used as pf_add_string callback in
858  * the vout method by this module
859  */
860 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
861                        subpicture_region_t *p_region_in )
862 {
863     filter_sys_t *p_sys = p_filter->p_sys;
864     line_desc_t  *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
865     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
866     uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
867     int i_string_length;
868     char *psz_string;
869     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
870     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
871
872     FT_BBox line;
873     FT_BBox glyph_size;
874     FT_Vector result;
875     FT_Glyph tmp_glyph;
876
877     /* Sanity check */
878     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
879     psz_string = p_region_in->psz_text;
880     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
881
882     if( p_region_in->p_style )
883     {
884         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
885         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
886         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
887     }
888     else
889     {
890         i_font_color = p_sys->i_font_color;
891         i_font_alpha = 255 - p_sys->i_font_opacity;
892         i_font_size  = p_sys->i_default_font_size;
893     }
894
895     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
896     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
897     SetFontSize( p_filter, i_font_size );
898
899     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
900     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
901     i_blue  =   i_font_color & 0x000000FF;
902
903     result.x =  result.y = 0;
904     line.xMin = line.xMax = line.yMin = line.yMax = 0;
905
906     psz_unicode = psz_unicode_orig =
907         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
908     if( psz_unicode == NULL )
909     {
910         msg_Err( p_filter, "out of memory" );
911         goto error;
912     }
913 #if defined(WORDS_BIGENDIAN)
914     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
915 #else
916     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
917 #endif
918     if( iconv_handle == (vlc_iconv_t)-1 )
919     {
920         msg_Warn( p_filter, "unable to do conversion" );
921         goto error;
922     }
923
924     {
925         char *p_out_buffer;
926         const char *p_in_buffer = psz_string;
927         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
928         i_in_bytes = strlen( psz_string );
929         i_out_bytes = i_in_bytes * sizeof( uint32_t );
930         i_out_bytes_left = i_out_bytes;
931         p_out_buffer = (char *)psz_unicode;
932         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
933                            &p_out_buffer, &i_out_bytes_left );
934
935         vlc_iconv_close( iconv_handle );
936
937         if( i_in_bytes )
938         {
939             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
940                       "bytes left %d", strerror(errno), (int)i_in_bytes );
941             goto error;
942         }
943         *(uint32_t*)p_out_buffer = 0;
944         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
945     }
946
947 #if defined(HAVE_FRIBIDI)
948     {
949         uint32_t *p_fribidi_string;
950         int start_pos, pos = 0;
951
952         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
953
954         /* Do bidi conversion line-by-line */
955         while(pos < i_string_length)
956         {
957             while(pos < i_string_length) {
958                 i_char = psz_unicode[pos];
959                 if (i_char != '\r' && i_char != '\n')
960                     break;
961                 p_fribidi_string[pos] = i_char;
962                 ++pos;
963             }
964             start_pos = pos;
965             while(pos < i_string_length) {
966                 i_char = psz_unicode[pos];
967                 if (i_char == '\r' || i_char == '\n')
968                     break;
969                 ++pos;
970             }
971             if (pos > start_pos)
972             {
973                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
974                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
975                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
976             }
977         }
978
979         free( psz_unicode_orig );
980         psz_unicode = psz_unicode_orig = p_fribidi_string;
981         p_fribidi_string[ i_string_length ] = 0;
982     }
983 #endif
984
985     /* Calculate relative glyph positions and a bounding box for the
986      * entire string */
987     if( !(p_line = NewLine( (byte_t *)psz_string )) )
988     {
989         msg_Err( p_filter, "out of memory" );
990         goto error;
991     }
992     p_lines = p_line;
993     i_pen_x = i_pen_y = 0;
994     i_previous = i = 0;
995     psz_line_start = psz_unicode;
996
997 #define face p_sys->p_face
998 #define glyph face->glyph
999
1000     while( *psz_unicode )
1001     {
1002         i_char = *psz_unicode++;
1003         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1004         {
1005             continue;
1006         }
1007
1008         if( i_char == '\n' )
1009         {
1010             psz_line_start = psz_unicode;
1011             if( !(p_next = NewLine( (byte_t *)psz_string )) )
1012             {
1013                 msg_Err( p_filter, "out of memory" );
1014                 goto error;
1015             }
1016             p_line->p_next = p_next;
1017             p_line->i_width = line.xMax;
1018             p_line->i_height = face->size->metrics.height >> 6;
1019             p_line->pp_glyphs[ i ] = NULL;
1020             p_line->i_alpha = i_font_alpha;
1021             p_line->i_red = i_red;
1022             p_line->i_green = i_green;
1023             p_line->i_blue = i_blue;
1024             p_prev = p_line;
1025             p_line = p_next;
1026             result.x = __MAX( result.x, line.xMax );
1027             result.y += face->size->metrics.height >> 6;
1028             i_pen_x = 0;
1029             i_previous = i = 0;
1030             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1031             i_pen_y += face->size->metrics.height >> 6;
1032 #if 0
1033             msg_Dbg( p_filter, "Creating new line, i is %d", i );
1034 #endif
1035             continue;
1036         }
1037
1038         i_glyph_index = FT_Get_Char_Index( face, i_char );
1039         if( p_sys->i_use_kerning && i_glyph_index
1040             && i_previous )
1041         {
1042             FT_Vector delta;
1043             FT_Get_Kerning( face, i_previous, i_glyph_index,
1044                             ft_kerning_default, &delta );
1045             i_pen_x += delta.x >> 6;
1046
1047         }
1048         p_line->p_glyph_pos[ i ].x = i_pen_x;
1049         p_line->p_glyph_pos[ i ].y = i_pen_y;
1050         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1051         if( i_error )
1052         {
1053             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1054                                " %d", i_error );
1055             goto error;
1056         }
1057         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1058         if( i_error )
1059         {
1060             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1061                                "%d", i_error );
1062             goto error;
1063         }
1064         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1065         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1066         if( i_error )
1067         {
1068             FT_Done_Glyph( tmp_glyph );
1069             continue;
1070         }
1071         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1072
1073         /* Do rest */
1074         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1075             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1076         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1077         {
1078             p_line->pp_glyphs[ i ] = NULL;
1079             FreeLine( p_line );
1080             p_line = NewLine( (byte_t *)psz_string );
1081             if( p_prev ) p_prev->p_next = p_line;
1082             else p_lines = p_line;
1083
1084             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1085             {
1086                 psz_unicode--;
1087             }
1088             if( psz_unicode == psz_line_start )
1089             {
1090                 msg_Warn( p_filter, "unbreakable string" );
1091                 goto error;
1092             }
1093             else
1094             {
1095                 *psz_unicode = '\n';
1096             }
1097             psz_unicode = psz_line_start;
1098             i_pen_x = 0;
1099             i_previous = i = 0;
1100             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1101             continue;
1102         }
1103         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1104         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1105
1106         i_previous = i_glyph_index;
1107         i_pen_x += glyph->advance.x >> 6;
1108         i++;
1109     }
1110
1111     p_line->i_width = line.xMax;
1112     p_line->i_height = face->size->metrics.height >> 6;
1113     p_line->pp_glyphs[ i ] = NULL;
1114     p_line->i_alpha = i_font_alpha;
1115     p_line->i_red = i_red;
1116     p_line->i_green = i_green;
1117     p_line->i_blue = i_blue;
1118     result.x = __MAX( result.x, line.xMax );
1119     result.y += line.yMax - line.yMin;
1120
1121 #undef face
1122 #undef glyph
1123
1124     p_region_out->i_x = p_region_in->i_x;
1125     p_region_out->i_y = p_region_in->i_y;
1126
1127     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1128         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1129     else
1130         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1131
1132     if( psz_unicode_orig ) free( psz_unicode_orig );
1133     FreeLines( p_lines );
1134     return VLC_SUCCESS;
1135
1136  error:
1137     if( psz_unicode_orig ) free( psz_unicode_orig );
1138     FreeLines( p_lines );
1139     return VLC_EGENERIC;
1140 }
1141
1142 #ifdef HAVE_FONTCONFIG
1143 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1144                      int i_color, int i_alpha )
1145 {
1146     font_stack_t *p_new;
1147
1148     if( !p_font )
1149         return VLC_EGENERIC;
1150
1151     p_new = malloc( sizeof( font_stack_t ) );
1152     p_new->p_next = NULL;
1153
1154     if( psz_name )
1155         p_new->psz_name = strdup( psz_name );
1156     else
1157         p_new->psz_name = NULL;
1158
1159     p_new->i_size   = i_size;
1160     p_new->i_color  = i_color;
1161     p_new->i_alpha  = i_alpha;
1162
1163     if( !*p_font )
1164     {
1165         *p_font = p_new;
1166     }
1167     else
1168     {
1169         font_stack_t *p_last;
1170
1171         for( p_last = *p_font;
1172              p_last->p_next;
1173              p_last = p_last->p_next )
1174         ;
1175
1176         p_last->p_next = p_new;
1177     }
1178     return VLC_SUCCESS;
1179 }
1180
1181 static int PopFont( font_stack_t **p_font )
1182 {
1183     font_stack_t *p_last, *p_next_to_last;
1184
1185     if( !p_font || !*p_font )
1186         return VLC_EGENERIC;
1187
1188     p_next_to_last = NULL;
1189     for( p_last = *p_font;
1190          p_last->p_next;
1191          p_last = p_last->p_next )
1192     {
1193         p_next_to_last = p_last;
1194     }
1195
1196     if( p_next_to_last )
1197         p_next_to_last->p_next = NULL;
1198     else
1199         *p_font = NULL;
1200
1201     free( p_last->psz_name );
1202     free( p_last );
1203
1204     return VLC_SUCCESS;
1205 }
1206
1207 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1208                      int *i_color, int *i_alpha )
1209 {
1210     font_stack_t *p_last;
1211
1212     if( !p_font || !*p_font )
1213         return VLC_EGENERIC;
1214
1215     for( p_last=*p_font;
1216          p_last->p_next;
1217          p_last=p_last->p_next )
1218     ;
1219
1220     *psz_name = p_last->psz_name;
1221     *i_size   = p_last->i_size;
1222     *i_color  = p_last->i_color;
1223     *i_alpha  = p_last->i_alpha;
1224
1225     return VLC_SUCCESS;
1226 }
1227
1228 static uint32_t *IconvText( filter_t *p_filter, char *psz_string )
1229 {
1230     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1231     uint32_t *psz_unicode;
1232     int i_string_length;
1233
1234     psz_unicode =
1235         malloc( ( strlen( psz_string ) + 1 ) * sizeof( uint32_t ) );
1236     if( psz_unicode == NULL )
1237     {
1238         msg_Err( p_filter, "out of memory" );
1239         return NULL;;
1240     }
1241 #if defined(WORDS_BIGENDIAN)
1242     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1243 #else
1244     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1245 #endif
1246     if( iconv_handle == (vlc_iconv_t)-1 )
1247     {
1248         msg_Warn( p_filter, "unable to do conversion" );
1249         free( psz_unicode );
1250         return NULL;;
1251     }
1252
1253     {
1254         char *p_in_buffer, *p_out_buffer;
1255         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1256         i_in_bytes = strlen( psz_string );
1257         i_out_bytes = i_in_bytes * sizeof( uint32_t );
1258         i_out_bytes_left = i_out_bytes;
1259         p_in_buffer = psz_string;
1260         p_out_buffer = (char *)psz_unicode;
1261         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1262                            &p_out_buffer, &i_out_bytes_left );
1263
1264         vlc_iconv_close( iconv_handle );
1265
1266         if( i_in_bytes )
1267         {
1268             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1269                       "bytes left %d", strerror(errno), (int)i_in_bytes );
1270             free( psz_unicode );
1271             return NULL;;
1272         }
1273         *(uint32_t*)p_out_buffer = 0;
1274         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1275     }
1276
1277 #if defined(HAVE_FRIBIDI)
1278     {
1279         uint32_t *p_fribidi_string;
1280
1281         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1282
1283         /* Do bidi conversion line-by-line */
1284         FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1285         fribidi_log2vis((FriBidiChar*)psz_unicode, i_string_length,
1286                         &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0);
1287
1288         free( psz_unicode );
1289         psz_unicode = p_fribidi_string;
1290         p_fribidi_string[ i_string_length ] = 0;
1291     }
1292 #endif
1293     return psz_unicode;
1294 }
1295
1296 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1297                       vlc_bool_t b_uline, line_desc_t *p_line, uint32_t *psz_unicode,
1298                       int *pi_pen_x, int i_pen_y, int *pi_start,
1299                       FT_Vector *p_result )
1300 {
1301     FT_BBox      line;
1302     int          i_yMin, i_yMax;
1303     int          i;
1304
1305     int          i_previous = 0;
1306     int          i_pen_x_start = *pi_pen_x;
1307
1308     uint32_t *psz_unicode_start = psz_unicode;
1309
1310     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1311
1312     /* Account for part of line already in position */
1313     for( i=0; i<*pi_start; i++ )
1314     {
1315         FT_BBox glyph_size;
1316
1317         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ], ft_glyph_bbox_pixels, &glyph_size );
1318
1319         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1320             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1321         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1322         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1323     }
1324     i_yMin = line.yMin;
1325     i_yMax = line.yMax;
1326
1327     while( *psz_unicode && ( *psz_unicode != 0xffff ) )
1328     {
1329         FT_BBox glyph_size;
1330         FT_Glyph tmp_glyph;
1331         int i_error;
1332
1333         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1334         if( FT_HAS_KERNING( p_face ) && i_glyph_index
1335             && i_previous )
1336         {
1337             FT_Vector delta;
1338             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1339                             ft_kerning_default, &delta );
1340             *pi_pen_x += delta.x >> 6;
1341         }
1342         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1343         p_line->p_glyph_pos[ i ].y = i_pen_y;
1344
1345         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1346         if( i_error )
1347         {
1348             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned %d", i_error );
1349             p_line->pp_glyphs[ i ] = NULL;
1350             return VLC_EGENERIC;
1351         }
1352         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1353         if( i_error )
1354         {
1355             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned %d", i_error );
1356             p_line->pp_glyphs[ i ] = NULL;
1357             return VLC_EGENERIC;
1358         }
1359         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1360         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1361         if( i_error )
1362         {
1363             FT_Done_Glyph( tmp_glyph );
1364             continue;
1365         }
1366         if( b_uline )
1367         {
1368             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position, p_face->size->metrics.y_scale));
1369             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness, p_face->size->metrics.y_scale));
1370
1371             p_line->pi_underline_offset[ i ]  = ( aOffset < 0 ) ? -aOffset : aOffset;
1372             p_line->pi_underline_thickness[ i ] = ( aSize < 0 ) ? -aSize   : aSize;
1373         }
1374         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1375         p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1376
1377         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1378             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1379         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1380         {
1381             while( --i > *pi_start )
1382             {
1383                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1384             }
1385
1386             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1387             {
1388                 psz_unicode--;
1389             }
1390             if( psz_unicode == psz_unicode_start )
1391             {
1392                 msg_Warn( p_filter, "unbreakable string" );
1393                 p_line->pp_glyphs[ i ] = NULL;
1394                 return VLC_EGENERIC;
1395             }
1396             else
1397             {
1398                 *psz_unicode = 0xffff;
1399             }
1400             psz_unicode = psz_unicode_start;
1401             *pi_pen_x = i_pen_x_start;
1402             i_previous = 0;
1403
1404             line.yMax = i_yMax;
1405             line.yMin = i_yMin;
1406
1407             continue;
1408         }
1409         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1410         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1411
1412         i_previous = i_glyph_index;
1413         *pi_pen_x += p_face->glyph->advance.x >> 6;
1414         i++;
1415     }
1416     p_line->i_width = line.xMax;
1417     p_line->i_height = __MAX( p_line->i_height, p_face->size->metrics.height >> 6 );
1418     p_line->pp_glyphs[ i ] = NULL;
1419
1420     p_result->x = __MAX( p_result->x, line.xMax );
1421     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height, line.yMax - line.yMin ) );
1422
1423     *pi_start = i;
1424
1425     /* Get rid of any text processed - if necessary repositioning
1426      * at the start of a new line of text
1427      */
1428     if( !*psz_unicode )
1429     {
1430         *psz_unicode_start = '\0';
1431     }
1432     else
1433     {
1434         psz_unicode++;
1435         for( i=0; psz_unicode[ i ]; i++ )
1436             psz_unicode_start[ i ] = psz_unicode[ i ];
1437         psz_unicode_start[ i ] = '\0';
1438     }
1439
1440     return VLC_SUCCESS;
1441 }
1442
1443 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, char *psz_html, text_style_t *p_font_style, line_desc_t  **p_lines, FT_Vector *p_result )
1444 {
1445     filter_sys_t *p_sys = p_filter->p_sys;
1446
1447     FT_Vector tmp_result;
1448
1449     font_stack_t *p_fonts = NULL;
1450     vlc_bool_t b_italic = VLC_FALSE;
1451     vlc_bool_t b_bold   = VLC_FALSE;
1452     vlc_bool_t b_uline  = VLC_FALSE;
1453
1454     line_desc_t *p_line = NULL;
1455     line_desc_t *p_prev = NULL;
1456
1457     char *psz_node  = NULL;
1458
1459     int i_pen_x = 0;
1460     int i_pen_y = 0;
1461     int i_posn  = 0;
1462
1463     int rv = VLC_SUCCESS;
1464
1465     p_result->x = p_result->y = 0;
1466     tmp_result.x = tmp_result.y = 0;
1467
1468     if( p_font_style )
1469     {
1470         PushFont( &p_fonts,
1471                   p_font_style->psz_fontname,
1472                   p_font_style->i_font_size,
1473                   p_font_style->i_font_color,
1474                   p_font_style->i_font_alpha );
1475
1476         if( p_font_style->i_style_flags & STYLE_BOLD )
1477             b_bold = VLC_TRUE;
1478         if( p_font_style->i_style_flags & STYLE_ITALIC )
1479             b_italic = VLC_TRUE;
1480         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1481             b_uline = VLC_TRUE;
1482     }
1483     else
1484     {
1485         PushFont( &p_fonts, FC_DEFAULT_FONT, p_sys->i_font_size, 0xffffff, 0 );
1486     }
1487
1488     while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) && ( rv == VLC_SUCCESS ) )
1489     {
1490         switch ( xml_ReaderNodeType( p_xml_reader ) )
1491         {
1492             case XML_READER_NONE:
1493                 break;
1494             case XML_READER_ENDELEM:
1495                 psz_node = xml_ReaderName( p_xml_reader );
1496
1497                 if( psz_node )
1498                 {
1499                     if( !strcasecmp( "font", psz_node ) )
1500                         PopFont( &p_fonts );
1501                     else if( !strcasecmp( "b", psz_node ) )
1502                         b_bold   = VLC_FALSE;
1503                     else if( !strcasecmp( "i", psz_node ) )
1504                         b_italic = VLC_FALSE;
1505                     else if( !strcasecmp( "u", psz_node ) )
1506                         b_uline  = VLC_FALSE;
1507
1508                     free( psz_node );
1509                 }
1510                 break;
1511             case XML_READER_STARTELEM:
1512                 psz_node = xml_ReaderName( p_xml_reader );
1513                 if( psz_node )
1514                 {
1515                     if( !strcasecmp( "font", psz_node ) )
1516                     {
1517                         char *psz_fontname = NULL;
1518                         int  i_font_color = 0xffffff;
1519                         int  i_font_alpha = 0;
1520                         int  i_font_size  = 24;
1521
1522                         /* Default all attributes to the top font in the stack -- in case not
1523                          * all attributes are specified in the sub-font
1524                          */
1525                         if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ))
1526                         {
1527                             psz_fontname = strdup( psz_fontname );
1528                         }
1529
1530                         while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1531                         {
1532                             char *psz_name = xml_ReaderName ( p_xml_reader );
1533                             char *psz_value = xml_ReaderValue ( p_xml_reader );
1534
1535                             if( psz_name && psz_value )
1536                             {
1537                                 if( !strcasecmp( "face", psz_name ) )
1538                                 {
1539                                     if( psz_fontname ) free( psz_fontname );
1540                                     psz_fontname = strdup( psz_value );
1541                                 }
1542                                 else if( !strcasecmp( "size", psz_name ) )
1543                                 {
1544                                     i_font_size = atoi( psz_value );
1545                                 }
1546                                 else if( !strcasecmp( "color", psz_name )  &&
1547                                          ( psz_value[0] == '#' ) )
1548                                 {
1549                                     i_font_color = strtol( psz_value+1, NULL, 16 );
1550                                     i_font_color &= 0x00ffffff;
1551                                 }
1552                                 else if( !strcasecmp( "alpha", psz_name ) &&
1553                                          ( psz_value[0] == '#' ) )
1554                                 {
1555                                     i_font_alpha = strtol( psz_value+1, NULL, 16 );
1556                                     i_font_alpha &= 0xff;
1557                                 }
1558                                 free( psz_name );
1559                                 free( psz_value );
1560                             }
1561                         }
1562                         PushFont( &p_fonts, psz_fontname, i_font_size, i_font_color, i_font_alpha );
1563                         free( psz_fontname );
1564                     }
1565                     else if( !strcasecmp( "b", psz_node ) )
1566                     {
1567                         b_bold = VLC_TRUE;
1568                     }
1569                     else if( !strcasecmp( "i", psz_node ) )
1570                     {
1571                         b_italic = VLC_TRUE;
1572                     }
1573                     else if( !strcasecmp( "u", psz_node ) )
1574                     {
1575                         b_uline = VLC_TRUE;
1576                     }
1577                     else if( !strcasecmp( "br", psz_node ) )
1578                     {
1579                         if( p_line )
1580                         {
1581                             p_prev = p_line;
1582                             if( !(p_line = NewLine( (byte_t *)psz_html )) )
1583                             {
1584                                 msg_Err( p_filter, "out of memory" );
1585                                 free( psz_node );
1586                                 rv = VLC_EGENERIC;
1587                                 break;
1588                             }
1589                             p_line->b_new_color_mode = VLC_TRUE;
1590                             p_result->x = __MAX( p_result->x, tmp_result.x );
1591                             p_result->y += tmp_result.y;
1592
1593                             p_line->p_next = NULL;
1594                             i_pen_x = 0;
1595                             i_pen_y += tmp_result.y;
1596                             i_posn = 0;
1597                             p_prev->p_next = p_line;
1598                             tmp_result.x = 0;
1599                             tmp_result.y = 0;
1600                         }
1601                     }
1602                     free( psz_node );
1603                 }
1604                 break;
1605             case XML_READER_TEXT:
1606                 psz_node = xml_ReaderValue( p_xml_reader );
1607                 if( psz_node )
1608                 {
1609                     char *psz_fontname = NULL;
1610                     int  i_font_color = 0xffffff;
1611                     int  i_font_alpha = 0;
1612                     int  i_font_size  = 24;
1613                     FT_Face p_face = NULL;
1614
1615                     if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
1616                     {
1617                         int            i_idx = 0;
1618                         char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig, psz_fontname, b_bold, b_italic, &i_idx );
1619
1620                         if( psz_fontfile )
1621                         {
1622                             if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1623                                              i_idx, &p_face ) )
1624                             {
1625                                 free( psz_fontfile );
1626                                 free( psz_node );
1627                                 rv = VLC_EGENERIC;
1628                                 break;
1629                             }
1630                             free( psz_fontfile );
1631                         }
1632                     }
1633
1634                     if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
1635                         FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, i_font_size ) )
1636                     {
1637                         free( psz_node );
1638                         rv = VLC_EGENERIC;
1639                         break;
1640                     }
1641                     p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
1642
1643                     uint32_t *psz_unicode = IconvText( p_filter, psz_node );
1644
1645                     if( !psz_unicode )
1646                     {
1647                         free( psz_node );
1648                         if( p_face ) FT_Done_Face( p_face );
1649                         rv = VLC_EGENERIC;
1650                         break;
1651                     }
1652
1653                     while( *psz_unicode )
1654                     {
1655                         if( !p_line )
1656                         {
1657                             if( !(p_line = NewLine( (byte_t *)psz_html )) )
1658                             {
1659                                 msg_Err( p_filter, "out of memory" );
1660                                 free( psz_node );
1661                                 if( p_face ) FT_Done_Face( p_face );
1662                                 rv = VLC_EGENERIC;
1663                                 break;
1664                             }
1665                             /* New Color mode only works in YUVA rendering mode --
1666                              * (RGB mode has palette constraints on it). We therefore
1667                              * need to populate the legacy colour fields also.
1668                              */
1669                             p_line->b_new_color_mode = VLC_TRUE;
1670                             p_line->i_alpha = i_font_alpha;
1671                             p_line->i_red   = ( i_font_color & 0xff0000 ) >> 16;
1672                             p_line->i_green = ( i_font_color & 0x00ff00 ) >>  8;
1673                             p_line->i_blue  = ( i_font_color & 0x0000ff );
1674                             p_line->p_next = NULL;
1675                             i_pen_x = 0;
1676                             i_pen_y += tmp_result.y;
1677                             tmp_result.x = 0;
1678                             tmp_result.y = 0;
1679                             i_posn = 0;
1680                             if( p_prev ) p_prev->p_next = p_line;
1681                             else *p_lines = p_line;
1682                         }
1683
1684                         if( RenderTag( p_filter, p_face, i_font_color, b_uline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
1685                         {
1686                             free( psz_node );
1687                             if( p_face ) FT_Done_Face( p_face );
1688                             rv = VLC_EGENERIC;
1689                             break;
1690                         }
1691                         if( *psz_unicode )
1692                         {
1693                             p_result->x = __MAX( p_result->x, tmp_result.x );
1694                             p_result->y += tmp_result.y;
1695
1696                             p_prev = p_line;
1697                             p_line = NULL;
1698                         }
1699                     }
1700                     if( rv != VLC_SUCCESS ) break;
1701
1702                     if( p_face ) FT_Done_Face( p_face );
1703                     free( psz_unicode );
1704                     free( psz_node );
1705                 }
1706                 break;
1707         }
1708     }
1709     if( p_line )
1710     {
1711         p_result->x = __MAX( p_result->x, tmp_result.x );
1712         p_result->y += tmp_result.y;
1713     }
1714
1715
1716     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1717
1718     return rv;
1719 }
1720
1721
1722 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1723                        subpicture_region_t *p_region_in )
1724 {
1725     int          rv = VLC_SUCCESS;
1726     stream_t     *p_sub = NULL;
1727     xml_t        *p_xml = NULL;
1728     xml_reader_t *p_xml_reader = NULL;
1729
1730     if( !p_region_in || !p_region_in->psz_html )
1731         return VLC_EGENERIC;
1732
1733     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1734                               (uint8_t *) p_region_in->psz_html,
1735                               strlen( p_region_in->psz_html ),
1736                               VLC_TRUE );
1737     if( p_sub )
1738     {
1739         p_xml = xml_Create( p_filter );
1740         if( p_xml )
1741         {
1742             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
1743             if( p_xml_reader )
1744             {
1745                 FT_Vector    result;
1746                 line_desc_t  *p_lines = NULL;
1747
1748                 rv = ProcessNodes( p_filter, p_xml_reader, p_region_in->psz_html, p_region_in->p_style, &p_lines, &result );
1749
1750                 if( rv == VLC_SUCCESS )
1751                 {
1752                     p_region_out->i_x = p_region_in->i_x;
1753                     p_region_out->i_y = p_region_in->i_y;
1754
1755                     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1756                         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1757                     else
1758                         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1759                 }
1760                 FreeLines( p_lines );
1761
1762                 xml_ReaderDelete( p_xml, p_xml_reader );
1763             }
1764             xml_Delete( p_xml );
1765         }
1766         stream_Delete( p_sub );
1767     }
1768
1769     return rv;
1770 }
1771
1772 static char* FontConfig_Select( FcConfig* priv, const char* family, vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
1773 {
1774     FcResult result;
1775     FcPattern *pat, *p_pat;
1776     FcChar8* val_s;
1777     FcBool val_b;
1778
1779     pat = FcPatternCreate();
1780     if (!pat) return NULL;
1781
1782     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
1783     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
1784     FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
1785     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
1786
1787     FcDefaultSubstitute( pat );
1788
1789     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
1790     {
1791         FcPatternDestroy( pat );
1792         return NULL;
1793     }
1794
1795     p_pat = FcFontMatch( priv, pat, &result );
1796     FcPatternDestroy( pat );
1797     if( !p_pat ) return NULL;
1798
1799     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) ) ||
1800         ( val_b != FcTrue ) )
1801     {
1802         FcPatternDestroy( p_pat );
1803         return NULL;
1804     }
1805     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
1806     {
1807         *i_idx = 0;
1808     }
1809
1810     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
1811     {
1812         FcPatternDestroy( p_pat );
1813         return NULL;
1814     }
1815
1816     /*
1817     if( strcasecmp((const char*)val_s, family ) != 0 )
1818         msg_Warn( p_filter, "fontconfig: selected font family is not the requested one: '%s' != '%s'\n",
1819                             (const char*)val_s, family );
1820     */
1821
1822     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
1823     {
1824         FcPatternDestroy( p_pat );
1825         return NULL;
1826     }
1827
1828     FcPatternDestroy( p_pat );
1829     return strdup( (const char*)val_s );
1830 }
1831 #endif
1832
1833 static void FreeLine( line_desc_t *p_line )
1834 {
1835     unsigned int i;
1836     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
1837     {
1838         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1839     }
1840     free( p_line->pp_glyphs );
1841     free( p_line->p_glyph_pos );
1842     free( p_line->p_rgb );
1843     free( p_line->pi_underline_offset );
1844     free( p_line->pi_underline_thickness );
1845     free( p_line );
1846 }
1847
1848 static void FreeLines( line_desc_t *p_lines )
1849 {
1850     line_desc_t *p_line, *p_next;
1851
1852     if( !p_lines ) return;
1853
1854     for( p_line = p_lines; p_line != NULL; p_line = p_next )
1855     {
1856         p_next = p_line->p_next;
1857         FreeLine( p_line );
1858     }
1859 }
1860
1861 static line_desc_t *NewLine( byte_t *psz_string )
1862 {
1863     int i_count;
1864     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1865
1866     if( !p_line ) return NULL;
1867     p_line->i_height = 0;
1868     p_line->i_width = 0;
1869     p_line->p_next = NULL;
1870
1871     /* We don't use CountUtf8Characters() here because we are not acutally
1872      * sure the string is utf8. Better be safe than sorry. */
1873     i_count = strlen( (char *)psz_string );
1874
1875     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1876     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1877     p_line->p_rgb = malloc( sizeof( uint32_t ) * i_count + 1 );
1878     p_line->pi_underline_offset = calloc( i_count+ + 1, sizeof( uint16_t ) );
1879     p_line->pi_underline_thickness = calloc( i_count+ + 1, sizeof( uint16_t ) );
1880     if( ( p_line->pp_glyphs == NULL ) ||
1881         ( p_line->p_glyph_pos == NULL ) ||
1882         ( p_line->p_rgb == NULL ) ||
1883         ( p_line->pi_underline_offset == NULL ) ||
1884         ( p_line->pi_underline_thickness == NULL ) )
1885     {
1886         if( p_line->pi_underline_thickness ) free( p_line->pi_underline_thickness );
1887         if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
1888         if( p_line->p_rgb ) free( p_line->p_rgb );
1889         if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
1890         if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
1891         free( p_line );
1892         return NULL;
1893     }
1894     p_line->pp_glyphs[0] = NULL;
1895     p_line->b_new_color_mode = VLC_FALSE;
1896
1897     return p_line;
1898 }
1899
1900 static int SetFontSize( filter_t *p_filter, int i_size )
1901 {
1902     filter_sys_t *p_sys = p_filter->p_sys;
1903
1904     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1905
1906     if( !i_size )
1907     {
1908         vlc_value_t val;
1909
1910         if( !p_sys->i_default_font_size &&
1911             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1912             return VLC_SUCCESS;
1913
1914         if( p_sys->i_default_font_size )
1915         {
1916             i_size = p_sys->i_default_font_size;
1917         }
1918         else
1919         {
1920             var_Get( p_filter, "freetype-rel-fontsize", &val );
1921             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1922             p_filter->p_sys->i_display_height =
1923                 p_filter->fmt_out.video.i_height;
1924         }
1925         if( i_size <= 0 )
1926         {
1927             msg_Warn( p_filter, "invalid fontsize, using 12" );
1928             i_size = 12;
1929         }
1930
1931         msg_Dbg( p_filter, "using fontsize: %i", i_size );
1932     }
1933
1934     p_sys->i_font_size = i_size;
1935
1936     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1937     {
1938         msg_Err( p_filter, "couldn't set font size to %d", i_size );
1939         return VLC_EGENERIC;
1940     }
1941
1942     return VLC_SUCCESS;
1943 }