298d27fbf3b001edbfdc3ed1469338313f8d6b68
[vlc.git] / modules / misc / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <b dot purcell at adbglobal dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>
31
32 #ifdef HAVE_LINUX_LIMITS_H
33 #   include <linux/limits.h>
34 #endif
35
36 #include <vlc/vlc.h>
37 #include <vlc_vout.h>
38 #include <vlc_osd.h>
39 #include <vlc_block.h>
40 #include <vlc_filter.h>
41 #include <vlc_stream.h>
42 #include <vlc_xml.h>
43
44 #include <math.h>
45
46 #ifdef HAVE_ERRNO_H
47 #   include <errno.h>
48 #endif
49
50 #include <ft2build.h>
51 #include FT_FREETYPE_H
52 #include FT_GLYPH_H
53 #define FT_FLOOR(X)     ((X & -64) >> 6)
54 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
55 #define FT_MulFix(v, s) (((v)*(s))>>16)
56
57 #ifdef __APPLE__
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define FC_DEFAULT_FONT "Lucida Grande"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
66 #else
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
69 #endif
70
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
73 #endif
74
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
77 #endif
78
79 typedef struct line_desc_t line_desc_t;
80
81 /*****************************************************************************
82  * Local prototypes
83  *****************************************************************************/
84 static int  Create ( vlc_object_t * );
85 static void Destroy( vlc_object_t * );
86
87 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
88 static int RenderText( filter_t *, subpicture_region_t *,
89                        subpicture_region_t * );
90 #ifdef HAVE_FONTCONFIG
91 static int RenderHtml( filter_t *, subpicture_region_t *,
92                        subpicture_region_t * );
93 static char *FontConfig_Select( FcConfig *, const char *,
94                                 vlc_bool_t, vlc_bool_t, int * );
95 #endif
96 static line_desc_t *NewLine( int );
97
98 static int SetFontSize( filter_t *, int );
99
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->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
490             {
491                 i_align_offset = i_width - p_line->i_width;
492             }
493             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
494             {
495                 i_align_offset = ( i_width - p_line->i_width ) / 2;
496             }
497         }
498
499         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
500         {
501             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
502
503             i_offset = ( p_line->p_glyph_pos[ i ].y +
504                 i_glyph_tmax - p_glyph->top + 2 ) *
505                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
506                 i_align_offset;
507
508             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
509             {
510                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
511                 {
512                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
513                         p_dst[i_offset+x] =
514                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
515                 }
516                 i_offset += i_pitch;
517             }
518         }
519     }
520
521     /* Outlining (find something better than nearest neighbour filtering ?) */
522     if( 1 )
523     {
524         uint8_t *p_dst = p_region->picture.Y_PIXELS;
525         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
526         uint8_t left, current;
527
528         for( y = 1; y < (int)fmt.i_height - 1; y++ )
529         {
530             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
531             p_dst += p_region->picture.Y_PITCH;
532             left = 0;
533
534             for( x = 1; x < (int)fmt.i_width - 1; x++ )
535             {
536                 current = p_dst[x];
537                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
538                              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;
539                 left = current;
540             }
541         }
542         memset( p_top, 0, fmt.i_width );
543     }
544
545     return VLC_SUCCESS;
546 }
547
548 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
549                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
550                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
551                                 int i_glyph_tmax, int i_align_offset,
552                                 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
553                                 subpicture_region_t *p_region)
554 {
555     int y, x, z;
556     int i_pitch;
557     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
558
559     p_dst_y = p_region->picture.Y_PIXELS;
560     p_dst_u = p_region->picture.U_PIXELS;
561     p_dst_v = p_region->picture.V_PIXELS;
562     p_dst_a = p_region->picture.A_PIXELS;
563     i_pitch = p_region->picture.A_PITCH;
564
565     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
566                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
567
568     for( y = 0; y < i_line_thickness; y++ )
569     {
570         int i_extra = p_this_glyph->bitmap.width;
571
572         if( b_ul_next_char )
573         {
574             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
575                       (p_this_glyph_pos->x + p_this_glyph->left);
576         }
577         for( x = 0; x < i_extra; x++ )
578         {
579             vlc_bool_t b_ok = VLC_TRUE;
580
581             /* break the underline around the tails of any glyphs which cross it */
582             for( z = x - i_line_thickness;
583                  z < x + i_line_thickness && b_ok;
584                  z++ )
585             {
586                 if( p_next_glyph && ( z >= i_extra ) )
587                 {
588                     int i_row = i_line_offset + p_next_glyph->top + y;
589
590                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
591                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
592                     {
593                         b_ok = VLC_FALSE;
594                     }
595                 }
596                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
597                 {
598                     int i_row = i_line_offset + p_this_glyph->top + y;
599
600                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
601                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
602                     {
603                         b_ok = VLC_FALSE;
604                     }
605                 }
606             }
607
608             if( b_ok )
609             {
610                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
611                 p_dst_u[i_offset+x] = i_u;
612                 p_dst_v[i_offset+x] = i_v;
613                 p_dst_a[i_offset+x] = 255;
614             }
615         }
616         i_offset += i_pitch;
617     }
618 }
619
620 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
621 {
622     uint8_t *p_dst = p_region->picture.A_PIXELS;
623     int i_pitch = p_region->picture.A_PITCH;
624     int x,y;
625
626     for( ; p_line != NULL; p_line = p_line->p_next )
627     {
628         int i_glyph_tmax=0, i = 0;
629         int i_bitmap_offset, i_offset, i_align_offset = 0;
630         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
631         {
632             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
633             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
634         }
635
636         if( p_line->i_width < i_width )
637         {
638             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
639             {
640                 i_align_offset = i_width - p_line->i_width;
641             }
642             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
643             {
644                 i_align_offset = ( i_width - p_line->i_width ) / 2;
645             }
646         }
647
648         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
649         {
650             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
651
652             i_offset = ( p_line->p_glyph_pos[ i ].y +
653                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
654                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
655                 i_align_offset +xoffset;
656
657             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
658             {
659                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
660                 {
661                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
662                         if( p_dst[i_offset+x] <
663                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
664                             p_dst[i_offset+x] =
665                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
666                 }
667                 i_offset += i_pitch;
668             }
669         }
670     }
671
672 }
673
674 /*****************************************************************************
675  * Render: place string in picture
676  *****************************************************************************
677  * This function merges the previously rendered freetype glyphs into a picture
678  *****************************************************************************/
679 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
680                    line_desc_t *p_line, int i_width, int i_height )
681 {
682     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
683     video_format_t fmt;
684     int i, x, y, i_pitch, i_alpha;
685     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
686     subpicture_region_t *p_region_tmp;
687
688     if( i_width == 0 || i_height == 0 )
689         return VLC_SUCCESS;
690
691     /* Create a new subpicture region */
692     memset( &fmt, 0, sizeof(video_format_t) );
693     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
694     fmt.i_aspect = 0;
695     fmt.i_width = fmt.i_visible_width = i_width + 6;
696     fmt.i_height = fmt.i_visible_height = i_height + 6;
697     if( p_region->fmt.i_visible_width > 0 )
698         fmt.i_visible_width = p_region->fmt.i_visible_width;
699     if( p_region->fmt.i_visible_height > 0 )
700         fmt.i_visible_height = p_region->fmt.i_visible_height;
701     fmt.i_x_offset = fmt.i_y_offset = 0;
702     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
703     if( !p_region_tmp )
704     {
705         msg_Err( p_filter, "cannot allocate SPU region" );
706         return VLC_EGENERIC;
707     }
708
709     p_region->fmt = p_region_tmp->fmt;
710     p_region->picture = p_region_tmp->picture;
711     free( p_region_tmp );
712
713     /* Calculate text color components */
714     i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red  + 4130 * p_line->i_green +
715                       802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
716     i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red  + -2384 * p_line->i_green +
717                      3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
718     i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
719                       -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
720     i_alpha = p_line->i_alpha;
721
722     p_dst_y = p_region->picture.Y_PIXELS;
723     p_dst_u = p_region->picture.U_PIXELS;
724     p_dst_v = p_region->picture.V_PIXELS;
725     p_dst_a = p_region->picture.A_PIXELS;
726     i_pitch = p_region->picture.A_PITCH;
727
728     /* Initialize the region pixels */
729     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
730     {
731         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
732         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
733         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
734         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
735     }
736     else
737     {
738         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
739         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
740         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
741         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
742     }
743     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
744         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
745     {
746         DrawBlack( p_line, i_width, p_region,  0,  0);
747         DrawBlack( p_line, i_width, p_region, -1,  0);
748         DrawBlack( p_line, i_width, p_region,  0, -1);
749         DrawBlack( p_line, i_width, p_region,  1,  0);
750         DrawBlack( p_line, i_width, p_region,  0,  1);
751     }
752
753     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
754     {
755         DrawBlack( p_line, i_width, p_region, -1, -1);
756         DrawBlack( p_line, i_width, p_region, -1,  1);
757         DrawBlack( p_line, i_width, p_region,  1, -1);
758         DrawBlack( p_line, i_width, p_region,  1,  1);
759
760         DrawBlack( p_line, i_width, p_region, -2,  0);
761         DrawBlack( p_line, i_width, p_region,  0, -2);
762         DrawBlack( p_line, i_width, p_region,  2,  0);
763         DrawBlack( p_line, i_width, p_region,  0,  2);
764
765         DrawBlack( p_line, i_width, p_region, -2, -2);
766         DrawBlack( p_line, i_width, p_region, -2,  2);
767         DrawBlack( p_line, i_width, p_region,  2, -2);
768         DrawBlack( p_line, i_width, p_region,  2,  2);
769
770         DrawBlack( p_line, i_width, p_region, -3,  0);
771         DrawBlack( p_line, i_width, p_region,  0, -3);
772         DrawBlack( p_line, i_width, p_region,  3,  0);
773         DrawBlack( p_line, i_width, p_region,  0,  3);
774     }
775
776     for( ; p_line != NULL; p_line = p_line->p_next )
777     {
778         int i_glyph_tmax = 0;
779         int i_bitmap_offset, i_offset, i_align_offset = 0;
780         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
781         {
782             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
783             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
784         }
785
786         if( p_line->i_width < i_width )
787         {
788             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
789             {
790                 i_align_offset = i_width - p_line->i_width;
791             }
792             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
793             {
794                 i_align_offset = ( i_width - p_line->i_width ) / 2;
795             }
796         }
797
798         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
799         {
800             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
801
802             i_offset = ( p_line->p_glyph_pos[ i ].y +
803                 i_glyph_tmax - p_glyph->top + 3 ) *
804                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
805                 i_align_offset;
806
807             if( p_line->b_new_color_mode )
808             {
809                 /* Every glyph can (and in fact must) have its own color */
810                 int i_red   = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
811                 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >>  8;
812                 int i_blue  = ( p_line->p_rgb[ i ] & 0x000000ff );
813
814                 i_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
815                                   802 * i_blue + 4096 + 131072 ) >> 13, 235);
816                 i_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
817                                  3598 * i_blue + 4096 + 1048576) >> 13, 240);
818                 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
819                                   -585 * i_blue + 4096 + 1048576) >> 13, 240);
820             }
821
822             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
823             {
824                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
825                 {
826                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
827                     {
828                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
829                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
830
831                         p_dst_u[i_offset+x] = i_u;
832                         p_dst_v[i_offset+x] = i_v;
833
834                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
835                             p_dst_a[i_offset+x] = 0xff;
836                     }
837                 }
838                 i_offset += i_pitch;
839             }
840
841             if( p_line->pi_underline_thickness[ i ] )
842             {
843                 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
844                                     p_line->pi_underline_offset[ i ],
845                                    (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
846                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
847                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
848                                     i_glyph_tmax, i_align_offset,
849                                     i_y, i_u, i_v, i_alpha,
850                                     p_region);
851             }
852         }
853     }
854
855     /* Apply the alpha setting */
856     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
857         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
858
859     return VLC_SUCCESS;
860 }
861
862 /**
863  * This function renders a text subpicture region into another one.
864  * It also calculates the size needed for this string, and renders the
865  * needed glyphs into memory. It is used as pf_add_string callback in
866  * the vout method by this module
867  */
868 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
869                        subpicture_region_t *p_region_in )
870 {
871     filter_sys_t *p_sys = p_filter->p_sys;
872     line_desc_t  *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
873     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
874     uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
875     int i_string_length;
876     char *psz_string;
877     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
878     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
879
880     FT_BBox line;
881     FT_BBox glyph_size;
882     FT_Vector result;
883     FT_Glyph tmp_glyph;
884
885     /* Sanity check */
886     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
887     psz_string = p_region_in->psz_text;
888     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
889
890     if( p_region_in->p_style )
891     {
892         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
893         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
894         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
895     }
896     else
897     {
898         i_font_color = p_sys->i_font_color;
899         i_font_alpha = 255 - p_sys->i_font_opacity;
900         i_font_size  = p_sys->i_default_font_size;
901     }
902
903     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
904     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
905     SetFontSize( p_filter, i_font_size );
906
907     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
908     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
909     i_blue  =   i_font_color & 0x000000FF;
910
911     result.x =  result.y = 0;
912     line.xMin = line.xMax = line.yMin = line.yMax = 0;
913
914     psz_unicode = psz_unicode_orig =
915         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
916     if( psz_unicode == NULL )
917     {
918         msg_Err( p_filter, "out of memory" );
919         goto error;
920     }
921 #if defined(WORDS_BIGENDIAN)
922     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
923 #else
924     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
925 #endif
926     if( iconv_handle == (vlc_iconv_t)-1 )
927     {
928         msg_Warn( p_filter, "unable to do conversion" );
929         goto error;
930     }
931
932     {
933         char *p_out_buffer;
934         const char *p_in_buffer = psz_string;
935         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
936         i_in_bytes = strlen( psz_string );
937         i_out_bytes = i_in_bytes * sizeof( uint32_t );
938         i_out_bytes_left = i_out_bytes;
939         p_out_buffer = (char *)psz_unicode;
940         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
941                            &p_out_buffer, &i_out_bytes_left );
942
943         vlc_iconv_close( iconv_handle );
944
945         if( i_in_bytes )
946         {
947             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
948                       "bytes left %d", strerror(errno), (int)i_in_bytes );
949             goto error;
950         }
951         *(uint32_t*)p_out_buffer = 0;
952         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
953     }
954
955 #if defined(HAVE_FRIBIDI)
956     {
957         uint32_t *p_fribidi_string;
958         int start_pos, pos = 0;
959
960         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
961         if( !p_fribidi_string )
962         {
963             msg_Err( p_filter, "out of memory" );
964             goto error;
965         }
966
967         /* Do bidi conversion line-by-line */
968         while(pos < i_string_length)
969         {
970             while(pos < i_string_length) {
971                 i_char = psz_unicode[pos];
972                 if (i_char != '\r' && i_char != '\n')
973                     break;
974                 p_fribidi_string[pos] = i_char;
975                 ++pos;
976             }
977             start_pos = pos;
978             while(pos < i_string_length) {
979                 i_char = psz_unicode[pos];
980                 if (i_char == '\r' || i_char == '\n')
981                     break;
982                 ++pos;
983             }
984             if (pos > start_pos)
985             {
986                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
987                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
988                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
989             }
990         }
991
992         free( psz_unicode_orig );
993         psz_unicode = psz_unicode_orig = p_fribidi_string;
994         p_fribidi_string[ i_string_length ] = 0;
995     }
996 #endif
997
998     /* Calculate relative glyph positions and a bounding box for the
999      * entire string */
1000     if( !(p_line = NewLine( strlen( psz_string ))) )
1001     {
1002         msg_Err( p_filter, "out of memory" );
1003         goto error;
1004     }
1005     p_lines = p_line;
1006     i_pen_x = i_pen_y = 0;
1007     i_previous = i = 0;
1008     psz_line_start = psz_unicode;
1009
1010 #define face p_sys->p_face
1011 #define glyph face->glyph
1012
1013     while( *psz_unicode )
1014     {
1015         i_char = *psz_unicode++;
1016         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1017         {
1018             continue;
1019         }
1020
1021         if( i_char == '\n' )
1022         {
1023             psz_line_start = psz_unicode;
1024             if( !(p_next = NewLine( strlen( psz_string ))) )
1025             {
1026                 msg_Err( p_filter, "out of memory" );
1027                 goto error;
1028             }
1029             p_line->p_next = p_next;
1030             p_line->i_width = line.xMax;
1031             p_line->i_height = face->size->metrics.height >> 6;
1032             p_line->pp_glyphs[ i ] = NULL;
1033             p_line->i_alpha = i_font_alpha;
1034             p_line->i_red = i_red;
1035             p_line->i_green = i_green;
1036             p_line->i_blue = i_blue;
1037             p_prev = p_line;
1038             p_line = p_next;
1039             result.x = __MAX( result.x, line.xMax );
1040             result.y += face->size->metrics.height >> 6;
1041             i_pen_x = 0;
1042             i_previous = i = 0;
1043             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1044             i_pen_y += face->size->metrics.height >> 6;
1045 #if 0
1046             msg_Dbg( p_filter, "Creating new line, i is %d", i );
1047 #endif
1048             continue;
1049         }
1050
1051         i_glyph_index = FT_Get_Char_Index( face, i_char );
1052         if( p_sys->i_use_kerning && i_glyph_index
1053             && i_previous )
1054         {
1055             FT_Vector delta;
1056             FT_Get_Kerning( face, i_previous, i_glyph_index,
1057                             ft_kerning_default, &delta );
1058             i_pen_x += delta.x >> 6;
1059
1060         }
1061         p_line->p_glyph_pos[ i ].x = i_pen_x;
1062         p_line->p_glyph_pos[ i ].y = i_pen_y;
1063         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1064         if( i_error )
1065         {
1066             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1067                                " %d", i_error );
1068             goto error;
1069         }
1070         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1071         if( i_error )
1072         {
1073             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1074                                "%d", i_error );
1075             goto error;
1076         }
1077         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1078         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1079         if( i_error )
1080         {
1081             FT_Done_Glyph( tmp_glyph );
1082             continue;
1083         }
1084         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1085
1086         /* Do rest */
1087         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1088             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1089         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1090         {
1091             p_line->pp_glyphs[ i ] = NULL;
1092             FreeLine( p_line );
1093             p_line = NewLine( strlen( psz_string ));
1094             if( p_prev ) p_prev->p_next = p_line;
1095             else p_lines = p_line;
1096
1097             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1098             {
1099                 psz_unicode--;
1100             }
1101             if( psz_unicode == psz_line_start )
1102             {
1103                 msg_Warn( p_filter, "unbreakable string" );
1104                 goto error;
1105             }
1106             else
1107             {
1108                 *psz_unicode = '\n';
1109             }
1110             psz_unicode = psz_line_start;
1111             i_pen_x = 0;
1112             i_previous = i = 0;
1113             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1114             continue;
1115         }
1116         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1117         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1118
1119         i_previous = i_glyph_index;
1120         i_pen_x += glyph->advance.x >> 6;
1121         i++;
1122     }
1123
1124     p_line->i_width = line.xMax;
1125     p_line->i_height = face->size->metrics.height >> 6;
1126     p_line->pp_glyphs[ i ] = NULL;
1127     p_line->i_alpha = i_font_alpha;
1128     p_line->i_red = i_red;
1129     p_line->i_green = i_green;
1130     p_line->i_blue = i_blue;
1131     result.x = __MAX( result.x, line.xMax );
1132     result.y += line.yMax - line.yMin;
1133
1134 #undef face
1135 #undef glyph
1136
1137     p_region_out->i_x = p_region_in->i_x;
1138     p_region_out->i_y = p_region_in->i_y;
1139
1140     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1141         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1142     else
1143         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1144
1145     if( psz_unicode_orig ) free( psz_unicode_orig );
1146     FreeLines( p_lines );
1147     return VLC_SUCCESS;
1148
1149  error:
1150     if( psz_unicode_orig ) free( psz_unicode_orig );
1151     FreeLines( p_lines );
1152     return VLC_EGENERIC;
1153 }
1154
1155 #ifdef HAVE_FONTCONFIG
1156 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1157         int i_font_color, int i_font_alpha, vlc_bool_t b_bold,
1158         vlc_bool_t b_italic, vlc_bool_t b_uline )
1159 {
1160     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
1161
1162     if( p_style )
1163     {
1164         p_style->i_font_size  = i_font_size;
1165         p_style->i_font_color = ( i_font_color & 0x00ffffff )
1166                                 | (( i_font_alpha & 0xff ) << 24 );
1167         p_style->b_italic     = b_italic;
1168         p_style->b_bold       = b_bold;
1169         p_style->b_underline  = b_uline;
1170         /* p_style has just been malloc'ed in this function -
1171          * it CAN'T have a previous assignment, and hence we
1172          * don't need to do a free() for any previous value -
1173          * which will in fact be undefined. */
1174         p_style->psz_fontname = strdup( psz_fontname );
1175     }
1176     return p_style;
1177 }
1178
1179 static void DeleteStyle( ft_style_t *p_style )
1180 {
1181     if( p_style )
1182     {
1183         if( p_style->psz_fontname )
1184             free( p_style->psz_fontname );
1185         free( p_style );
1186     }
1187 }
1188
1189 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1190 {
1191     if( !s1 || !s2 )
1192         return VLC_FALSE;
1193     if( s1 == s2 )
1194         return VLC_TRUE;
1195
1196     if(( s1->i_font_size  == s2->i_font_size ) &&
1197        ( s1->i_font_color == s2->i_font_color ) &&
1198        ( s1->b_italic     == s2->b_italic ) &&
1199        ( s1->b_bold       == s2->b_bold ) &&
1200        ( s1->b_underline  == s2->b_underline ) &&
1201        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1202     {
1203         return VLC_TRUE;
1204     }
1205     return VLC_FALSE;
1206 }
1207
1208 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1209                      int i_color, int i_alpha )
1210 {
1211     font_stack_t *p_new;
1212
1213     if( !p_font )
1214         return VLC_EGENERIC;
1215
1216     p_new = malloc( sizeof( font_stack_t ) );
1217     if( ! p_new )
1218         return VLC_ENOMEM;
1219
1220     p_new->p_next = NULL;
1221
1222     if( psz_name )
1223         p_new->psz_name = strdup( psz_name );
1224     else
1225         p_new->psz_name = NULL;
1226
1227     p_new->i_size   = i_size;
1228     p_new->i_color  = i_color;
1229     p_new->i_alpha  = i_alpha;
1230
1231     if( !*p_font )
1232     {
1233         *p_font = p_new;
1234     }
1235     else
1236     {
1237         font_stack_t *p_last;
1238
1239         for( p_last = *p_font;
1240              p_last->p_next;
1241              p_last = p_last->p_next )
1242         ;
1243
1244         p_last->p_next = p_new;
1245     }
1246     return VLC_SUCCESS;
1247 }
1248
1249 static int PopFont( font_stack_t **p_font )
1250 {
1251     font_stack_t *p_last, *p_next_to_last;
1252
1253     if( !p_font || !*p_font )
1254         return VLC_EGENERIC;
1255
1256     p_next_to_last = NULL;
1257     for( p_last = *p_font;
1258          p_last->p_next;
1259          p_last = p_last->p_next )
1260     {
1261         p_next_to_last = p_last;
1262     }
1263
1264     if( p_next_to_last )
1265         p_next_to_last->p_next = NULL;
1266     else
1267         *p_font = NULL;
1268
1269     free( p_last->psz_name );
1270     free( p_last );
1271
1272     return VLC_SUCCESS;
1273 }
1274
1275 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1276                      int *i_color, int *i_alpha )
1277 {
1278     font_stack_t *p_last;
1279
1280     if( !p_font || !*p_font )
1281         return VLC_EGENERIC;
1282
1283     for( p_last=*p_font;
1284          p_last->p_next;
1285          p_last=p_last->p_next )
1286     ;
1287
1288     *psz_name = p_last->psz_name;
1289     *i_size   = p_last->i_size;
1290     *i_color  = p_last->i_color;
1291     *i_alpha  = p_last->i_alpha;
1292
1293     return VLC_SUCCESS;
1294 }
1295
1296 static void IconvText( filter_t *p_filter, const char *psz_string,
1297                        uint32_t *i_string_length, uint32_t **ppsz_unicode )
1298 {
1299     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1300
1301     /* If memory hasn't been allocated for our output string, allocate it here
1302      * - the calling function must now be responsible for freeing it.
1303      */
1304     if( !*ppsz_unicode )
1305         *ppsz_unicode = (uint32_t *)
1306             malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1307
1308     /* We don't need to handle a NULL pointer in *ppsz_unicode
1309      * if we are instead testing for a non NULL value like we are here */
1310
1311     if( *ppsz_unicode )
1312     {
1313 #if defined(WORDS_BIGENDIAN)
1314         iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1315 #else
1316         iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1317 #endif
1318         if( iconv_handle != (vlc_iconv_t)-1 )
1319         {
1320             char *p_in_buffer, *p_out_buffer;
1321             size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1322             i_in_bytes = strlen( psz_string );
1323             i_out_bytes = i_in_bytes * sizeof( uint32_t );
1324             i_out_bytes_left = i_out_bytes;
1325             p_in_buffer = (char *) psz_string;
1326             p_out_buffer = (char *) *ppsz_unicode;
1327             i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1328                     &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1329
1330             vlc_iconv_close( iconv_handle );
1331
1332             if( i_in_bytes )
1333             {
1334                 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1335                           "bytes left %d", strerror(errno), (int)i_in_bytes );
1336             }
1337             else
1338             {
1339                 *(uint32_t*)p_out_buffer = 0;
1340                 *i_string_length =
1341                     (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1342             }
1343         }
1344         else
1345         {
1346             msg_Warn( p_filter, "unable to do conversion" );
1347         }
1348     }
1349 }
1350
1351 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1352         font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic, 
1353         vlc_bool_t b_uline )
1354 {
1355     ft_style_t   *p_style = NULL;
1356
1357     char  *psz_fontname = NULL;
1358     int    i_font_color = p_sys->i_font_color;
1359     int    i_font_alpha = 0;
1360     int    i_font_size  = p_sys->i_font_size;
1361
1362     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1363                                  &i_font_color, &i_font_alpha ) )
1364     {
1365         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1366                 i_font_alpha, b_bold, b_italic, b_uline );
1367     }
1368     return p_style;
1369 }
1370
1371 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1372                       vlc_bool_t b_uline, line_desc_t *p_line,
1373                       uint32_t *psz_unicode, int *pi_pen_x, int i_pen_y,
1374                       int *pi_start, FT_Vector *p_result )
1375 {
1376     FT_BBox      line;
1377     int          i_yMin, i_yMax;
1378     int          i;
1379     vlc_bool_t   b_first_on_line = VLC_TRUE;
1380
1381     int          i_previous = 0;
1382     int          i_pen_x_start = *pi_pen_x;
1383
1384     uint32_t *psz_unicode_start = psz_unicode;
1385
1386     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1387
1388     /* Account for part of line already in position */
1389     for( i=0; i<*pi_start; i++ )
1390     {
1391         FT_BBox glyph_size;
1392
1393         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1394                             ft_glyph_bbox_pixels, &glyph_size );
1395
1396         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1397             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1398         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1399         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1400     }
1401     i_yMin = line.yMin;
1402     i_yMax = line.yMax;
1403
1404     if( line.xMax > 0 )
1405         b_first_on_line = VLC_FALSE;
1406
1407     while( *psz_unicode && ( *psz_unicode != '\n' ) )
1408     {
1409         FT_BBox glyph_size;
1410         FT_Glyph tmp_glyph;
1411         int i_error;
1412
1413         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1414         if( FT_HAS_KERNING( p_face ) && i_glyph_index
1415             && i_previous )
1416         {
1417             FT_Vector delta;
1418             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1419                             ft_kerning_default, &delta );
1420             *pi_pen_x += delta.x >> 6;
1421         }
1422         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1423         p_line->p_glyph_pos[ i ].y = i_pen_y;
1424
1425         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1426         if( i_error )
1427         {
1428             msg_Err( p_filter,
1429                    "unable to render text FT_Load_Glyph returned %d", i_error );
1430             p_line->pp_glyphs[ i ] = NULL;
1431             return VLC_EGENERIC;
1432         }
1433         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1434         if( i_error )
1435         {
1436             msg_Err( p_filter,
1437                     "unable to render text FT_Get_Glyph returned %d", i_error );
1438             p_line->pp_glyphs[ i ] = NULL;
1439             return VLC_EGENERIC;
1440         }
1441         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1442         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1443         if( i_error )
1444         {
1445             FT_Done_Glyph( tmp_glyph );
1446             continue;
1447         }
1448         if( b_uline )
1449         {
1450             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1451                                                p_face->size->metrics.y_scale));
1452             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1453                                             p_face->size->metrics.y_scale));
1454
1455             p_line->pi_underline_offset[ i ]  =
1456                                        ( aOffset < 0 ) ? -aOffset : aOffset;
1457             p_line->pi_underline_thickness[ i ] =
1458                                        ( aSize < 0 ) ? -aSize   : aSize;
1459         }
1460         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1461         p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1462
1463         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1464                     glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1465         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1466         {
1467             while( --i > *pi_start )
1468             {
1469                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1470             }
1471
1472             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1473             {
1474                 psz_unicode--;
1475             }
1476             if( psz_unicode == psz_unicode_start )
1477             {
1478                 if( b_first_on_line )
1479                 {
1480                     msg_Warn( p_filter, "unbreakable string" );
1481                     p_line->pp_glyphs[ i ] = NULL;
1482                     return VLC_EGENERIC;
1483                 }
1484                 *pi_pen_x = i_pen_x_start;
1485
1486                 p_line->i_width = line.xMax;
1487                 p_line->i_height = __MAX( p_line->i_height,
1488                                           p_face->size->metrics.height >> 6 );
1489                 p_line->pp_glyphs[ i ] = NULL;
1490
1491                 p_result->x = __MAX( p_result->x, line.xMax );
1492                 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1493                                                          i_yMax - i_yMin ) );
1494
1495                 *pi_start = i;
1496                 return VLC_SUCCESS;
1497             }
1498             else
1499             {
1500                 *psz_unicode = '\n';
1501             }
1502             psz_unicode = psz_unicode_start;
1503             *pi_pen_x = i_pen_x_start;
1504             i_previous = 0;
1505
1506             line.yMax = i_yMax;
1507             line.yMin = i_yMin;
1508
1509             continue;
1510         }
1511         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1512         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1513
1514         i_previous = i_glyph_index;
1515         *pi_pen_x += p_face->glyph->advance.x >> 6;
1516         i++;
1517     }
1518     p_line->i_width = line.xMax;
1519     p_line->i_height = __MAX( p_line->i_height,
1520                               p_face->size->metrics.height >> 6 );
1521     p_line->pp_glyphs[ i ] = NULL;
1522
1523     p_result->x = __MAX( p_result->x, line.xMax );
1524     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1525                          line.yMax - line.yMin ) );
1526
1527     *pi_start = i;
1528
1529     /* Get rid of any text processed - if necessary repositioning
1530      * at the start of a new line of text
1531      */
1532     if( !*psz_unicode )
1533     {
1534         *psz_unicode_start = '\0';
1535     }
1536     else
1537     {
1538         psz_unicode++;
1539         for( i=0; psz_unicode[ i ]; i++ )
1540             psz_unicode_start[ i ] = psz_unicode[ i ];
1541         psz_unicode_start[ i ] = '\0';
1542     }
1543
1544     return VLC_SUCCESS;
1545 }
1546
1547 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1548                                   font_stack_t **p_fonts )
1549 {
1550     int   rv;
1551     char *psz_fontname = NULL;
1552     int   i_font_color = 0xffffff;
1553     int   i_font_alpha = 0;
1554     int   i_font_size  = 24;
1555
1556     /* Default all attributes to the top font in the stack -- in case not
1557      * all attributes are specified in the sub-font
1558      */
1559     if( VLC_SUCCESS == PeekFont( p_fonts,
1560                                  &psz_fontname,
1561                                  &i_font_size,
1562                                  &i_font_color,
1563                                  &i_font_alpha ))
1564     {
1565         psz_fontname = strdup( psz_fontname );
1566     }
1567
1568     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1569     {
1570         char *psz_name = xml_ReaderName ( p_xml_reader );
1571         char *psz_value = xml_ReaderValue ( p_xml_reader );
1572
1573         if( psz_name && psz_value )
1574         {
1575             if( !strcasecmp( "face", psz_name ) )
1576             {
1577                 if( psz_fontname ) free( psz_fontname );
1578                 psz_fontname = strdup( psz_value );
1579             }
1580             else if( !strcasecmp( "size", psz_name ) )
1581             {
1582                 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1583                 {
1584                     int i_value = atoi( psz_value );
1585
1586                     if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1587                         i_font_size += ( i_value * i_font_size ) / 10;
1588                     else if( i_value < -5 )
1589                         i_font_size = - i_value;
1590                     else if( i_value > 5 )
1591                         i_font_size = i_value;
1592                 }
1593                 else
1594                     i_font_size = atoi( psz_value );
1595             }
1596             else if( !strcasecmp( "color", psz_name )  &&
1597                      ( psz_value[0] == '#' ) )
1598             {
1599                 i_font_color = strtol( psz_value + 1, NULL, 16 );
1600                 i_font_color &= 0x00ffffff;
1601             }
1602             else if( !strcasecmp( "alpha", psz_name ) &&
1603                      ( psz_value[0] == '#' ) )
1604             {
1605                 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1606                 i_font_alpha &= 0xff;
1607             }
1608             free( psz_name );
1609             free( psz_value );
1610         }
1611     }
1612     rv = PushFont( p_fonts,
1613                    psz_fontname,
1614                    i_font_size,
1615                    i_font_color,
1616                    i_font_alpha );
1617
1618     free( psz_fontname );
1619
1620     return rv;
1621 }
1622
1623 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1624                        uint32_t **psz_text_out, uint32_t *pi_runs,
1625                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1626                        ft_style_t *p_style )
1627 {
1628     uint32_t      i_string_length = 0;
1629
1630     IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1631     *psz_text_out += i_string_length;
1632
1633     if( ppp_styles && ppi_run_lengths )
1634     {
1635         (*pi_runs)++;
1636
1637         if( *ppp_styles )
1638         {
1639             *ppp_styles = (ft_style_t **)
1640                 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1641         }
1642         else if( *pi_runs == 1 )
1643         {
1644             *ppp_styles = (ft_style_t **)
1645                 malloc( *pi_runs * sizeof( ft_style_t * ) );
1646         }
1647
1648         /* We have just malloc'ed this memory successfully -
1649          * *pi_runs HAS to be within the memory area of *ppp_styles */
1650         if( *ppp_styles )
1651         {
1652             (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1653             p_style = NULL;
1654         }
1655
1656         if( *ppi_run_lengths )
1657         {
1658             *ppi_run_lengths = (uint32_t *)
1659                 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1660         }
1661         else if( *pi_runs == 1 )
1662         {
1663             *ppi_run_lengths = (uint32_t *)
1664                 malloc( *pi_runs * sizeof( uint32_t ) );
1665         }
1666
1667         /* same remarks here */
1668         if( *ppi_run_lengths )
1669         {
1670             (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1671         }
1672     }
1673     /* If we couldn't use the p_style argument due to memory allocation
1674      * problems above, release it here.
1675      */
1676     if( p_style ) DeleteStyle( p_style );
1677 }
1678
1679 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader,
1680                          text_style_t *p_font_style, uint32_t *psz_text,
1681                          int *pi_len, uint32_t *pi_runs,
1682                          uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles)
1683 {
1684     int           rv             = VLC_SUCCESS;
1685     filter_sys_t *p_sys          = p_filter->p_sys;
1686     uint32_t     *psz_text_orig  = psz_text;
1687     font_stack_t *p_fonts        = NULL;
1688
1689     char *psz_node  = NULL;
1690
1691     vlc_bool_t b_italic = VLC_FALSE;
1692     vlc_bool_t b_bold   = VLC_FALSE;
1693     vlc_bool_t b_uline  = VLC_FALSE;
1694
1695     if( p_font_style )
1696     {
1697         rv = PushFont( &p_fonts,
1698                        p_font_style->psz_fontname,
1699                        p_font_style->i_font_size,
1700                        p_font_style->i_font_color,
1701                        p_font_style->i_font_alpha );
1702
1703         if( p_font_style->i_style_flags & STYLE_BOLD )
1704             b_bold = VLC_TRUE;
1705         if( p_font_style->i_style_flags & STYLE_ITALIC )
1706             b_italic = VLC_TRUE;
1707         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1708             b_uline = VLC_TRUE;
1709     }
1710     else
1711     {
1712         rv = PushFont( &p_fonts,
1713                        FC_DEFAULT_FONT,
1714                        p_sys->i_font_size,
1715                        0xffffff,
1716                        0 );
1717     }
1718     if( rv != VLC_SUCCESS )
1719         return rv;
1720
1721     while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1722     {
1723         switch ( xml_ReaderNodeType( p_xml_reader ) )
1724         {
1725             case XML_READER_NONE:
1726                 break;
1727             case XML_READER_ENDELEM:
1728                 psz_node = xml_ReaderName( p_xml_reader );
1729
1730                 if( psz_node )
1731                 {
1732                     if( !strcasecmp( "font", psz_node ) )
1733                         PopFont( &p_fonts );
1734                     else if( !strcasecmp( "b", psz_node ) )
1735                         b_bold   = VLC_FALSE;
1736                     else if( !strcasecmp( "i", psz_node ) )
1737                         b_italic = VLC_FALSE;
1738                     else if( !strcasecmp( "u", psz_node ) )
1739                         b_uline  = VLC_FALSE;
1740
1741                     free( psz_node );
1742                 }
1743                 break;
1744             case XML_READER_STARTELEM:
1745                 psz_node = xml_ReaderName( p_xml_reader );
1746                 if( psz_node )
1747                 {
1748                     if( !strcasecmp( "font", psz_node ) )
1749                         rv = HandleFontAttributes( p_xml_reader, &p_fonts );
1750                     else if( !strcasecmp( "b", psz_node ) )
1751                         b_bold = VLC_TRUE;
1752                     else if( !strcasecmp( "i", psz_node ) )
1753                         b_italic = VLC_TRUE;
1754                     else if( !strcasecmp( "u", psz_node ) )
1755                         b_uline = VLC_TRUE;
1756                     else if( !strcasecmp( "br", psz_node ) )
1757                     {
1758                         SetupLine( p_filter, "\n", &psz_text,
1759                                    pi_runs, ppi_run_lengths, ppp_styles,
1760                                    GetStyleFromFontStack( p_sys,
1761                                                           &p_fonts,
1762                                                           b_bold,
1763                                                           b_italic,
1764                                                           b_uline ) );
1765                     }
1766
1767                     free( psz_node );
1768                 }
1769                 break;
1770             case XML_READER_TEXT:
1771                 psz_node = xml_ReaderValue( p_xml_reader );
1772                 if( psz_node )
1773                 {
1774                     /* Turn any multiple-whitespaces into single spaces */
1775                     char *s = strpbrk( psz_node, "\t\r\n " );
1776                     while( s )
1777                     {
1778                         int i_whitespace = strspn( s, "\t\r\n " );
1779
1780                         if( i_whitespace > 1 )
1781                             memmove( &s[1],
1782                                      &s[i_whitespace],
1783                                      strlen( s ) - i_whitespace + 1 );
1784                         *s++ = ' ';
1785
1786                         s = strpbrk( s, "\t\r\n " );
1787                     }
1788                     SetupLine( p_filter, psz_node, &psz_text,
1789                                pi_runs, ppi_run_lengths, ppp_styles,
1790                                GetStyleFromFontStack( p_sys,
1791                                                       &p_fonts,
1792                                                       b_bold,
1793                                                       b_italic,
1794                                                       b_uline ) );
1795                     free( psz_node );
1796                 }
1797                 break;
1798         }
1799         if( rv != VLC_SUCCESS )
1800         {
1801             psz_text = psz_text_orig;
1802             break;
1803         }
1804     }
1805
1806     *pi_len = psz_text - psz_text_orig;
1807
1808     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1809
1810     return rv;
1811 }
1812
1813 static int ProcessLines( filter_t *p_filter, uint32_t *psz_text,
1814                          int i_len, uint32_t i_runs,
1815                          uint32_t *pi_run_lengths, ft_style_t **pp_styles,
1816                          line_desc_t **pp_lines, FT_Vector *p_result )
1817 {
1818     filter_sys_t   *p_sys = p_filter->p_sys;
1819     ft_style_t     **pp_char_styles;
1820     uint32_t        i, j, k;
1821     int             i_prev;
1822
1823     /* Assign each character in the text string its style explicitly, so that
1824      * after the characters have been shuffled around by Fribidi, we can re-apply
1825      * the styles, and to simplify the calculation of runs within a line.
1826      */
1827     pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1828     if( !pp_char_styles )
1829         return VLC_ENOMEM;
1830
1831     i = 0;
1832     for( j = 0; j < i_runs; j++ )
1833         for( k = 0; k < pi_run_lengths[ j ]; k++ )
1834             pp_char_styles[ i++ ] = pp_styles[ j ];
1835
1836 #if defined(HAVE_FRIBIDI)
1837     {
1838         ft_style_t  **pp_char_styles_new;
1839         int         *p_positions;
1840         uint32_t    *p_fribidi_string;
1841         int start_pos, pos = 0;
1842
1843         p_fribidi_string = malloc( (i_len + 1) * sizeof(uint32_t) );
1844         if(! p_fribidi_string )
1845         {
1846             msg_Err( p_filter, "out of memory" );
1847             free( pp_char_styles );
1848             return VLC_ENOMEM;
1849         }
1850         pp_char_styles_new  = (ft_style_t **)
1851                                        malloc( i_len * sizeof( ft_style_t * ));
1852         if(! pp_char_styles_new )
1853         {
1854             msg_Err( p_filter, "out of memory" );
1855             free( p_fribidi_string );
1856             free( pp_char_styles );
1857             return VLC_ENOMEM;
1858         }
1859         p_positions = (int *) malloc( (i_len + 1) * sizeof( int ) );
1860         if(! p_positions )
1861         {
1862             msg_Err( p_filter, "out of memory" );
1863             free( pp_char_styles_new );
1864             free( p_fribidi_string );
1865             free( pp_char_styles );
1866             return VLC_ENOMEM;
1867         }
1868
1869         /* Do bidi conversion line-by-line */
1870         while(pos < i_len)
1871         {
1872             while(pos < i_len) {
1873                 if (psz_text[pos] != '\n')
1874                     break;
1875                 p_fribidi_string[pos] = psz_text[pos];
1876                 pp_char_styles_new[pos] = pp_char_styles[pos];
1877                 ++pos;
1878             }
1879             start_pos = pos;
1880             while(pos < i_len) {
1881                 if (psz_text[pos] == '\n')
1882                     break;
1883                 ++pos;
1884             }
1885             if (pos > start_pos)
1886             {
1887                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1888                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1889                         pos - start_pos, &base_dir,
1890                         (FriBidiChar*)p_fribidi_string + start_pos,
1891                         0,
1892                         p_positions, 0);
1893
1894                 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1895                 {
1896                     pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1897                                                 p_positions[ j - start_pos ] ];
1898                 }
1899             }
1900         }
1901         free( p_positions );
1902         free( pp_char_styles );
1903         pp_char_styles = pp_char_styles_new;
1904         psz_text = p_fribidi_string;
1905         p_fribidi_string[ i_len ] = 0;
1906     }
1907 #endif
1908     FT_Vector tmp_result;
1909
1910     line_desc_t *p_line = NULL;
1911     line_desc_t *p_prev = NULL;
1912
1913     int i_pen_x = 0;
1914     int i_pen_y = 0;
1915     int i_posn  = 0;
1916
1917     p_result->x = p_result->y = 0;
1918     tmp_result.x = tmp_result.y = 0;
1919
1920     i_prev = 0;
1921     for( k = 0; k <= (uint32_t) i_len; k++ )
1922     {
1923         if( ( k == (uint32_t) i_len ) ||
1924           ( ( k > 0 ) &&
1925             !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1926         {
1927             ft_style_t *p_style = pp_char_styles[ k - 1 ];
1928
1929             /* End of the current style run */
1930             FT_Face p_face = NULL;
1931             int      i_idx = 0;
1932             char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
1933                                                     p_style->psz_fontname,
1934                                                     p_style->b_bold,
1935                                                     p_style->b_italic,
1936                                                     &i_idx );
1937             if( psz_fontfile )
1938             {
1939                 if( FT_New_Face( p_sys->p_library,
1940                             psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
1941                 {
1942                     free( psz_fontfile );
1943                     free( pp_char_styles );
1944 #if defined(HAVE_FRIBIDI)
1945                     free( psz_text );
1946 #endif
1947                     return VLC_EGENERIC;
1948                 }
1949                 free( psz_fontfile );
1950             }
1951
1952             if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1953                         ft_encoding_unicode ) ||
1954                 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1955                     p_style->i_font_size ) )
1956             {
1957                 if( p_face ) FT_Done_Face( p_face );
1958                 free( pp_char_styles );
1959 #if defined(HAVE_FRIBIDI)
1960                 free( psz_text );
1961 #endif
1962                 return VLC_EGENERIC;
1963             }
1964             p_sys->i_use_kerning =
1965                         FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
1966
1967
1968             uint32_t *psz_unicode = (uint32_t *)
1969                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1970             if( !psz_unicode )
1971             {
1972                 msg_Err( p_filter, "out of memory" );
1973                 if( p_face ) FT_Done_Face( p_face );
1974                 free( pp_char_styles );
1975                 free( psz_unicode );
1976 #if defined(HAVE_FRIBIDI)
1977                 free( psz_text );
1978 #endif
1979                 return VLC_ENOMEM;
1980             }
1981             memcpy( psz_unicode, psz_text + i_prev,
1982                                         sizeof( uint32_t ) * ( k - i_prev ) );
1983             psz_unicode[ k - i_prev ] = 0;
1984             while( *psz_unicode )
1985             {
1986                 if( !p_line )
1987                 {
1988                     if( !(p_line = NewLine( i_len - i_prev)) )
1989                     {
1990                         msg_Err( p_filter, "out of memory" );
1991                         if( p_face ) FT_Done_Face( p_face );
1992                         free( pp_char_styles );
1993                         free( psz_unicode );
1994 #if defined(HAVE_FRIBIDI)
1995                         free( psz_text );
1996 #endif
1997                         return VLC_ENOMEM;
1998                     }
1999                     /* New Color mode only works in YUVA rendering mode --
2000                      * (RGB mode has palette constraints on it). We therefore
2001                      * need to populate the legacy colour fields also.
2002                      */
2003                     p_line->b_new_color_mode = VLC_TRUE;
2004                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2005                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2006                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
2007                     p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
2008                     p_line->p_next = NULL;
2009                     i_pen_x = 0;
2010                     i_pen_y += tmp_result.y;
2011                     tmp_result.x = 0;
2012                     tmp_result.y = 0;
2013                     i_posn = 0;
2014                     if( p_prev ) p_prev->p_next = p_line;
2015                     else *pp_lines = p_line;
2016                 }
2017                 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2018                                p_style->i_font_color, p_style->b_underline,
2019                                p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2020                                &tmp_result ) != VLC_SUCCESS )
2021                 {
2022                     if( p_face ) FT_Done_Face( p_face );
2023                     free( pp_char_styles );
2024                     free( psz_unicode );
2025 #if defined(HAVE_FRIBIDI)
2026                     free( psz_text );
2027 #endif
2028                     return VLC_EGENERIC;
2029                 }
2030                 if( *psz_unicode )
2031                 {
2032                     p_result->x = __MAX( p_result->x, tmp_result.x );
2033                     p_result->y += tmp_result.y;
2034
2035                     p_prev = p_line;
2036                     p_line = NULL;
2037                 }
2038             }
2039             free( psz_unicode );
2040             if( p_face ) FT_Done_Face( p_face );
2041             i_prev = k;
2042         }
2043     }
2044     free( pp_char_styles );
2045 #if defined(HAVE_FRIBIDI)
2046     free( psz_text );
2047 #endif
2048
2049     if( p_line )
2050     {
2051         p_result->x = __MAX( p_result->x, tmp_result.x );
2052         p_result->y += tmp_result.y;
2053     }
2054     return VLC_SUCCESS;
2055 }
2056
2057 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2058                        subpicture_region_t *p_region_in )
2059 {
2060     int          rv = VLC_SUCCESS;
2061     stream_t     *p_sub = NULL;
2062     xml_t        *p_xml = NULL;
2063     xml_reader_t *p_xml_reader = NULL;
2064
2065     if( !p_region_in || !p_region_in->psz_html )
2066         return VLC_EGENERIC;
2067
2068     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2069                               (uint8_t *) p_region_in->psz_html,
2070                               strlen( p_region_in->psz_html ),
2071                               VLC_TRUE );
2072     if( p_sub )
2073     {
2074         p_xml = xml_Create( p_filter );
2075         if( p_xml )
2076         {
2077             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2078             if( p_xml_reader )
2079             {
2080                 uint32_t   *psz_text;
2081                 int         i_len;
2082                 uint32_t    i_runs = 0;
2083                 uint32_t   *pi_run_lengths = NULL;
2084                 ft_style_t  **pp_styles = NULL;
2085                 FT_Vector    result;
2086                 line_desc_t  *p_lines = NULL;
2087
2088                 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2089                                                 sizeof( uint32_t ) );
2090                 if( psz_text )
2091                 {
2092                     uint32_t k;
2093
2094                     rv = ProcessNodes( p_filter, p_xml_reader,
2095                                   p_region_in->p_style, psz_text, &i_len,
2096                                   &i_runs, &pi_run_lengths, &pp_styles );
2097
2098                     p_region_out->i_x = p_region_in->i_x;
2099                     p_region_out->i_y = p_region_in->i_y;
2100
2101                     if( rv == VLC_SUCCESS )
2102                     {
2103                         rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2104                                 pi_run_lengths, pp_styles, &p_lines, &result );
2105                     }
2106
2107                     for( k=0; k<i_runs; k++)
2108                         DeleteStyle( pp_styles[k] );
2109                     free( pp_styles );
2110                     free( pi_run_lengths );
2111                     free( psz_text );
2112
2113                     /* Don't attempt to render text that couldn't be layed out
2114                      * properly.
2115                      */
2116                     if( rv == VLC_SUCCESS )
2117                     {
2118                         if( config_GetInt( p_filter, "freetype-yuvp" ) )
2119                         {
2120                             Render( p_filter, p_region_out, p_lines,
2121                                     result.x, result.y );
2122                         }
2123                         else
2124                         {
2125                             RenderYUVA( p_filter, p_region_out, p_lines,
2126                                     result.x, result.y );
2127                         }
2128                     }
2129                 }
2130                 FreeLines( p_lines );
2131
2132                 xml_ReaderDelete( p_xml, p_xml_reader );
2133             }
2134             xml_Delete( p_xml );
2135         }
2136         stream_Delete( p_sub );
2137     }
2138
2139     return rv;
2140 }
2141
2142 static char* FontConfig_Select( FcConfig* priv, const char* family,
2143                           vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2144 {
2145     FcResult result;
2146     FcPattern *pat, *p_pat;
2147     FcChar8* val_s;
2148     FcBool val_b;
2149
2150     pat = FcPatternCreate();
2151     if (!pat) return NULL;
2152
2153     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2154     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2155     FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2156     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2157
2158     FcDefaultSubstitute( pat );
2159
2160     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2161     {
2162         FcPatternDestroy( pat );
2163         return NULL;
2164     }
2165
2166     p_pat = FcFontMatch( priv, pat, &result );
2167     FcPatternDestroy( pat );
2168     if( !p_pat ) return NULL;
2169
2170     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2171         || ( val_b != FcTrue ) )
2172     {
2173         FcPatternDestroy( p_pat );
2174         return NULL;
2175     }
2176     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2177     {
2178         *i_idx = 0;
2179     }
2180
2181     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2182     {
2183         FcPatternDestroy( p_pat );
2184         return NULL;
2185     }
2186
2187     /*
2188     if( strcasecmp((const char*)val_s, family ) != 0 )
2189         msg_Warn( p_filter, "fontconfig: selected font family is not"
2190                             "the requested one: '%s' != '%s'\n",
2191                             (const char*)val_s, family );
2192     */
2193
2194     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2195     {
2196         FcPatternDestroy( p_pat );
2197         return NULL;
2198     }
2199
2200     FcPatternDestroy( p_pat );
2201     return strdup( (const char*)val_s );
2202 }
2203 #endif
2204
2205 static void FreeLine( line_desc_t *p_line )
2206 {
2207     unsigned int i;
2208     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2209     {
2210         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2211     }
2212     free( p_line->pp_glyphs );
2213     free( p_line->p_glyph_pos );
2214     free( p_line->p_rgb );
2215     free( p_line->pi_underline_offset );
2216     free( p_line->pi_underline_thickness );
2217     free( p_line );
2218 }
2219
2220 static void FreeLines( line_desc_t *p_lines )
2221 {
2222     line_desc_t *p_line, *p_next;
2223
2224     if( !p_lines ) return;
2225
2226     for( p_line = p_lines; p_line != NULL; p_line = p_next )
2227     {
2228         p_next = p_line->p_next;
2229         FreeLine( p_line );
2230     }
2231 }
2232
2233 static line_desc_t *NewLine( int i_count )
2234 {
2235     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2236
2237     if( !p_line ) return NULL;
2238     p_line->i_height = 0;
2239     p_line->i_width = 0;
2240     p_line->p_next = NULL;
2241
2242     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2243     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2244     p_line->p_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2245     p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2246     p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2247     if( ( p_line->pp_glyphs == NULL ) ||
2248         ( p_line->p_glyph_pos == NULL ) ||
2249         ( p_line->p_rgb == NULL ) ||
2250         ( p_line->pi_underline_offset == NULL ) ||
2251         ( p_line->pi_underline_thickness == NULL ) )
2252     {
2253         if( p_line->pi_underline_thickness )
2254             free( p_line->pi_underline_thickness );
2255         if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2256         if( p_line->p_rgb ) free( p_line->p_rgb );
2257         if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2258         if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2259         free( p_line );
2260         return NULL;
2261     }
2262     p_line->pp_glyphs[0] = NULL;
2263     p_line->b_new_color_mode = VLC_FALSE;
2264
2265     return p_line;
2266 }
2267
2268 static int SetFontSize( filter_t *p_filter, int i_size )
2269 {
2270     filter_sys_t *p_sys = p_filter->p_sys;
2271
2272     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
2273
2274     if( !i_size )
2275     {
2276         vlc_value_t val;
2277
2278         if( !p_sys->i_default_font_size &&
2279             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
2280             return VLC_SUCCESS;
2281
2282         if( p_sys->i_default_font_size )
2283         {
2284             i_size = p_sys->i_default_font_size;
2285         }
2286         else
2287         {
2288             var_Get( p_filter, "freetype-rel-fontsize", &val );
2289             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2290             p_filter->p_sys->i_display_height =
2291                 p_filter->fmt_out.video.i_height;
2292         }
2293         if( i_size <= 0 )
2294         {
2295             msg_Warn( p_filter, "invalid fontsize, using 12" );
2296             i_size = 12;
2297         }
2298
2299         msg_Dbg( p_filter, "using fontsize: %i", i_size );
2300     }
2301
2302     p_sys->i_font_size = i_size;
2303
2304     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2305     {
2306         msg_Err( p_filter, "couldn't set font size to %d", i_size );
2307         return VLC_EGENERIC;
2308     }
2309
2310     return VLC_SUCCESS;
2311 }