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