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