]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
small codingstyle fix
[vlc] / modules / misc / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
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
30 #include <vlc/vlc.h>
31 #include <vlc_vout.h>
32 #include <vlc_osd.h>
33 #include <vlc_block.h>
34 #include <vlc_filter.h>
35 #include <vlc_stream.h>
36 #include <vlc_xml.h>
37 #include <vlc_input.h>
38
39 #ifdef HAVE_LINUX_LIMITS_H
40 #   include <linux/limits.h>
41 #endif
42
43 #include <time.h>
44 #include <math.h>
45 #include <errno.h>
46
47 #include <ft2build.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_H
50 #define FT_FLOOR(X)     ((X & -64) >> 6)
51 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
52 #define FT_MulFix(v, s) (((v)*(s))>>16)
53
54 #ifdef __APPLE__
55 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
56 #define FC_DEFAULT_FONT "Lucida Grande"
57 #elif defined( SYS_BEOS )
58 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
59 #define FC_DEFAULT_FONT "Swiss"
60 #elif defined( WIN32 )
61 #define DEFAULT_FONT "" /* Default font found at run-time */
62 #define FC_DEFAULT_FONT "Arial"
63 #else
64 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
65 #define FC_DEFAULT_FONT "Serif Bold"
66 #endif
67
68 #if defined(HAVE_FRIBIDI)
69 #include <fribidi/fribidi.h>
70 #endif
71
72 #ifdef HAVE_FONTCONFIG
73 #include <fontconfig/fontconfig.h>
74 #endif
75
76 typedef struct line_desc_t line_desc_t;
77
78 /*****************************************************************************
79  * Local prototypes
80  *****************************************************************************/
81 static int  Create ( vlc_object_t * );
82 static void Destroy( vlc_object_t * );
83
84 static int LoadFontsFromAttachments( filter_t *p_filter );
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( int );
96
97 static int GetFontSize( filter_t *p_filter );
98 static int SetFontSize( filter_t *, int );
99 static void YUVFromRGB( uint32_t i_argb,
100                         uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
101
102 /*****************************************************************************
103  * Module descriptor
104  *****************************************************************************/
105 #define FONT_TEXT N_("Font")
106 #define FONT_LONGTEXT N_("Filename for the font you want to use")
107 #define FONTSIZE_TEXT N_("Font size in pixels")
108 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
109     "that will be rendered on the video. " \
110     "If set to something different than 0 this option will override the " \
111     "relative font size." )
112 #define OPACITY_TEXT N_("Opacity")
113 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
114     "text that will be rendered on the video. 0 = transparent, " \
115     "255 = totally opaque. " )
116 #define COLOR_TEXT N_("Text default color")
117 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
118     "the video. This must be an hexadecimal (like HTML colors). The first two "\
119     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
120     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
121 #define FONTSIZER_TEXT N_("Relative font size")
122 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
123     "fonts that will be rendered on the video. If absolute font size is set, "\
124     "relative size will be overriden." )
125
126 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
127 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
128                                          N_("Large"), N_("Larger") };
129 #define YUVP_TEXT N_("Use YUVP renderer")
130 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
131   "This option is only needed if you want to encode into DVB subtitles" )
132 #define EFFECT_TEXT N_("Font Effect")
133 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
134 "text to improve its readability." )
135
136 #define EFFECT_BACKGROUND  1
137 #define EFFECT_OUTLINE     2
138 #define EFFECT_OUTLINE_FAT 3
139
140 static int   pi_effects[] = { 1, 2, 3 };
141 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
142                                            N_("Fat Outline") };
143 static int pi_color_values[] = {
144   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
145   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
146   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
147
148 static const char *ppsz_color_descriptions[] = {
149   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
150   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
151   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
152
153 vlc_module_begin();
154     set_shortname( _("Text renderer"));
155     set_description( _("Freetype2 font renderer") );
156     set_category( CAT_VIDEO );
157     set_subcategory( SUBCAT_VIDEO_SUBPIC );
158
159     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
160               VLC_FALSE );
161
162     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
163                  FONTSIZE_LONGTEXT, VLC_TRUE );
164
165     /* opacity valid on 0..255, with default 255 = fully opaque */
166     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
167         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
168
169     /* hook to the color values list, with default 0x00ffffff = white */
170     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
171                  COLOR_LONGTEXT, VLC_FALSE );
172         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
173
174     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
175                  FONTSIZER_LONGTEXT, VLC_FALSE );
176         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
177     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
178                  EFFECT_LONGTEXT, VLC_FALSE );
179         change_integer_list( pi_effects, ppsz_effects_text, 0 );
180
181     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
182               YUVP_LONGTEXT, VLC_TRUE );
183     set_capability( "text renderer", 100 );
184     add_shortcut( "text" );
185     set_callbacks( Create, Destroy );
186 vlc_module_end();
187
188 struct line_desc_t
189 {
190     /** NULL-terminated list of glyphs making the string */
191     FT_BitmapGlyph *pp_glyphs;
192     /** list of relative positions for the glyphs */
193     FT_Vector      *p_glyph_pos;
194     /** list of RGB information for styled text
195      * -- if the rendering mode supports it (RenderYUVA) and
196      *  b_new_color_mode is set, then it becomes possible to
197      *  have multicoloured text within the subtitles. */
198     uint32_t       *p_fg_rgb;
199     uint32_t       *p_bg_rgb;
200     uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
201     vlc_bool_t      b_new_color_mode;
202     /** underline information -- only supplied if text should be underlined */
203     uint16_t       *pi_underline_offset;
204     uint16_t       *pi_underline_thickness;
205
206     int             i_height;
207     int             i_width;
208     int             i_red, i_green, i_blue;
209     int             i_alpha;
210
211     line_desc_t    *p_next;
212 };
213
214 typedef struct font_stack_t font_stack_t;
215 struct font_stack_t
216 {
217     char          *psz_name;
218     int            i_size;
219     uint32_t       i_color;            /* ARGB */
220     uint32_t       i_karaoke_bg_color; /* ARGB */
221
222     font_stack_t  *p_next;
223 };
224
225 typedef struct
226 {
227     int         i_font_size;
228     uint32_t    i_font_color;         /* ARGB */
229     uint32_t    i_karaoke_bg_color;   /* ARGB */
230     vlc_bool_t  b_italic;
231     vlc_bool_t  b_bold;
232     vlc_bool_t  b_underline;
233     char       *psz_fontname;
234 } ft_style_t;
235
236 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
237 static void FreeLines( line_desc_t * );
238 static void FreeLine( line_desc_t * );
239 #ifdef HAVE_FONTCONFIG
240 static void FontBuilder( vlc_object_t *p_this);
241 #endif
242
243 /*****************************************************************************
244  * filter_sys_t: freetype local data
245  *****************************************************************************
246  * This structure is part of the video output thread descriptor.
247  * It describes the freetype specific properties of an output thread.
248  *****************************************************************************/
249 struct filter_sys_t
250 {
251     FT_Library     p_library;   /* handle to library     */
252     FT_Face        p_face;      /* handle to face object */
253     vlc_bool_t     i_use_kerning;
254     uint8_t        i_font_opacity;
255     int            i_font_color;
256     int            i_font_size;
257     int            i_effect;
258
259     int            i_default_font_size;
260     int            i_display_height;
261 #ifdef HAVE_FONTCONFIG
262     FcConfig      *p_fontconfig;
263     vlc_bool_t     b_fontconfig_ok;
264     vlc_mutex_t    fontconfig_lock;
265 #endif
266
267     input_attachment_t **pp_font_attachments;
268     int                  i_font_attachments;
269 };
270
271 /*****************************************************************************
272  * Create: allocates osd-text video thread output method
273  *****************************************************************************
274  * This function allocates and initializes a Clone vout method.
275  *****************************************************************************/
276 static int Create( vlc_object_t *p_this )
277 {
278     filter_t *p_filter = (filter_t *)p_this;
279     filter_sys_t *p_sys;
280     char *psz_fontfile = NULL;
281     int i_error;
282     vlc_value_t val;
283
284     /* Allocate structure */
285     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
286     if( !p_sys )
287     {
288         msg_Err( p_filter, "out of memory" );
289         return VLC_ENOMEM;
290     }
291     p_sys->p_face = 0;
292     p_sys->p_library = 0;
293     p_sys->i_font_size = 0;
294     p_sys->i_display_height = 0;
295
296     var_Create( p_filter, "freetype-font",
297                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
298     var_Create( p_filter, "freetype-fontsize",
299                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
300     var_Create( p_filter, "freetype-rel-fontsize",
301                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
302     var_Create( p_filter, "freetype-opacity",
303                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
304     var_Create( p_filter, "freetype-effect",
305                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
306     var_Get( p_filter, "freetype-opacity", &val );
307     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
308     var_Create( p_filter, "freetype-color",
309                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
310     var_Get( p_filter, "freetype-color", &val );
311     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
312     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
313
314     /* Look what method was requested */
315     var_Get( p_filter, "freetype-font", &val );
316     psz_fontfile = val.psz_string;
317     if( !psz_fontfile || !*psz_fontfile )
318     {
319         if( psz_fontfile ) free( psz_fontfile );
320         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
321         if( !psz_fontfile )
322         {
323             msg_Err( p_filter, "out of memory" );
324             goto error;
325         }
326 #ifdef WIN32
327         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
328         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
329 #elif defined(__APPLE__)
330         strcpy( psz_fontfile, DEFAULT_FONT );
331 #else
332         msg_Err( p_filter, "user didn't specify a font" );
333         goto error;
334 #endif
335     }
336
337     i_error = FT_Init_FreeType( &p_sys->p_library );
338     if( i_error )
339     {
340         msg_Err( p_filter, "couldn't initialize freetype" );
341         goto error;
342     }
343     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
344                            0, &p_sys->p_face );
345     if( i_error == FT_Err_Unknown_File_Format )
346     {
347         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
348         goto error;
349     }
350     else if( i_error )
351     {
352         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
353         goto error;
354     }
355
356     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
357     if( i_error )
358     {
359         msg_Err( p_filter, "font has no unicode translation table" );
360         goto error;
361     }
362
363 #ifdef HAVE_FONTCONFIG
364     vlc_mutex_init( p_filter, &p_sys->fontconfig_lock );
365     p_sys->b_fontconfig_ok = VLC_FALSE;
366
367     p_sys->p_fontconfig = FcInitLoadConfig();
368
369     if( p_sys->p_fontconfig )
370     {
371         /* Normally this doesn't take very long, but an initial build of
372          * the fontconfig database or the addition of a lot of new fonts
373          * can cause it to take several minutes for a large number of fonts.
374          * Even a small number can take several seconds - much longer than
375          * we can afford to block, so we build the list in the background
376          * and if it succeeds we allow fontconfig to be used.
377          */
378         if( vlc_thread_create( p_filter, "fontlist builder", FontBuilder,
379                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
380         {
381             /* Don't destroy the fontconfig object - we won't be able to do
382              * italics or bold or change the font face, but we will still
383              * be able to do underline and change the font size.
384              */
385             msg_Warn( p_filter, "fontconfig database builder thread can't "
386                     "be launched. Font styling support will be limited." );
387         };
388     }
389     else
390     {
391         msg_Warn( p_filter, "Couldn't initialise Fontconfig. "
392                             "Font styling won't be available." );
393     }
394 #endif
395
396     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
397
398     var_Get( p_filter, "freetype-fontsize", &val );
399     p_sys->i_default_font_size = val.i_int;
400     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
401
402     if( psz_fontfile ) free( psz_fontfile );
403
404     p_sys->pp_font_attachments = NULL;
405     p_sys->i_font_attachments = 0;
406
407     p_filter->pf_render_text = RenderText;
408 #ifdef HAVE_FONTCONFIG
409     if( p_sys->p_fontconfig )
410         p_filter->pf_render_html = RenderHtml;
411     else
412 #endif
413         p_filter->pf_render_html = NULL;
414
415     LoadFontsFromAttachments( p_filter );
416
417     return VLC_SUCCESS;
418
419  error:
420     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
421     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
422     if( psz_fontfile ) free( psz_fontfile );
423     free( p_sys );
424     return VLC_EGENERIC;
425 }
426
427 /*****************************************************************************
428  * Destroy: destroy Clone video thread output method
429  *****************************************************************************
430  * Clean up all data and library connections
431  *****************************************************************************/
432 static void Destroy( vlc_object_t *p_this )
433 {
434     filter_t *p_filter = (filter_t *)p_this;
435     filter_sys_t *p_sys = p_filter->p_sys;
436
437     if( p_sys->pp_font_attachments )
438     {
439         int   k;
440
441         for( k = 0; k < p_sys->i_font_attachments; k++ )
442         {
443             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
444         }
445
446         free( p_sys->pp_font_attachments );
447     }
448
449 #ifdef HAVE_FONTCONFIG
450     /* wait for the FontBuilder thread to terminate */
451     vlc_thread_join( p_this );
452     vlc_mutex_destroy( &p_sys->fontconfig_lock );
453
454     if( p_sys->p_fontconfig )
455     {
456         FcConfigDestroy( p_sys->p_fontconfig );
457         p_sys->p_fontconfig = NULL;
458     }
459     /* FcFini asserts calling the subfunction FcCacheFini()
460      * even if no other library functions have been made since FcInit(),
461      * so don't call it. */
462 #endif
463     FT_Done_Face( p_sys->p_face );
464     FT_Done_FreeType( p_sys->p_library );
465     free( p_sys );
466 }
467
468 #ifdef HAVE_FONTCONFIG
469
470 static void FontBuilder( vlc_object_t *p_this )
471 {
472     filter_t *p_filter = (filter_t*)p_this;
473     filter_sys_t *p_sys = p_filter->p_sys;
474     time_t    t1, t2;
475
476     /* Find the session to announce */
477     vlc_mutex_lock( &p_sys->fontconfig_lock );
478
479     msg_Dbg( p_filter, "Building font database..." );
480     time(&t1);
481     if(! FcConfigBuildFonts( p_sys->p_fontconfig ))
482     {
483         /* Don't destroy the fontconfig object - we won't be able to do
484          * italics or bold or change the font face, but we will still
485          * be able to do underline and change the font size.
486          */
487         msg_Err( p_filter, "fontconfig database can't be built. "
488                                 "Font styling won't be available" );
489     }
490     time(&t2);
491
492     msg_Dbg( p_filter, "Finished building font database." );
493     if( t1 > 0 && t2 > 0 )
494         msg_Dbg( p_filter, "Took %ld seconds", t2 - t1 );
495
496     p_sys->b_fontconfig_ok = VLC_TRUE;
497
498     vlc_mutex_unlock( &p_sys->fontconfig_lock );
499 }
500
501 #endif
502
503 /*****************************************************************************
504  * Make any TTF/OTF fonts present in the attachments of the media file
505  * and store them for later use by the FreeType Engine
506  *****************************************************************************/
507 static int LoadFontsFromAttachments( filter_t *p_filter )
508 {
509     filter_sys_t         *p_sys = p_filter->p_sys;
510     input_thread_t       *p_input;
511     input_attachment_t  **pp_attachments;
512     int                   i_attachments_cnt;
513     int                   k;
514     int                   rv = VLC_SUCCESS;
515
516     p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
517     if( ! p_input )
518         return VLC_EGENERIC;
519
520     if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
521     {
522         vlc_object_release(p_input);
523         return VLC_EGENERIC;
524     }
525
526     p_sys->i_font_attachments = 0;
527     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
528     if(! p_sys->pp_font_attachments )
529         rv = VLC_ENOMEM;
530
531     for( k = 0; k < i_attachments_cnt; k++ )
532     {
533         input_attachment_t *p_attach = pp_attachments[k];
534
535         if( p_sys->pp_font_attachments )
536         {
537             if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
538                  !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
539                ( p_attach->i_data > 0 ) &&
540                ( p_attach->p_data != NULL ) )
541             {
542                 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
543             }
544             else
545             {
546                 vlc_input_attachment_Delete( p_attach );
547             }
548         }
549         else
550         {
551             vlc_input_attachment_Delete( p_attach );
552         }
553     }
554     free( pp_attachments );
555
556     vlc_object_release(p_input);
557
558     return rv;
559 }
560
561 /*****************************************************************************
562  * Render: place string in picture
563  *****************************************************************************
564  * This function merges the previously rendered freetype glyphs into a picture
565  *****************************************************************************/
566 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
567                    line_desc_t *p_line, int i_width, int i_height )
568 {
569     static uint8_t pi_gamma[16] =
570         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
571           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
572
573     uint8_t *p_dst;
574     video_format_t fmt;
575     int i, x, y, i_pitch;
576     uint8_t i_y; /* YUV values, derived from incoming RGB */
577     int8_t i_u, i_v;
578     subpicture_region_t *p_region_tmp;
579
580     /* Create a new subpicture region */
581     memset( &fmt, 0, sizeof(video_format_t) );
582     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
583     fmt.i_aspect = 0;
584     fmt.i_width = fmt.i_visible_width = i_width + 4;
585     fmt.i_height = fmt.i_visible_height = i_height + 4;
586     if( p_region->fmt.i_visible_width > 0 )
587         fmt.i_visible_width = p_region->fmt.i_visible_width;
588     if( p_region->fmt.i_visible_height > 0 )
589         fmt.i_visible_height = p_region->fmt.i_visible_height;
590     fmt.i_x_offset = fmt.i_y_offset = 0;
591     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
592     if( !p_region_tmp )
593     {
594         msg_Err( p_filter, "cannot allocate SPU region" );
595         return VLC_EGENERIC;
596     }
597
598     p_region->fmt = p_region_tmp->fmt;
599     p_region->picture = p_region_tmp->picture;
600     free( p_region_tmp );
601
602     /* Calculate text color components */
603     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
604                       25 * p_line->i_blue + 128) >> 8) +  16;
605     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
606                      112 * p_line->i_blue + 128) >> 8) + 128;
607     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
608                       18 * p_line->i_blue + 128) >> 8) + 128;
609
610     /* Build palette */
611     fmt.p_palette->i_entries = 16;
612     for( i = 0; i < 8; i++ )
613     {
614         fmt.p_palette->palette[i][0] = 0;
615         fmt.p_palette->palette[i][1] = 0x80;
616         fmt.p_palette->palette[i][2] = 0x80;
617         fmt.p_palette->palette[i][3] = pi_gamma[i];
618         fmt.p_palette->palette[i][3] =
619             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
620     }
621     for( i = 8; i < fmt.p_palette->i_entries; i++ )
622     {
623         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
624         fmt.p_palette->palette[i][1] = i_u;
625         fmt.p_palette->palette[i][2] = i_v;
626         fmt.p_palette->palette[i][3] = pi_gamma[i];
627         fmt.p_palette->palette[i][3] =
628             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
629     }
630
631     p_dst = p_region->picture.Y_PIXELS;
632     i_pitch = p_region->picture.Y_PITCH;
633
634     /* Initialize the region pixels */
635     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
636
637     for( ; p_line != NULL; p_line = p_line->p_next )
638     {
639         int i_glyph_tmax = 0;
640         int i_bitmap_offset, i_offset, i_align_offset = 0;
641         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
642         {
643             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
644             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
645         }
646
647         if( p_line->i_width < i_width )
648         {
649             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
650             {
651                 i_align_offset = i_width - p_line->i_width;
652             }
653             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
654             {
655                 i_align_offset = ( i_width - p_line->i_width ) / 2;
656             }
657         }
658
659         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
660         {
661             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
662
663             i_offset = ( p_line->p_glyph_pos[ i ].y +
664                 i_glyph_tmax - p_glyph->top + 2 ) *
665                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
666                 i_align_offset;
667
668             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
669             {
670                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
671                 {
672                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
673                         p_dst[i_offset+x] =
674                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
675                 }
676                 i_offset += i_pitch;
677             }
678         }
679     }
680
681     /* Outlining (find something better than nearest neighbour filtering ?) */
682     if( 1 )
683     {
684         uint8_t *p_dst = p_region->picture.Y_PIXELS;
685         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
686         uint8_t left, current;
687
688         for( y = 1; y < (int)fmt.i_height - 1; y++ )
689         {
690             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
691             p_dst += p_region->picture.Y_PITCH;
692             left = 0;
693
694             for( x = 1; x < (int)fmt.i_width - 1; x++ )
695             {
696                 current = p_dst[x];
697                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
698                              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;
699                 left = current;
700             }
701         }
702         memset( p_top, 0, fmt.i_width );
703     }
704
705     return VLC_SUCCESS;
706 }
707
708 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
709                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
710                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
711                                 int i_glyph_tmax, int i_align_offset,
712                                 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
713                                 subpicture_region_t *p_region)
714 {
715     int y, x, z;
716     int i_pitch;
717     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
718
719     p_dst_y = p_region->picture.Y_PIXELS;
720     p_dst_u = p_region->picture.U_PIXELS;
721     p_dst_v = p_region->picture.V_PIXELS;
722     p_dst_a = p_region->picture.A_PIXELS;
723     i_pitch = p_region->picture.A_PITCH;
724
725     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
726                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
727
728     for( y = 0; y < i_line_thickness; y++ )
729     {
730         int i_extra = p_this_glyph->bitmap.width;
731
732         if( b_ul_next_char )
733         {
734             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
735                       (p_this_glyph_pos->x + p_this_glyph->left);
736         }
737         for( x = 0; x < i_extra; x++ )
738         {
739             vlc_bool_t b_ok = VLC_TRUE;
740
741             /* break the underline around the tails of any glyphs which cross it */
742             for( z = x - i_line_thickness;
743                  z < x + i_line_thickness && b_ok;
744                  z++ )
745             {
746                 if( p_next_glyph && ( z >= i_extra ) )
747                 {
748                     int i_row = i_line_offset + p_next_glyph->top + y;
749
750                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
751                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
752                     {
753                         b_ok = VLC_FALSE;
754                     }
755                 }
756                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
757                 {
758                     int i_row = i_line_offset + p_this_glyph->top + y;
759
760                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
761                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
762                     {
763                         b_ok = VLC_FALSE;
764                     }
765                 }
766             }
767
768             if( b_ok )
769             {
770                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
771                 p_dst_u[i_offset+x] = i_u;
772                 p_dst_v[i_offset+x] = i_v;
773                 p_dst_a[i_offset+x] = 255;
774             }
775         }
776         i_offset += i_pitch;
777     }
778 }
779
780 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
781 {
782     uint8_t *p_dst = p_region->picture.A_PIXELS;
783     int i_pitch = p_region->picture.A_PITCH;
784     int x,y;
785
786     for( ; p_line != NULL; p_line = p_line->p_next )
787     {
788         int i_glyph_tmax=0, i = 0;
789         int i_bitmap_offset, i_offset, i_align_offset = 0;
790         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
791         {
792             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
793             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
794         }
795
796         if( p_line->i_width < i_width )
797         {
798             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
799             {
800                 i_align_offset = i_width - p_line->i_width;
801             }
802             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
803             {
804                 i_align_offset = ( i_width - p_line->i_width ) / 2;
805             }
806         }
807
808         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
809         {
810             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
811
812             i_offset = ( p_line->p_glyph_pos[ i ].y +
813                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
814                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
815                 i_align_offset +xoffset;
816
817             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
818             {
819                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
820                 {
821                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
822                         if( p_dst[i_offset+x] <
823                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
824                             p_dst[i_offset+x] =
825                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
826                 }
827                 i_offset += i_pitch;
828             }
829         }
830     }
831
832 }
833
834 /*****************************************************************************
835  * Render: place string in picture
836  *****************************************************************************
837  * This function merges the previously rendered freetype glyphs into a picture
838  *****************************************************************************/
839 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
840                    line_desc_t *p_line, int i_width, int i_height )
841 {
842     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
843     video_format_t fmt;
844     int i, x, y, i_pitch, i_alpha;
845     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
846     subpicture_region_t *p_region_tmp;
847
848     if( i_width == 0 || i_height == 0 )
849         return VLC_SUCCESS;
850
851     /* Create a new subpicture region */
852     memset( &fmt, 0, sizeof(video_format_t) );
853     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
854     fmt.i_aspect = 0;
855     fmt.i_width = fmt.i_visible_width = i_width + 6;
856     fmt.i_height = fmt.i_visible_height = i_height + 6;
857     if( p_region->fmt.i_visible_width > 0 )
858         fmt.i_visible_width = p_region->fmt.i_visible_width;
859     if( p_region->fmt.i_visible_height > 0 )
860         fmt.i_visible_height = p_region->fmt.i_visible_height;
861     fmt.i_x_offset = fmt.i_y_offset = 0;
862     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
863     if( !p_region_tmp )
864     {
865         msg_Err( p_filter, "cannot allocate SPU region" );
866         return VLC_EGENERIC;
867     }
868
869     p_region->fmt = p_region_tmp->fmt;
870     p_region->picture = p_region_tmp->picture;
871     free( p_region_tmp );
872
873     /* Calculate text color components */
874     YUVFromRGB( (p_line->i_red   << 16) |
875                 (p_line->i_green <<  8) |
876                 (p_line->i_blue       ),
877                 &i_y, &i_u, &i_v);
878     i_alpha = p_line->i_alpha;
879
880     p_dst_y = p_region->picture.Y_PIXELS;
881     p_dst_u = p_region->picture.U_PIXELS;
882     p_dst_v = p_region->picture.V_PIXELS;
883     p_dst_a = p_region->picture.A_PIXELS;
884     i_pitch = p_region->picture.A_PITCH;
885
886     /* Initialize the region pixels */
887     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
888     {
889         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
890         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
891         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
892         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
893     }
894     else
895     {
896         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
897         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
898         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
899         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
900     }
901     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
902         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
903     {
904         DrawBlack( p_line, i_width, p_region,  0,  0);
905         DrawBlack( p_line, i_width, p_region, -1,  0);
906         DrawBlack( p_line, i_width, p_region,  0, -1);
907         DrawBlack( p_line, i_width, p_region,  1,  0);
908         DrawBlack( p_line, i_width, p_region,  0,  1);
909     }
910
911     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
912     {
913         DrawBlack( p_line, i_width, p_region, -1, -1);
914         DrawBlack( p_line, i_width, p_region, -1,  1);
915         DrawBlack( p_line, i_width, p_region,  1, -1);
916         DrawBlack( p_line, i_width, p_region,  1,  1);
917
918         DrawBlack( p_line, i_width, p_region, -2,  0);
919         DrawBlack( p_line, i_width, p_region,  0, -2);
920         DrawBlack( p_line, i_width, p_region,  2,  0);
921         DrawBlack( p_line, i_width, p_region,  0,  2);
922
923         DrawBlack( p_line, i_width, p_region, -2, -2);
924         DrawBlack( p_line, i_width, p_region, -2,  2);
925         DrawBlack( p_line, i_width, p_region,  2, -2);
926         DrawBlack( p_line, i_width, p_region,  2,  2);
927
928         DrawBlack( p_line, i_width, p_region, -3,  0);
929         DrawBlack( p_line, i_width, p_region,  0, -3);
930         DrawBlack( p_line, i_width, p_region,  3,  0);
931         DrawBlack( p_line, i_width, p_region,  0,  3);
932     }
933
934     for( ; p_line != NULL; p_line = p_line->p_next )
935     {
936         int i_glyph_tmax = 0;
937         int i_bitmap_offset, i_offset, i_align_offset = 0;
938         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
939         {
940             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
941             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
942         }
943
944         if( p_line->i_width < i_width )
945         {
946             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
947             {
948                 i_align_offset = i_width - p_line->i_width;
949             }
950             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
951             {
952                 i_align_offset = ( i_width - p_line->i_width ) / 2;
953             }
954         }
955
956         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
957         {
958             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
959
960             i_offset = ( p_line->p_glyph_pos[ i ].y +
961                 i_glyph_tmax - p_glyph->top + 3 ) *
962                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
963                 i_align_offset;
964
965             if( p_line->b_new_color_mode )
966             {
967                 /* Every glyph can (and in fact must) have its own color */
968                 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
969             }
970
971             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
972             {
973                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
974                 {
975                     uint8_t i_y_local = i_y;
976                     uint8_t i_u_local = i_u;
977                     uint8_t i_v_local = i_v;
978
979                     if( p_line->p_fg_bg_ratio != 0x00 )
980                     {
981                         int i_split = p_glyph->bitmap.width *
982                                       p_line->p_fg_bg_ratio[ i ] / 0x7f;
983
984                         if( x > i_split )
985                         {
986                             YUVFromRGB( p_line->p_bg_rgb[ i ],
987                                         &i_y_local, &i_u_local, &i_v_local );
988                         }
989                     }
990
991                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
992                     {
993                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
994                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
995
996                         p_dst_u[i_offset+x] = i_u;
997                         p_dst_v[i_offset+x] = i_v;
998
999                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
1000                             p_dst_a[i_offset+x] = 0xff;
1001                     }
1002                 }
1003                 i_offset += i_pitch;
1004             }
1005
1006             if( p_line->pi_underline_thickness[ i ] )
1007             {
1008                 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
1009                                     p_line->pi_underline_offset[ i ],
1010                                    (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
1011                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
1012                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
1013                                     i_glyph_tmax, i_align_offset,
1014                                     i_y, i_u, i_v, i_alpha,
1015                                     p_region);
1016             }
1017         }
1018     }
1019
1020     /* Apply the alpha setting */
1021     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
1022         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
1023
1024     return VLC_SUCCESS;
1025 }
1026
1027 /**
1028  * This function renders a text subpicture region into another one.
1029  * It also calculates the size needed for this string, and renders the
1030  * needed glyphs into memory. It is used as pf_add_string callback in
1031  * the vout method by this module
1032  */
1033 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1034                        subpicture_region_t *p_region_in )
1035 {
1036     filter_sys_t *p_sys = p_filter->p_sys;
1037     line_desc_t  *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
1038     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
1039     uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
1040     int i_string_length;
1041     char *psz_string;
1042     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1043     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
1044     vlc_value_t val;
1045     int i_scale = 1000;
1046
1047     FT_BBox line;
1048     FT_BBox glyph_size;
1049     FT_Vector result;
1050     FT_Glyph tmp_glyph;
1051
1052     /* Sanity check */
1053     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
1054     psz_string = p_region_in->psz_text;
1055     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
1056
1057     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1058         i_scale = val.i_int;
1059
1060     if( p_region_in->p_style )
1061     {
1062         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
1063         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
1064         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
1065     }
1066     else
1067     {
1068         i_font_color = p_sys->i_font_color;
1069         i_font_alpha = 255 - p_sys->i_font_opacity;
1070         i_font_size  = p_sys->i_default_font_size * i_scale / 1000;
1071     }
1072
1073     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
1074     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
1075     SetFontSize( p_filter, i_font_size );
1076
1077     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
1078     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
1079     i_blue  =   i_font_color & 0x000000FF;
1080
1081     result.x =  result.y = 0;
1082     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1083
1084     psz_unicode = psz_unicode_orig =
1085         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
1086     if( psz_unicode == NULL )
1087     {
1088         msg_Err( p_filter, "out of memory" );
1089         goto error;
1090     }
1091 #if defined(WORDS_BIGENDIAN)
1092     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1093 #else
1094     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1095 #endif
1096     if( iconv_handle == (vlc_iconv_t)-1 )
1097     {
1098         msg_Warn( p_filter, "unable to do conversion" );
1099         goto error;
1100     }
1101
1102     {
1103         char *p_out_buffer;
1104         const char *p_in_buffer = psz_string;
1105         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1106         i_in_bytes = strlen( psz_string );
1107         i_out_bytes = i_in_bytes * sizeof( uint32_t );
1108         i_out_bytes_left = i_out_bytes;
1109         p_out_buffer = (char *)psz_unicode;
1110         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1111                            &p_out_buffer, &i_out_bytes_left );
1112
1113         vlc_iconv_close( iconv_handle );
1114
1115         if( i_in_bytes )
1116         {
1117             msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1118                       "bytes left %u", (unsigned)i_in_bytes );
1119             goto error;
1120         }
1121         *(uint32_t*)p_out_buffer = 0;
1122         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1123     }
1124
1125 #if defined(HAVE_FRIBIDI)
1126     {
1127         uint32_t *p_fribidi_string;
1128         int start_pos, pos = 0;
1129
1130         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1131         if( !p_fribidi_string )
1132         {
1133             msg_Err( p_filter, "out of memory" );
1134             goto error;
1135         }
1136
1137         /* Do bidi conversion line-by-line */
1138         while(pos < i_string_length)
1139         {
1140             while(pos < i_string_length) {
1141                 i_char = psz_unicode[pos];
1142                 if (i_char != '\r' && i_char != '\n')
1143                     break;
1144                 p_fribidi_string[pos] = i_char;
1145                 ++pos;
1146             }
1147             start_pos = pos;
1148             while(pos < i_string_length) {
1149                 i_char = psz_unicode[pos];
1150                 if (i_char == '\r' || i_char == '\n')
1151                     break;
1152                 ++pos;
1153             }
1154             if (pos > start_pos)
1155             {
1156                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1157                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
1158                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
1159             }
1160         }
1161
1162         free( psz_unicode_orig );
1163         psz_unicode = psz_unicode_orig = p_fribidi_string;
1164         p_fribidi_string[ i_string_length ] = 0;
1165     }
1166 #endif
1167
1168     /* Calculate relative glyph positions and a bounding box for the
1169      * entire string */
1170     if( !(p_line = NewLine( strlen( psz_string ))) )
1171     {
1172         msg_Err( p_filter, "out of memory" );
1173         goto error;
1174     }
1175     p_lines = p_line;
1176     i_pen_x = i_pen_y = 0;
1177     i_previous = i = 0;
1178     psz_line_start = psz_unicode;
1179
1180 #define face p_sys->p_face
1181 #define glyph face->glyph
1182
1183     while( *psz_unicode )
1184     {
1185         i_char = *psz_unicode++;
1186         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1187         {
1188             continue;
1189         }
1190
1191         if( i_char == '\n' )
1192         {
1193             psz_line_start = psz_unicode;
1194             if( !(p_next = NewLine( strlen( psz_string ))) )
1195             {
1196                 msg_Err( p_filter, "out of memory" );
1197                 goto error;
1198             }
1199             p_line->p_next = p_next;
1200             p_line->i_width = line.xMax;
1201             p_line->i_height = face->size->metrics.height >> 6;
1202             p_line->pp_glyphs[ i ] = NULL;
1203             p_line->i_alpha = i_font_alpha;
1204             p_line->i_red = i_red;
1205             p_line->i_green = i_green;
1206             p_line->i_blue = i_blue;
1207             p_prev = p_line;
1208             p_line = p_next;
1209             result.x = __MAX( result.x, line.xMax );
1210             result.y += face->size->metrics.height >> 6;
1211             i_pen_x = 0;
1212             i_previous = i = 0;
1213             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1214             i_pen_y += face->size->metrics.height >> 6;
1215 #if 0
1216             msg_Dbg( p_filter, "Creating new line, i is %d", i );
1217 #endif
1218             continue;
1219         }
1220
1221         i_glyph_index = FT_Get_Char_Index( face, i_char );
1222         if( p_sys->i_use_kerning && i_glyph_index
1223             && i_previous )
1224         {
1225             FT_Vector delta;
1226             FT_Get_Kerning( face, i_previous, i_glyph_index,
1227                             ft_kerning_default, &delta );
1228             i_pen_x += delta.x >> 6;
1229
1230         }
1231         p_line->p_glyph_pos[ i ].x = i_pen_x;
1232         p_line->p_glyph_pos[ i ].y = i_pen_y;
1233         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1234         if( i_error )
1235         {
1236             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1237                                " %d", i_error );
1238             goto error;
1239         }
1240         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1241         if( i_error )
1242         {
1243             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1244                                "%d", i_error );
1245             goto error;
1246         }
1247         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1248         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1249         if( i_error )
1250         {
1251             FT_Done_Glyph( tmp_glyph );
1252             continue;
1253         }
1254         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1255
1256         /* Do rest */
1257         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1258             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1259         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1260         {
1261             p_line->pp_glyphs[ i ] = NULL;
1262             FreeLine( p_line );
1263             p_line = NewLine( strlen( psz_string ));
1264             if( p_prev ) p_prev->p_next = p_line;
1265             else p_lines = p_line;
1266
1267             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1268             {
1269                 psz_unicode--;
1270             }
1271             if( psz_unicode == psz_line_start )
1272             {
1273                 msg_Warn( p_filter, "unbreakable string" );
1274                 goto error;
1275             }
1276             else
1277             {
1278                 *psz_unicode = '\n';
1279             }
1280             psz_unicode = psz_line_start;
1281             i_pen_x = 0;
1282             i_previous = i = 0;
1283             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1284             continue;
1285         }
1286         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1287         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1288
1289         i_previous = i_glyph_index;
1290         i_pen_x += glyph->advance.x >> 6;
1291         i++;
1292     }
1293
1294     p_line->i_width = line.xMax;
1295     p_line->i_height = face->size->metrics.height >> 6;
1296     p_line->pp_glyphs[ i ] = NULL;
1297     p_line->i_alpha = i_font_alpha;
1298     p_line->i_red = i_red;
1299     p_line->i_green = i_green;
1300     p_line->i_blue = i_blue;
1301     result.x = __MAX( result.x, line.xMax );
1302     result.y += line.yMax - line.yMin;
1303
1304 #undef face
1305 #undef glyph
1306
1307     p_region_out->i_x = p_region_in->i_x;
1308     p_region_out->i_y = p_region_in->i_y;
1309
1310     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1311         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1312     else
1313         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1314
1315     if( psz_unicode_orig ) free( psz_unicode_orig );
1316     FreeLines( p_lines );
1317     return VLC_SUCCESS;
1318
1319  error:
1320     if( psz_unicode_orig ) free( psz_unicode_orig );
1321     FreeLines( p_lines );
1322     return VLC_EGENERIC;
1323 }
1324
1325 #ifdef HAVE_FONTCONFIG
1326 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1327         uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1328         vlc_bool_t b_italic, vlc_bool_t b_uline )
1329 {
1330     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
1331
1332     if( p_style )
1333     {
1334         p_style->i_font_size        = i_font_size;
1335         p_style->i_font_color       = i_font_color;
1336         p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1337         p_style->b_italic           = b_italic;
1338         p_style->b_bold             = b_bold;
1339         p_style->b_underline        = b_uline;
1340
1341         p_style->psz_fontname = strdup( psz_fontname );
1342     }
1343     return p_style;
1344 }
1345
1346 static void DeleteStyle( ft_style_t *p_style )
1347 {
1348     if( p_style )
1349     {
1350         if( p_style->psz_fontname )
1351             free( p_style->psz_fontname );
1352         free( p_style );
1353     }
1354 }
1355
1356 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1357 {
1358     if( !s1 || !s2 )
1359         return VLC_FALSE;
1360     if( s1 == s2 )
1361         return VLC_TRUE;
1362
1363     if(( s1->i_font_size  == s2->i_font_size ) &&
1364        ( s1->i_font_color == s2->i_font_color ) &&
1365        ( s1->b_italic     == s2->b_italic ) &&
1366        ( s1->b_bold       == s2->b_bold ) &&
1367        ( s1->b_underline  == s2->b_underline ) &&
1368        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1369     {
1370         return VLC_TRUE;
1371     }
1372     return VLC_FALSE;
1373 }
1374
1375 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1376                      uint32_t i_color, uint32_t i_karaoke_bg_color )
1377 {
1378     font_stack_t *p_new;
1379
1380     if( !p_font )
1381         return VLC_EGENERIC;
1382
1383     p_new = malloc( sizeof( font_stack_t ) );
1384     if( ! p_new )
1385         return VLC_ENOMEM;
1386
1387     p_new->p_next = NULL;
1388
1389     if( psz_name )
1390         p_new->psz_name = strdup( psz_name );
1391     else
1392         p_new->psz_name = NULL;
1393
1394     p_new->i_size              = i_size;
1395     p_new->i_color             = i_color;
1396     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
1397
1398     if( !*p_font )
1399     {
1400         *p_font = p_new;
1401     }
1402     else
1403     {
1404         font_stack_t *p_last;
1405
1406         for( p_last = *p_font;
1407              p_last->p_next;
1408              p_last = p_last->p_next )
1409         ;
1410
1411         p_last->p_next = p_new;
1412     }
1413     return VLC_SUCCESS;
1414 }
1415
1416 static int PopFont( font_stack_t **p_font )
1417 {
1418     font_stack_t *p_last, *p_next_to_last;
1419
1420     if( !p_font || !*p_font )
1421         return VLC_EGENERIC;
1422
1423     p_next_to_last = NULL;
1424     for( p_last = *p_font;
1425          p_last->p_next;
1426          p_last = p_last->p_next )
1427     {
1428         p_next_to_last = p_last;
1429     }
1430
1431     if( p_next_to_last )
1432         p_next_to_last->p_next = NULL;
1433     else
1434         *p_font = NULL;
1435
1436     free( p_last->psz_name );
1437     free( p_last );
1438
1439     return VLC_SUCCESS;
1440 }
1441
1442 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1443                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1444 {
1445     font_stack_t *p_last;
1446
1447     if( !p_font || !*p_font )
1448         return VLC_EGENERIC;
1449
1450     for( p_last=*p_font;
1451          p_last->p_next;
1452          p_last=p_last->p_next )
1453     ;
1454
1455     *psz_name            = p_last->psz_name;
1456     *i_size              = p_last->i_size;
1457     *i_color             = p_last->i_color;
1458     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
1459
1460     return VLC_SUCCESS;
1461 }
1462
1463 static void IconvText( filter_t *p_filter, const char *psz_string,
1464                        uint32_t *i_string_length, uint32_t **ppsz_unicode )
1465 {
1466     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1467
1468     /* If memory hasn't been allocated for our output string, allocate it here
1469      * - the calling function must now be responsible for freeing it.
1470      */
1471     if( !*ppsz_unicode )
1472         *ppsz_unicode = (uint32_t *)
1473             malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1474
1475     /* We don't need to handle a NULL pointer in *ppsz_unicode
1476      * if we are instead testing for a non NULL value like we are here */
1477
1478     if( *ppsz_unicode )
1479     {
1480 #if defined(WORDS_BIGENDIAN)
1481         iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1482 #else
1483         iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1484 #endif
1485         if( iconv_handle != (vlc_iconv_t)-1 )
1486         {
1487             char *p_in_buffer, *p_out_buffer;
1488             size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1489             i_in_bytes = strlen( psz_string );
1490             i_out_bytes = i_in_bytes * sizeof( uint32_t );
1491             i_out_bytes_left = i_out_bytes;
1492             p_in_buffer = (char *) psz_string;
1493             p_out_buffer = (char *) *ppsz_unicode;
1494             i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1495                     &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1496
1497             vlc_iconv_close( iconv_handle );
1498
1499             if( i_in_bytes )
1500             {
1501                 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1502                           "bytes left %u", (unsigned)i_in_bytes );
1503             }
1504             else
1505             {
1506                 *(uint32_t*)p_out_buffer = 0;
1507                 *i_string_length =
1508                     (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1509             }
1510         }
1511         else
1512         {
1513             msg_Warn( p_filter, "unable to do conversion" );
1514         }
1515     }
1516 }
1517
1518 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1519         font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1520         vlc_bool_t b_uline )
1521 {
1522     ft_style_t   *p_style = NULL;
1523
1524     char       *psz_fontname = NULL;
1525     uint32_t    i_font_color = p_sys->i_font_color & 0x00ffffff;
1526     uint32_t    i_karaoke_bg_color = i_font_color;
1527     int         i_font_size  = p_sys->i_font_size;
1528
1529     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1530                                  &i_font_color, &i_karaoke_bg_color ))
1531     {
1532         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1533                 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1534     }
1535     return p_style;
1536 }
1537
1538 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1539                       vlc_bool_t b_uline, int i_karaoke_bgcolor,
1540                       line_desc_t *p_line, uint32_t *psz_unicode,
1541                       int *pi_pen_x, int i_pen_y, int *pi_start,
1542                       FT_Vector *p_result )
1543 {
1544     FT_BBox      line;
1545     int          i_yMin, i_yMax;
1546     int          i;
1547     vlc_bool_t   b_first_on_line = VLC_TRUE;
1548
1549     int          i_previous = 0;
1550     int          i_pen_x_start = *pi_pen_x;
1551
1552     uint32_t *psz_unicode_start = psz_unicode;
1553
1554     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1555
1556     /* Account for part of line already in position */
1557     for( i=0; i<*pi_start; i++ )
1558     {
1559         FT_BBox glyph_size;
1560
1561         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1562                             ft_glyph_bbox_pixels, &glyph_size );
1563
1564         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1565             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1566         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1567         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1568     }
1569     i_yMin = line.yMin;
1570     i_yMax = line.yMax;
1571
1572     if( line.xMax > 0 )
1573         b_first_on_line = VLC_FALSE;
1574
1575     while( *psz_unicode && ( *psz_unicode != '\n' ) )
1576     {
1577         FT_BBox glyph_size;
1578         FT_Glyph tmp_glyph;
1579         int i_error;
1580
1581         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1582         if( FT_HAS_KERNING( p_face ) && i_glyph_index
1583             && i_previous )
1584         {
1585             FT_Vector delta;
1586             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1587                             ft_kerning_default, &delta );
1588             *pi_pen_x += delta.x >> 6;
1589         }
1590         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1591         p_line->p_glyph_pos[ i ].y = i_pen_y;
1592
1593         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1594         if( i_error )
1595         {
1596             msg_Err( p_filter,
1597                    "unable to render text FT_Load_Glyph returned %d", i_error );
1598             p_line->pp_glyphs[ i ] = NULL;
1599             return VLC_EGENERIC;
1600         }
1601         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1602         if( i_error )
1603         {
1604             msg_Err( p_filter,
1605                     "unable to render text FT_Get_Glyph returned %d", i_error );
1606             p_line->pp_glyphs[ i ] = NULL;
1607             return VLC_EGENERIC;
1608         }
1609         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1610         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1611         if( i_error )
1612         {
1613             FT_Done_Glyph( tmp_glyph );
1614             continue;
1615         }
1616         if( b_uline )
1617         {
1618             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1619                                                p_face->size->metrics.y_scale));
1620             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1621                                             p_face->size->metrics.y_scale));
1622
1623             p_line->pi_underline_offset[ i ]  =
1624                                        ( aOffset < 0 ) ? -aOffset : aOffset;
1625             p_line->pi_underline_thickness[ i ] =
1626                                        ( aSize < 0 ) ? -aSize   : aSize;
1627         }
1628         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1629         p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1630         p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1631         p_line->p_fg_bg_ratio[ i ] = 0x00;
1632
1633         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1634                     glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1635         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1636         {
1637             while( --i > *pi_start )
1638             {
1639                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1640             }
1641
1642             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1643             {
1644                 psz_unicode--;
1645             }
1646             if( psz_unicode == psz_unicode_start )
1647             {
1648                 if( b_first_on_line )
1649                 {
1650                     msg_Warn( p_filter, "unbreakable string" );
1651                     p_line->pp_glyphs[ i ] = NULL;
1652                     return VLC_EGENERIC;
1653                 }
1654                 *pi_pen_x = i_pen_x_start;
1655
1656                 p_line->i_width = line.xMax;
1657                 p_line->i_height = __MAX( p_line->i_height,
1658                                           p_face->size->metrics.height >> 6 );
1659                 p_line->pp_glyphs[ i ] = NULL;
1660
1661                 p_result->x = __MAX( p_result->x, line.xMax );
1662                 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1663                                                          i_yMax - i_yMin ) );
1664
1665                 *pi_start = i;
1666                 return VLC_SUCCESS;
1667             }
1668             else
1669             {
1670                 *psz_unicode = '\n';
1671             }
1672             psz_unicode = psz_unicode_start;
1673             *pi_pen_x = i_pen_x_start;
1674             i_previous = 0;
1675
1676             line.yMax = i_yMax;
1677             line.yMin = i_yMin;
1678
1679             continue;
1680         }
1681         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1682         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1683
1684         i_previous = i_glyph_index;
1685         *pi_pen_x += p_face->glyph->advance.x >> 6;
1686         i++;
1687     }
1688     p_line->i_width = line.xMax;
1689     p_line->i_height = __MAX( p_line->i_height,
1690                               p_face->size->metrics.height >> 6 );
1691     p_line->pp_glyphs[ i ] = NULL;
1692
1693     p_result->x = __MAX( p_result->x, line.xMax );
1694     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1695                          line.yMax - line.yMin ) );
1696
1697     *pi_start = i;
1698
1699     /* Get rid of any text processed - if necessary repositioning
1700      * at the start of a new line of text
1701      */
1702     if( !*psz_unicode )
1703     {
1704         *psz_unicode_start = '\0';
1705     }
1706     else
1707     {
1708         psz_unicode++;
1709         for( i=0; psz_unicode[ i ]; i++ )
1710             psz_unicode_start[ i ] = psz_unicode[ i ];
1711         psz_unicode_start[ i ] = '\0';
1712     }
1713
1714     return VLC_SUCCESS;
1715 }
1716
1717 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1718                                   font_stack_t **p_fonts, int i_scale )
1719 {
1720     int        rv;
1721     char      *psz_fontname = NULL;
1722     uint32_t   i_font_color = 0xffffff;
1723     int        i_font_alpha = 0;
1724     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1725     int        i_font_size  = 24;
1726
1727     /* Default all attributes to the top font in the stack -- in case not
1728      * all attributes are specified in the sub-font
1729      */
1730     if( VLC_SUCCESS == PeekFont( p_fonts,
1731                                  &psz_fontname,
1732                                  &i_font_size,
1733                                  &i_font_color,
1734                                  &i_karaoke_bg_color ))
1735     {
1736         psz_fontname = strdup( psz_fontname );
1737         i_font_size = i_font_size * 1000 / i_scale;
1738     }
1739     i_font_alpha = (i_font_color >> 24) & 0xff;
1740     i_font_color &= 0x00ffffff;
1741
1742     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1743     {
1744         char *psz_name = xml_ReaderName( p_xml_reader );
1745         char *psz_value = xml_ReaderValue( p_xml_reader );
1746
1747         if( psz_name && psz_value )
1748         {
1749             if( !strcasecmp( "face", psz_name ) )
1750             {
1751                 if( psz_fontname ) free( psz_fontname );
1752                 psz_fontname = strdup( psz_value );
1753             }
1754             else if( !strcasecmp( "size", psz_name ) )
1755             {
1756                 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1757                 {
1758                     int i_value = atoi( psz_value );
1759
1760                     if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1761                         i_font_size += ( i_value * i_font_size ) / 10;
1762                     else if( i_value < -5 )
1763                         i_font_size = - i_value;
1764                     else if( i_value > 5 )
1765                         i_font_size = i_value;
1766                 }
1767                 else
1768                     i_font_size = atoi( psz_value );
1769             }
1770             else if( !strcasecmp( "color", psz_name )  &&
1771                      ( psz_value[0] == '#' ) )
1772             {
1773                 i_font_color = strtol( psz_value + 1, NULL, 16 );
1774                 i_font_color &= 0x00ffffff;
1775             }
1776             else if( !strcasecmp( "alpha", psz_name ) &&
1777                      ( psz_value[0] == '#' ) )
1778             {
1779                 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1780                 i_font_alpha &= 0xff;
1781             }
1782             free( psz_name );
1783             free( psz_value );
1784         }
1785     }
1786     rv = PushFont( p_fonts,
1787                    psz_fontname,
1788                    i_font_size * i_scale / 1000,
1789                    (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1790                    i_karaoke_bg_color );
1791
1792     free( psz_fontname );
1793
1794     return rv;
1795 }
1796
1797 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1798                        uint32_t **psz_text_out, uint32_t *pi_runs,
1799                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1800                        ft_style_t *p_style )
1801 {
1802     uint32_t      i_string_length = 0;
1803
1804     IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1805     *psz_text_out += i_string_length;
1806
1807     if( ppp_styles && ppi_run_lengths )
1808     {
1809         (*pi_runs)++;
1810
1811         if( *ppp_styles )
1812         {
1813             *ppp_styles = (ft_style_t **)
1814                 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1815         }
1816         else if( *pi_runs == 1 )
1817         {
1818             *ppp_styles = (ft_style_t **)
1819                 malloc( *pi_runs * sizeof( ft_style_t * ) );
1820         }
1821
1822         /* We have just malloc'ed this memory successfully -
1823          * *pi_runs HAS to be within the memory area of *ppp_styles */
1824         if( *ppp_styles )
1825         {
1826             (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1827             p_style = NULL;
1828         }
1829
1830         if( *ppi_run_lengths )
1831         {
1832             *ppi_run_lengths = (uint32_t *)
1833                 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1834         }
1835         else if( *pi_runs == 1 )
1836         {
1837             *ppi_run_lengths = (uint32_t *)
1838                 malloc( *pi_runs * sizeof( uint32_t ) );
1839         }
1840
1841         /* same remarks here */
1842         if( *ppi_run_lengths )
1843         {
1844             (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1845         }
1846     }
1847     /* If we couldn't use the p_style argument due to memory allocation
1848      * problems above, release it here.
1849      */
1850     if( p_style ) DeleteStyle( p_style );
1851 }
1852
1853 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1854                            uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1855 {
1856     /* Karaoke tags _PRECEDE_ the text they specify a duration
1857      * for, therefore we are working out the length for the
1858      * previous tag, and first time through we have nothing
1859      */
1860     if( pi_k_run_lengths )
1861     {
1862         int i_chars = 0;
1863         uint32_t i;
1864
1865         /* Work out how many characters are presently in the string
1866          */
1867         for( i = 0; i < i_runs; i++ )
1868             i_chars += pi_run_lengths[ i ];
1869
1870         /* Subtract away those we've already allocated to other
1871          * karaoke tags
1872          */
1873         for( i = 0; i < i_k_runs; i++ )
1874             i_chars -= pi_k_run_lengths[ i ];
1875
1876         pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1877     }
1878 }
1879
1880 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1881                           uint32_t **ppi_k_run_lengths,
1882                           uint32_t **ppi_k_durations )
1883 {
1884     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1885     {
1886         char *psz_name = xml_ReaderName( p_xml_reader );
1887         char *psz_value = xml_ReaderValue( p_xml_reader );
1888
1889         if( psz_name && psz_value &&
1890             !strcasecmp( "t", psz_name ) )
1891         {
1892             if( ppi_k_durations && ppi_k_run_lengths )
1893             {
1894                 (*pi_k_runs)++;
1895
1896                 if( *ppi_k_durations )
1897                 {
1898                     *ppi_k_durations = (uint32_t *)
1899                         realloc( *ppi_k_durations,
1900                                  *pi_k_runs * sizeof( uint32_t ) );
1901                 }
1902                 else if( *pi_k_runs == 1 )
1903                 {
1904                     *ppi_k_durations = (uint32_t *)
1905                         malloc( *pi_k_runs * sizeof( uint32_t ) );
1906                 }
1907
1908                 if( *ppi_k_run_lengths )
1909                 {
1910                     *ppi_k_run_lengths = (uint32_t *)
1911                         realloc( *ppi_k_run_lengths,
1912                                  *pi_k_runs * sizeof( uint32_t ) );
1913                 }
1914                 else if( *pi_k_runs == 1 )
1915                 {
1916                     *ppi_k_run_lengths = (uint32_t *)
1917                         malloc( *pi_k_runs * sizeof( uint32_t ) );
1918                 }
1919                 if( *ppi_k_durations )
1920                     (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1921
1922                 if( *ppi_k_run_lengths )
1923                     (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1924             }
1925         }
1926         if( psz_name )  free( psz_name );
1927         if( psz_value ) free( psz_value );
1928     }
1929 }
1930
1931 static int ProcessNodes( filter_t *p_filter,
1932                          xml_reader_t *p_xml_reader,
1933                          text_style_t *p_font_style,
1934                          uint32_t *psz_text,
1935                          int *pi_len,
1936
1937                          uint32_t *pi_runs,
1938                          uint32_t **ppi_run_lengths,
1939                          ft_style_t ***ppp_styles,
1940
1941                          vlc_bool_t b_karaoke,
1942                          uint32_t *pi_k_runs,
1943                          uint32_t **ppi_k_run_lengths,
1944                          uint32_t **ppi_k_durations )
1945 {
1946     int           rv             = VLC_SUCCESS;
1947     filter_sys_t *p_sys          = p_filter->p_sys;
1948     uint32_t     *psz_text_orig  = psz_text;
1949     font_stack_t *p_fonts        = NULL;
1950     vlc_value_t   val;
1951     int           i_scale        = 1000;
1952
1953     char *psz_node  = NULL;
1954
1955     vlc_bool_t b_italic = VLC_FALSE;
1956     vlc_bool_t b_bold   = VLC_FALSE;
1957     vlc_bool_t b_uline  = VLC_FALSE;
1958
1959     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
1960         i_scale = val.i_int;
1961
1962     if( p_font_style )
1963     {
1964         rv = PushFont( &p_fonts,
1965                p_font_style->psz_fontname,
1966                p_font_style->i_font_size * i_scale / 1000,
1967                (p_font_style->i_font_color & 0xffffff) |
1968                    ((p_font_style->i_font_alpha & 0xff) << 24),
1969                (p_font_style->i_karaoke_background_color & 0xffffff) |
1970                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1971
1972         if( p_font_style->i_style_flags & STYLE_BOLD )
1973             b_bold = VLC_TRUE;
1974         if( p_font_style->i_style_flags & STYLE_ITALIC )
1975             b_italic = VLC_TRUE;
1976         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1977             b_uline = VLC_TRUE;
1978     }
1979     else
1980     {
1981         rv = PushFont( &p_fonts,
1982                        FC_DEFAULT_FONT,
1983                        p_sys->i_font_size,
1984                        0x00ffffff,
1985                        0x00ffffff );
1986     }
1987     if( rv != VLC_SUCCESS )
1988         return rv;
1989
1990     while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1991     {
1992         switch ( xml_ReaderNodeType( p_xml_reader ) )
1993         {
1994             case XML_READER_NONE:
1995                 break;
1996             case XML_READER_ENDELEM:
1997                 psz_node = xml_ReaderName( p_xml_reader );
1998
1999                 if( psz_node )
2000                 {
2001                     if( !strcasecmp( "font", psz_node ) )
2002                         PopFont( &p_fonts );
2003                     else if( !strcasecmp( "b", psz_node ) )
2004                         b_bold   = VLC_FALSE;
2005                     else if( !strcasecmp( "i", psz_node ) )
2006                         b_italic = VLC_FALSE;
2007                     else if( !strcasecmp( "u", psz_node ) )
2008                         b_uline  = VLC_FALSE;
2009
2010                     free( psz_node );
2011                 }
2012                 break;
2013             case XML_READER_STARTELEM:
2014                 psz_node = xml_ReaderName( p_xml_reader );
2015                 if( psz_node )
2016                 {
2017                     if( !strcasecmp( "font", psz_node ) )
2018                         rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2019                     else if( !strcasecmp( "b", psz_node ) )
2020                         b_bold = VLC_TRUE;
2021                     else if( !strcasecmp( "i", psz_node ) )
2022                         b_italic = VLC_TRUE;
2023                     else if( !strcasecmp( "u", psz_node ) )
2024                         b_uline = VLC_TRUE;
2025                     else if( !strcasecmp( "br", psz_node ) )
2026                     {
2027                         SetupLine( p_filter, "\n", &psz_text,
2028                                    pi_runs, ppi_run_lengths, ppp_styles,
2029                                    GetStyleFromFontStack( p_sys,
2030                                                           &p_fonts,
2031                                                           b_bold,
2032                                                           b_italic,
2033                                                           b_uline ) );
2034                     }
2035                     else if( !strcasecmp( "k", psz_node ) )
2036                     {
2037                         /* Only valid in karaoke */
2038                         if( b_karaoke )
2039                         {
2040                             if( *pi_k_runs > 0 )
2041                             {
2042                                 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2043                                                *pi_k_runs, *ppi_k_run_lengths );
2044                             }
2045                             SetupKaraoke( p_xml_reader, pi_k_runs,
2046                                           ppi_k_run_lengths, ppi_k_durations );
2047                         }
2048                     }
2049
2050                     free( psz_node );
2051                 }
2052                 break;
2053             case XML_READER_TEXT:
2054                 psz_node = xml_ReaderValue( p_xml_reader );
2055                 if( psz_node )
2056                 {
2057                     /* Turn any multiple-whitespaces into single spaces */
2058                     char *s = strpbrk( psz_node, "\t\r\n " );
2059                     while( s )
2060                     {
2061                         int i_whitespace = strspn( s, "\t\r\n " );
2062
2063                         if( i_whitespace > 1 )
2064                             memmove( &s[1],
2065                                      &s[i_whitespace],
2066                                      strlen( s ) - i_whitespace + 1 );
2067                         *s++ = ' ';
2068
2069                         s = strpbrk( s, "\t\r\n " );
2070                     }
2071                     SetupLine( p_filter, psz_node, &psz_text,
2072                                pi_runs, ppi_run_lengths, ppp_styles,
2073                                GetStyleFromFontStack( p_sys,
2074                                                       &p_fonts,
2075                                                       b_bold,
2076                                                       b_italic,
2077                                                       b_uline ) );
2078                     free( psz_node );
2079                 }
2080                 break;
2081         }
2082         if( rv != VLC_SUCCESS )
2083         {
2084             psz_text = psz_text_orig;
2085             break;
2086         }
2087     }
2088     if( b_karaoke )
2089     {
2090         SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2091                        *pi_k_runs, *ppi_k_run_lengths );
2092     }
2093
2094     *pi_len = psz_text - psz_text_orig;
2095
2096     while( VLC_SUCCESS == PopFont( &p_fonts ) );
2097
2098     return rv;
2099 }
2100
2101 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2102 {
2103     int k;
2104
2105     for( k=0; k < p_sys->i_font_attachments; k++ )
2106     {
2107         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
2108         int                 i_font_idx = 0;
2109         FT_Face             p_face = NULL;
2110
2111         while( 0 == FT_New_Memory_Face( p_sys->p_library,
2112                                         p_attach->p_data,
2113                                         p_attach->i_data,
2114                                         i_font_idx,
2115                                         &p_face ))
2116         {
2117             if( p_face )
2118             {
2119                 vlc_bool_t match = !strcasecmp( p_face->family_name,
2120                                                 p_style->psz_fontname );
2121
2122                 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2123                     match = match && p_style->b_bold;
2124                 else
2125                     match = match && !p_style->b_bold;
2126
2127                 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2128                     match = match && p_style->b_italic;
2129                 else
2130                     match = match && !p_style->b_italic;
2131
2132                 if(  match )
2133                 {
2134                     *pp_face = p_face;
2135                     return VLC_SUCCESS;
2136                 }
2137
2138                 FT_Done_Face( p_face );
2139             }
2140             i_font_idx++;
2141         }
2142     }
2143     return VLC_EGENERIC;
2144 }
2145
2146 static int ProcessLines( filter_t *p_filter,
2147                          uint32_t *psz_text,
2148                          int i_len,
2149
2150                          uint32_t i_runs,
2151                          uint32_t *pi_run_lengths,
2152                          ft_style_t **pp_styles,
2153                          line_desc_t **pp_lines,
2154
2155                          FT_Vector *p_result,
2156
2157                          vlc_bool_t b_karaoke,
2158                          uint32_t i_k_runs,
2159                          uint32_t *pi_k_run_lengths,
2160                          uint32_t *pi_k_durations )
2161 {
2162     filter_sys_t   *p_sys = p_filter->p_sys;
2163     ft_style_t    **pp_char_styles;
2164     int            *p_new_positions = NULL;
2165     int8_t         *p_levels = NULL;
2166     uint8_t        *pi_karaoke_bar = NULL;
2167     uint32_t        i, j, k;
2168     int             i_prev;
2169
2170     /* Assign each character in the text string its style explicitly, so that
2171      * after the characters have been shuffled around by Fribidi, we can re-apply
2172      * the styles, and to simplify the calculation of runs within a line.
2173      */
2174     pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2175     if( !pp_char_styles )
2176         return VLC_ENOMEM;
2177
2178     if( b_karaoke )
2179     {
2180         pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2181         /* If we can't allocate sufficient memory for karaoke, continue anyway -
2182          * we just won't be able to display the progress bar; at least we'll
2183          * get the text.
2184          */
2185     }
2186
2187     i = 0;
2188     for( j = 0; j < i_runs; j++ )
2189         for( k = 0; k < pi_run_lengths[ j ]; k++ )
2190             pp_char_styles[ i++ ] = pp_styles[ j ];
2191
2192 #if defined(HAVE_FRIBIDI)
2193     {
2194         ft_style_t  **pp_char_styles_new;
2195         int         *p_old_positions;
2196         uint32_t    *p_fribidi_string;
2197         int start_pos, pos = 0;
2198
2199         pp_char_styles_new  = (ft_style_t **)
2200             malloc( i_len * sizeof( ft_style_t * ));
2201
2202         p_fribidi_string = (uint32_t *)
2203             malloc( (i_len + 1) * sizeof(uint32_t) );
2204         p_old_positions = (int *)
2205             malloc( (i_len + 1) * sizeof( int ) );
2206         p_new_positions = (int *)
2207             malloc( (i_len + 1) * sizeof( int ) );
2208         p_levels = (int8_t *)
2209             malloc( (i_len + 1) * sizeof( int8_t ) );
2210
2211         if( ! pp_char_styles_new ||
2212             ! p_fribidi_string ||
2213             ! p_old_positions ||
2214             ! p_new_positions ||
2215             ! p_levels )
2216         {
2217             msg_Err( p_filter, "out of memory" );
2218             if( p_levels )           free( p_levels );
2219             if( p_old_positions )    free( p_old_positions );
2220             if( p_new_positions )    free( p_new_positions );
2221             if( p_fribidi_string )   free( p_fribidi_string );
2222             if( pp_char_styles_new ) free( pp_char_styles_new );
2223             if( pi_karaoke_bar )     free( pi_karaoke_bar );
2224
2225             free( pp_char_styles );
2226             return VLC_ENOMEM;
2227         }
2228
2229         /* Do bidi conversion line-by-line */
2230         while(pos < i_len)
2231         {
2232             while(pos < i_len) {
2233                 if (psz_text[pos] != '\n')
2234                     break;
2235                 p_fribidi_string[pos] = psz_text[pos];
2236                 pp_char_styles_new[pos] = pp_char_styles[pos];
2237                 p_new_positions[pos] = pos;
2238                 p_levels[pos] = 0;
2239                 ++pos;
2240             }
2241             start_pos = pos;
2242             while(pos < i_len) {
2243                 if (psz_text[pos] == '\n')
2244                     break;
2245                 ++pos;
2246             }
2247             if (pos > start_pos)
2248             {
2249                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2250                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2251                         pos - start_pos, &base_dir,
2252                         (FriBidiChar*)p_fribidi_string + start_pos,
2253                         p_new_positions + start_pos,
2254                         p_old_positions,
2255                         p_levels + start_pos );
2256                 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2257                 {
2258                     pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2259                                                 p_old_positions[ j - start_pos ] ];
2260                     p_new_positions[ j ] += start_pos;
2261                 }
2262             }
2263         }
2264         free( p_old_positions );
2265         free( pp_char_styles );
2266         pp_char_styles = pp_char_styles_new;
2267         psz_text = p_fribidi_string;
2268         p_fribidi_string[ i_len ] = 0;
2269     }
2270 #endif
2271     /* Work out the karaoke */
2272     if( pi_karaoke_bar )
2273     {
2274         int64_t i_last_duration = 0;
2275         int64_t i_duration = 0;
2276         int64_t i_start_pos = 0;
2277         int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2278
2279         for( k = 0; k< i_k_runs; k++ )
2280         {
2281              double fraction = 0.0;
2282
2283              i_duration += pi_k_durations[ k ];
2284
2285              if( i_duration < i_elapsed )
2286              {
2287                  /* Completely finished this run-length -
2288                   * let it render normally */
2289
2290                  fraction = 1.0;
2291              }
2292              else if( i_elapsed < i_last_duration )
2293              {
2294                  /* Haven't got up to this segment yet -
2295                   * render it completely in karaoke BG mode */
2296
2297                  fraction = 0.0;
2298              }
2299              else
2300              {
2301                  /* Partway through this run */
2302
2303                  fraction = (double)(i_elapsed - i_last_duration) /
2304                             (double)pi_k_durations[ k ];
2305              }
2306              for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2307              {
2308                  double shade = pi_k_run_lengths[ k ] * fraction;
2309
2310                  if( p_new_positions )
2311                      j = p_new_positions[ i_start_pos + i ];
2312                  else
2313                      j = i_start_pos + i;
2314
2315                  if( i < (uint32_t)shade )
2316                      pi_karaoke_bar[ j ] = 0xff;
2317                  else if( (double)i > shade )
2318                      pi_karaoke_bar[ j ] = 0x00;
2319                  else
2320                  {
2321                      shade -= (int)shade;
2322                      pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2323                                    ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2324                  }
2325              }
2326
2327              i_last_duration = i_duration;
2328              i_start_pos += pi_k_run_lengths[ k ];
2329         }
2330     }
2331     if( p_levels )         free( p_levels );
2332     if( p_new_positions )  free( p_new_positions );
2333
2334     FT_Vector tmp_result;
2335
2336     line_desc_t *p_line = NULL;
2337     line_desc_t *p_prev = NULL;
2338
2339     int i_pen_x = 0;
2340     int i_pen_y = 0;
2341     int i_posn  = 0;
2342
2343     p_result->x = p_result->y = 0;
2344     tmp_result.x = tmp_result.y = 0;
2345
2346     i_prev = 0;
2347     for( k = 0; k <= (uint32_t) i_len; k++ )
2348     {
2349         if( ( k == (uint32_t) i_len ) ||
2350           ( ( k > 0 ) &&
2351             !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2352         {
2353             ft_style_t *p_style = pp_char_styles[ k - 1 ];
2354
2355             /* End of the current style run */
2356             FT_Face p_face = NULL;
2357             int      i_idx = 0;
2358
2359             /* Look for a match amongst our attachments first */
2360             CheckForEmbeddedFont( p_sys, &p_face, p_style );
2361
2362             if( ! p_face && p_sys->b_fontconfig_ok )
2363             {
2364                 char *psz_fontfile;
2365
2366                 vlc_mutex_lock( &p_sys->fontconfig_lock );
2367
2368                 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2369                                                   p_style->psz_fontname,
2370                                                   p_style->b_bold,
2371                                                   p_style->b_italic,
2372                                                   &i_idx );
2373
2374                 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2375
2376                 if( psz_fontfile && ! *psz_fontfile )
2377                 {
2378                     msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2379                         " so using default font", p_style->psz_fontname,
2380                         ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2381                                                (p_style->b_bold ? "(Bold)" :
2382                                              (p_style->b_italic ? "(Italic)" : ""))) );
2383                     free( psz_fontfile );
2384                     psz_fontfile = NULL;
2385                 }
2386
2387                 if( psz_fontfile )
2388                 {
2389                     if( FT_New_Face( p_sys->p_library,
2390                                 psz_fontfile, i_idx, &p_face ) )
2391                     {
2392                         free( psz_fontfile );
2393                         free( pp_char_styles );
2394 #if defined(HAVE_FRIBIDI)
2395                         free( psz_text );
2396 #endif
2397                         if( pi_karaoke_bar )
2398                             free( pi_karaoke_bar );
2399                         return VLC_EGENERIC;
2400                     }
2401                     free( psz_fontfile );
2402                 }
2403             }
2404             if( p_face &&
2405                 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2406             {
2407                 /* We've loaded a font face which is unhelpful for actually
2408                  * rendering text - fallback to the default one.
2409                  */
2410                  FT_Done_Face( p_face );
2411                  p_face = NULL;
2412             }
2413
2414             if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2415                         ft_encoding_unicode ) ||
2416                 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2417                     p_style->i_font_size ) )
2418             {
2419                 if( p_face ) FT_Done_Face( p_face );
2420                 free( pp_char_styles );
2421 #if defined(HAVE_FRIBIDI)
2422                 free( psz_text );
2423 #endif
2424                 if( pi_karaoke_bar )
2425                     free( pi_karaoke_bar );
2426                 return VLC_EGENERIC;
2427             }
2428             p_sys->i_use_kerning =
2429                         FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
2430
2431
2432             uint32_t *psz_unicode = (uint32_t *)
2433                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2434             if( !psz_unicode )
2435             {
2436                 msg_Err( p_filter, "out of memory" );
2437                 if( p_face ) FT_Done_Face( p_face );
2438                 free( pp_char_styles );
2439                 free( psz_unicode );
2440 #if defined(HAVE_FRIBIDI)
2441                 free( psz_text );
2442 #endif
2443                 if( pi_karaoke_bar )
2444                     free( pi_karaoke_bar );
2445                 return VLC_ENOMEM;
2446             }
2447             memcpy( psz_unicode, psz_text + i_prev,
2448                                         sizeof( uint32_t ) * ( k - i_prev ) );
2449             psz_unicode[ k - i_prev ] = 0;
2450             while( *psz_unicode )
2451             {
2452                 if( !p_line )
2453                 {
2454                     if( !(p_line = NewLine( i_len - i_prev)) )
2455                     {
2456                         msg_Err( p_filter, "out of memory" );
2457                         if( p_face ) FT_Done_Face( p_face );
2458                         free( pp_char_styles );
2459                         free( psz_unicode );
2460 #if defined(HAVE_FRIBIDI)
2461                         free( psz_text );
2462 #endif
2463                         if( pi_karaoke_bar )
2464                             free( pi_karaoke_bar );
2465                         return VLC_ENOMEM;
2466                     }
2467                     /* New Color mode only works in YUVA rendering mode --
2468                      * (RGB mode has palette constraints on it). We therefore
2469                      * need to populate the legacy colour fields also.
2470                      */
2471                     p_line->b_new_color_mode = VLC_TRUE;
2472                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2473                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2474                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
2475                     p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
2476                     p_line->p_next = NULL;
2477                     i_pen_x = 0;
2478                     i_pen_y += tmp_result.y;
2479                     tmp_result.x = 0;
2480                     tmp_result.y = 0;
2481                     i_posn = 0;
2482                     if( p_prev ) p_prev->p_next = p_line;
2483                     else *pp_lines = p_line;
2484                 }
2485
2486                 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2487                                p_style->i_font_color, p_style->b_underline,
2488                                p_style->i_karaoke_bg_color,
2489                                p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2490                                &tmp_result ) != VLC_SUCCESS )
2491                 {
2492                     if( p_face ) FT_Done_Face( p_face );
2493                     free( pp_char_styles );
2494                     free( psz_unicode );
2495 #if defined(HAVE_FRIBIDI)
2496                     free( psz_text );
2497 #endif
2498                     if( pi_karaoke_bar )
2499                         free( pi_karaoke_bar );
2500                     return VLC_EGENERIC;
2501                 }
2502
2503                 if( *psz_unicode )
2504                 {
2505                     p_result->x = __MAX( p_result->x, tmp_result.x );
2506                     p_result->y += tmp_result.y;
2507
2508                     p_prev = p_line;
2509                     p_line = NULL;
2510                 }
2511             }
2512             free( psz_unicode );
2513             if( p_face ) FT_Done_Face( p_face );
2514             i_prev = k;
2515         }
2516     }
2517     free( pp_char_styles );
2518 #if defined(HAVE_FRIBIDI)
2519     free( psz_text );
2520 #endif
2521     if( p_line )
2522     {
2523         p_result->x = __MAX( p_result->x, tmp_result.x );
2524         p_result->y += tmp_result.y;
2525     }
2526
2527     if( pi_karaoke_bar )
2528     {
2529         int i = 0;
2530         for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2531         {
2532             for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2533             {
2534                 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2535                 {
2536                     /* do nothing */
2537                 }
2538                 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2539                 {
2540                     /* 100% BG colour will render faster if we
2541                      * instead make it 100% FG colour, so leave
2542                      * the ratio alone and copy the value across
2543                      */
2544                     p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2545                 }
2546                 else
2547                 {
2548                     if( pi_karaoke_bar[ i ] & 0x80 )
2549                     {
2550                         /* Swap Left and Right sides over for Right aligned
2551                          * language text (eg. Arabic, Hebrew)
2552                          */
2553                         uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2554
2555                         p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2556                         p_line->p_bg_rgb[ k ] = i_tmp;
2557                     }
2558                     p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2559                 }
2560             }
2561             /* Jump over the '\n' at the line-end */
2562             i++;
2563         }
2564         free( pi_karaoke_bar );
2565     }
2566
2567     return VLC_SUCCESS;
2568 }
2569
2570 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2571                        subpicture_region_t *p_region_in )
2572 {
2573     int          rv = VLC_SUCCESS;
2574     stream_t     *p_sub = NULL;
2575     xml_t        *p_xml = NULL;
2576     xml_reader_t *p_xml_reader = NULL;
2577
2578     if( !p_region_in || !p_region_in->psz_html )
2579         return VLC_EGENERIC;
2580
2581     /* Reset the default fontsize in case screen metrics have changed */
2582     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2583
2584     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2585                               (uint8_t *) p_region_in->psz_html,
2586                               strlen( p_region_in->psz_html ),
2587                               VLC_TRUE );
2588     if( p_sub )
2589     {
2590         p_xml = xml_Create( p_filter );
2591         if( p_xml )
2592         {
2593             vlc_bool_t b_karaoke = VLC_FALSE;
2594
2595             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2596             if( p_xml_reader )
2597             {
2598                 /* Look for Root Node */
2599                 if( xml_ReaderRead( p_xml_reader ) == 1 )
2600                 {
2601                     char *psz_node = xml_ReaderName( p_xml_reader );
2602
2603                     if( !strcasecmp( "karaoke", psz_node ) )
2604                     {
2605                         /* We're going to have to render the text a number
2606                          * of times to show the progress marker on the text.
2607                          */
2608                         var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2609                         b_karaoke = VLC_TRUE;
2610                     }
2611                     else if( !strcasecmp( "text", psz_node ) )
2612                     {
2613                         b_karaoke = VLC_FALSE;
2614                     }
2615                     else
2616                     {
2617                         /* Only text and karaoke tags are supported */
2618                         msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2619                         xml_ReaderDelete( p_xml, p_xml_reader );
2620                         p_xml_reader = NULL;
2621                         rv = VLC_EGENERIC;
2622                     }
2623
2624                     free( psz_node );
2625                 }
2626             }
2627
2628             if( p_xml_reader )
2629             {
2630                 uint32_t   *psz_text;
2631                 int         i_len = 0;
2632                 uint32_t    i_runs = 0;
2633                 uint32_t    i_k_runs = 0;
2634                 uint32_t   *pi_run_lengths = NULL;
2635                 uint32_t   *pi_k_run_lengths = NULL;
2636                 uint32_t   *pi_k_durations = NULL;
2637                 ft_style_t  **pp_styles = NULL;
2638                 FT_Vector    result;
2639                 line_desc_t  *p_lines = NULL;
2640
2641                 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2642                                                 sizeof( uint32_t ) );
2643                 if( psz_text )
2644                 {
2645                     uint32_t k;
2646
2647                     rv = ProcessNodes( p_filter, p_xml_reader,
2648                                   p_region_in->p_style, psz_text, &i_len,
2649                                   &i_runs, &pi_run_lengths, &pp_styles,
2650                                   b_karaoke, &i_k_runs, &pi_k_run_lengths,
2651                                   &pi_k_durations );
2652
2653                     p_region_out->i_x = p_region_in->i_x;
2654                     p_region_out->i_y = p_region_in->i_y;
2655
2656                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2657                     {
2658                         rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2659                                 pi_run_lengths, pp_styles, &p_lines, &result,
2660                                 b_karaoke, i_k_runs, pi_k_run_lengths,
2661                                 pi_k_durations );
2662                     }
2663
2664                     for( k=0; k<i_runs; k++)
2665                         DeleteStyle( pp_styles[k] );
2666                     free( pp_styles );
2667                     free( pi_run_lengths );
2668                     free( psz_text );
2669
2670                     /* Don't attempt to render text that couldn't be layed out
2671                      * properly.
2672                      */
2673                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2674                     {
2675                         if( config_GetInt( p_filter, "freetype-yuvp" ) )
2676                         {
2677                             Render( p_filter, p_region_out, p_lines,
2678                                     result.x, result.y );
2679                         }
2680                         else
2681                         {
2682                             RenderYUVA( p_filter, p_region_out, p_lines,
2683                                     result.x, result.y );
2684                         }
2685                     }
2686                 }
2687                 FreeLines( p_lines );
2688
2689                 xml_ReaderDelete( p_xml, p_xml_reader );
2690             }
2691             xml_Delete( p_xml );
2692         }
2693         stream_Delete( p_sub );
2694     }
2695
2696     return rv;
2697 }
2698
2699 static char* FontConfig_Select( FcConfig* priv, const char* family,
2700                           vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2701 {
2702     FcResult result;
2703     FcPattern *pat, *p_pat;
2704     FcChar8* val_s;
2705     FcBool val_b;
2706
2707     pat = FcPatternCreate();
2708     if (!pat) return NULL;
2709
2710     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2711     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2712     FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2713     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2714
2715     FcDefaultSubstitute( pat );
2716
2717     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2718     {
2719         FcPatternDestroy( pat );
2720         return NULL;
2721     }
2722
2723     p_pat = FcFontMatch( priv, pat, &result );
2724     FcPatternDestroy( pat );
2725     if( !p_pat ) return NULL;
2726
2727     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2728         || ( val_b != FcTrue ) )
2729     {
2730         FcPatternDestroy( p_pat );
2731         return NULL;
2732     }
2733     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2734     {
2735         *i_idx = 0;
2736     }
2737
2738     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2739     {
2740         FcPatternDestroy( p_pat );
2741         return NULL;
2742     }
2743
2744     /*
2745     if( strcasecmp((const char*)val_s, family ) != 0 )
2746         msg_Warn( p_filter, "fontconfig: selected font family is not"
2747                             "the requested one: '%s' != '%s'\n",
2748                             (const char*)val_s, family );
2749     */
2750
2751     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2752     {
2753         FcPatternDestroy( p_pat );
2754         return NULL;
2755     }
2756
2757     FcPatternDestroy( p_pat );
2758     return strdup( (const char*)val_s );
2759 }
2760 #endif
2761
2762 static void FreeLine( line_desc_t *p_line )
2763 {
2764     unsigned int i;
2765     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2766     {
2767         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2768     }
2769     free( p_line->pp_glyphs );
2770     free( p_line->p_glyph_pos );
2771     free( p_line->p_fg_rgb );
2772     free( p_line->p_bg_rgb );
2773     free( p_line->p_fg_bg_ratio );
2774     free( p_line->pi_underline_offset );
2775     free( p_line->pi_underline_thickness );
2776     free( p_line );
2777 }
2778
2779 static void FreeLines( line_desc_t *p_lines )
2780 {
2781     line_desc_t *p_line, *p_next;
2782
2783     if( !p_lines ) return;
2784
2785     for( p_line = p_lines; p_line != NULL; p_line = p_next )
2786     {
2787         p_next = p_line->p_next;
2788         FreeLine( p_line );
2789     }
2790 }
2791
2792 static line_desc_t *NewLine( int i_count )
2793 {
2794     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2795
2796     if( !p_line ) return NULL;
2797     p_line->i_height = 0;
2798     p_line->i_width = 0;
2799     p_line->p_next = NULL;
2800
2801     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2802     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2803     p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2804     p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2805     p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2806     p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2807     p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2808     if( ( p_line->pp_glyphs == NULL ) ||
2809         ( p_line->p_glyph_pos == NULL ) ||
2810         ( p_line->p_fg_rgb == NULL ) ||
2811         ( p_line->p_bg_rgb == NULL ) ||
2812         ( p_line->p_fg_bg_ratio == NULL ) ||
2813         ( p_line->pi_underline_offset == NULL ) ||
2814         ( p_line->pi_underline_thickness == NULL ) )
2815     {
2816         if( p_line->pi_underline_thickness )
2817             free( p_line->pi_underline_thickness );
2818         if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2819         if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2820         if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2821         if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2822         if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2823         if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2824         free( p_line );
2825         return NULL;
2826     }
2827     p_line->pp_glyphs[0] = NULL;
2828     p_line->b_new_color_mode = VLC_FALSE;
2829
2830     return p_line;
2831 }
2832
2833 static int GetFontSize( filter_t *p_filter )
2834 {
2835     filter_sys_t *p_sys = p_filter->p_sys;
2836     vlc_value_t   val;
2837     int           i_size = 0;
2838
2839     if( p_sys->i_default_font_size )
2840     {
2841         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2842             i_size = p_sys->i_default_font_size * val.i_int / 1000;
2843         else
2844             i_size = p_sys->i_default_font_size;
2845     }
2846     else
2847     {
2848         var_Get( p_filter, "freetype-rel-fontsize", &val );
2849         i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2850         p_filter->p_sys->i_display_height =
2851             p_filter->fmt_out.video.i_height;
2852     }
2853     if( i_size <= 0 )
2854     {
2855         msg_Warn( p_filter, "invalid fontsize, using 12" );
2856         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2857             i_size = 12 * val.i_int / 1000;
2858         else
2859             i_size = 12;
2860     }
2861     return i_size;
2862 }
2863
2864 static int SetFontSize( filter_t *p_filter, int i_size )
2865 {
2866     filter_sys_t *p_sys = p_filter->p_sys;
2867
2868     if( !i_size )
2869     {
2870         i_size = GetFontSize( p_filter );
2871
2872         msg_Dbg( p_filter, "using fontsize: %i", i_size );
2873     }
2874
2875     p_sys->i_font_size = i_size;
2876
2877     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2878     {
2879         msg_Err( p_filter, "couldn't set font size to %d", i_size );
2880         return VLC_EGENERIC;
2881     }
2882
2883     return VLC_SUCCESS;
2884 }
2885
2886 static void YUVFromRGB( uint32_t i_argb,
2887                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2888 {
2889     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
2890     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
2891     int i_blue  = ( i_argb & 0x000000ff );
2892
2893     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
2894                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
2895     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
2896                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
2897     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2898                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
2899 }