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