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