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