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