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