]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
small cleanup
[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,
1138                            &i_in_bytes,
1139                            &p_out_buffer, &i_out_bytes_left );
1140
1141         vlc_iconv_close( iconv_handle );
1142
1143         if( i_in_bytes )
1144         {
1145             msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1146                       "bytes left %u", (unsigned)i_in_bytes );
1147             goto error;
1148         }
1149         *(uint32_t*)p_out_buffer = 0;
1150         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1151     }
1152
1153 #if defined(HAVE_FRIBIDI)
1154     {
1155         uint32_t *p_fribidi_string;
1156         int32_t start_pos, pos = 0;
1157
1158         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1159         if( !p_fribidi_string )
1160         {
1161             msg_Err( p_filter, "out of memory" );
1162             goto error;
1163         }
1164
1165         /* Do bidi conversion line-by-line */
1166         while( pos < i_string_length )
1167         {
1168             while( pos < i_string_length ) 
1169             {
1170                 i_char = psz_unicode[pos];
1171                 if (i_char != '\r' && i_char != '\n')
1172                     break;
1173                 p_fribidi_string[pos] = i_char;
1174                 ++pos;
1175             }
1176             start_pos = pos;
1177             while( pos < i_string_length )
1178             {
1179                 i_char = psz_unicode[pos];
1180                 if (i_char == '\r' || i_char == '\n')
1181                     break;
1182                 ++pos;
1183             }
1184             if (pos > start_pos)
1185             {
1186                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1187                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
1188                                 pos - start_pos,
1189                                 &base_dir,
1190                                 (FriBidiChar*)p_fribidi_string + start_pos,
1191                                 0, 0, 0);
1192             }
1193         }
1194
1195         free( psz_unicode_orig );
1196         psz_unicode = psz_unicode_orig = p_fribidi_string;
1197         p_fribidi_string[ i_string_length ] = 0;
1198     }
1199 #endif
1200
1201     /* Calculate relative glyph positions and a bounding box for the
1202      * entire string */
1203     if( !(p_line = NewLine( strlen( psz_string ))) )
1204     {
1205         msg_Err( p_filter, "out of memory" );
1206         goto error;
1207     }
1208     p_lines = p_line;
1209     i_pen_x = i_pen_y = 0;
1210     i_previous = i = 0;
1211     psz_line_start = psz_unicode;
1212
1213 #define face p_sys->p_face
1214 #define glyph face->glyph
1215
1216     while( *psz_unicode )
1217     {
1218         i_char = *psz_unicode++;
1219         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1220         {
1221             continue;
1222         }
1223
1224         if( i_char == '\n' )
1225         {
1226             psz_line_start = psz_unicode;
1227             if( !(p_next = NewLine( strlen( psz_string ))) )
1228             {
1229                 msg_Err( p_filter, "out of memory" );
1230                 goto error;
1231             }
1232             p_line->p_next = p_next;
1233             p_line->i_width = line.xMax;
1234             p_line->i_height = face->size->metrics.height >> 6;
1235             p_line->pp_glyphs[ i ] = NULL;
1236             p_line->i_alpha = i_font_alpha;
1237             p_line->i_red = i_red;
1238             p_line->i_green = i_green;
1239             p_line->i_blue = i_blue;
1240             p_prev = p_line;
1241             p_line = p_next;
1242             result.x = __MAX( result.x, line.xMax );
1243             result.y += face->size->metrics.height >> 6;
1244             i_pen_x = 0;
1245             i_previous = i = 0;
1246             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1247             i_pen_y += face->size->metrics.height >> 6;
1248 #if 0
1249             msg_Dbg( p_filter, "Creating new line, i is %d", i );
1250 #endif
1251             continue;
1252         }
1253
1254         i_glyph_index = FT_Get_Char_Index( face, i_char );
1255         if( p_sys->i_use_kerning && i_glyph_index
1256             && i_previous )
1257         {
1258             FT_Vector delta;
1259             FT_Get_Kerning( face, i_previous, i_glyph_index,
1260                             ft_kerning_default, &delta );
1261             i_pen_x += delta.x >> 6;
1262
1263         }
1264         p_line->p_glyph_pos[ i ].x = i_pen_x;
1265         p_line->p_glyph_pos[ i ].y = i_pen_y;
1266         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1267         if( i_error )
1268         {
1269             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1270                                " %d", i_error );
1271             goto error;
1272         }
1273         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1274         if( i_error )
1275         {
1276             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1277                                "%d", i_error );
1278             goto error;
1279         }
1280         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1281         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1282         if( i_error )
1283         {
1284             FT_Done_Glyph( tmp_glyph );
1285             continue;
1286         }
1287         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1288
1289         /* Do rest */
1290         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1291             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1292         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1293         {
1294             p_line->pp_glyphs[ i ] = NULL;
1295             FreeLine( p_line );
1296             p_line = NewLine( strlen( psz_string ));
1297             if( p_prev ) p_prev->p_next = p_line;
1298             else p_lines = p_line;
1299
1300             uint32_t *psz_unicode_saved = psz_unicode;
1301             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1302             {
1303                 psz_unicode--;
1304             }
1305             if( psz_unicode == psz_line_start )
1306             {   /* try harder to break that line */
1307                 psz_unicode = psz_unicode_saved;
1308                 while( psz_unicode > psz_line_start &&
1309                     *psz_unicode != '_'  && *psz_unicode != '/' &&
1310                     *psz_unicode != '\\' && *psz_unicode != '.' )
1311                 {
1312                     psz_unicode--;
1313                 }
1314             }
1315             if( psz_unicode == psz_line_start )
1316             {
1317                 msg_Warn( p_filter, "unbreakable string" );
1318                 goto error;
1319             }
1320             else
1321             {
1322                 *psz_unicode = '\n';
1323             }
1324             psz_unicode = psz_line_start;
1325             i_pen_x = 0;
1326             i_previous = i = 0;
1327             line.xMin = line.xMax = line.yMin = line.yMax = 0;
1328             continue;
1329         }
1330         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1331         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1332
1333         i_previous = i_glyph_index;
1334         i_pen_x += glyph->advance.x >> 6;
1335         i++;
1336     }
1337
1338     p_line->i_width = line.xMax;
1339     p_line->i_height = face->size->metrics.height >> 6;
1340     p_line->pp_glyphs[ i ] = NULL;
1341     p_line->i_alpha = i_font_alpha;
1342     p_line->i_red = i_red;
1343     p_line->i_green = i_green;
1344     p_line->i_blue = i_blue;
1345     result.x = __MAX( result.x, line.xMax );
1346     result.y += line.yMax - line.yMin;
1347
1348 #undef face
1349 #undef glyph
1350
1351     p_region_out->i_x = p_region_in->i_x;
1352     p_region_out->i_y = p_region_in->i_y;
1353
1354     if( config_GetInt( p_filter, "freetype-yuvp" ) )
1355         Render( p_filter, p_region_out, p_lines, result.x, result.y );
1356     else
1357         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1358
1359     if( psz_unicode_orig ) free( psz_unicode_orig );
1360     FreeLines( p_lines );
1361     return VLC_SUCCESS;
1362
1363  error:
1364     if( psz_unicode_orig ) free( psz_unicode_orig );
1365     FreeLines( p_lines );
1366     return VLC_EGENERIC;
1367 }
1368
1369 #ifdef HAVE_FONTCONFIG
1370 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1371         uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
1372         vlc_bool_t b_italic, vlc_bool_t b_uline )
1373 {
1374     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
1375
1376     if( p_style )
1377     {
1378         p_style->i_font_size        = i_font_size;
1379         p_style->i_font_color       = i_font_color;
1380         p_style->i_karaoke_bg_color = i_karaoke_bg_color;
1381         p_style->b_italic           = b_italic;
1382         p_style->b_bold             = b_bold;
1383         p_style->b_underline        = b_uline;
1384
1385         p_style->psz_fontname = strdup( psz_fontname );
1386     }
1387     return p_style;
1388 }
1389
1390 static void DeleteStyle( ft_style_t *p_style )
1391 {
1392     if( p_style )
1393     {
1394         if( p_style->psz_fontname )
1395             free( p_style->psz_fontname );
1396         free( p_style );
1397     }
1398 }
1399
1400 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1401 {
1402     if( !s1 || !s2 )
1403         return VLC_FALSE;
1404     if( s1 == s2 )
1405         return VLC_TRUE;
1406
1407     if(( s1->i_font_size  == s2->i_font_size ) &&
1408        ( s1->i_font_color == s2->i_font_color ) &&
1409        ( s1->b_italic     == s2->b_italic ) &&
1410        ( s1->b_bold       == s2->b_bold ) &&
1411        ( s1->b_underline  == s2->b_underline ) &&
1412        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1413     {
1414         return VLC_TRUE;
1415     }
1416     return VLC_FALSE;
1417 }
1418
1419 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1420                      uint32_t i_color, uint32_t i_karaoke_bg_color )
1421 {
1422     font_stack_t *p_new;
1423
1424     if( !p_font )
1425         return VLC_EGENERIC;
1426
1427     p_new = malloc( sizeof( font_stack_t ) );
1428     if( ! p_new )
1429         return VLC_ENOMEM;
1430
1431     p_new->p_next = NULL;
1432
1433     if( psz_name )
1434         p_new->psz_name = strdup( psz_name );
1435     else
1436         p_new->psz_name = NULL;
1437
1438     p_new->i_size              = i_size;
1439     p_new->i_color             = i_color;
1440     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
1441
1442     if( !*p_font )
1443     {
1444         *p_font = p_new;
1445     }
1446     else
1447     {
1448         font_stack_t *p_last;
1449
1450         for( p_last = *p_font;
1451              p_last->p_next;
1452              p_last = p_last->p_next )
1453         ;
1454
1455         p_last->p_next = p_new;
1456     }
1457     return VLC_SUCCESS;
1458 }
1459
1460 static int PopFont( font_stack_t **p_font )
1461 {
1462     font_stack_t *p_last, *p_next_to_last;
1463
1464     if( !p_font || !*p_font )
1465         return VLC_EGENERIC;
1466
1467     p_next_to_last = NULL;
1468     for( p_last = *p_font;
1469          p_last->p_next;
1470          p_last = p_last->p_next )
1471     {
1472         p_next_to_last = p_last;
1473     }
1474
1475     if( p_next_to_last )
1476         p_next_to_last->p_next = NULL;
1477     else
1478         *p_font = NULL;
1479
1480     free( p_last->psz_name );
1481     free( p_last );
1482
1483     return VLC_SUCCESS;
1484 }
1485
1486 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1487                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1488 {
1489     font_stack_t *p_last;
1490
1491     if( !p_font || !*p_font )
1492         return VLC_EGENERIC;
1493
1494     for( p_last=*p_font;
1495          p_last->p_next;
1496          p_last=p_last->p_next )
1497     ;
1498
1499     *psz_name            = p_last->psz_name;
1500     *i_size              = p_last->i_size;
1501     *i_color             = p_last->i_color;
1502     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
1503
1504     return VLC_SUCCESS;
1505 }
1506
1507 static void IconvText( filter_t *p_filter, const char *psz_string,
1508                        uint32_t *i_string_length, uint32_t **ppsz_unicode )
1509 {
1510     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1511
1512     /* If memory hasn't been allocated for our output string, allocate it here
1513      * - the calling function must now be responsible for freeing it.
1514      */
1515     if( !*ppsz_unicode )
1516         *ppsz_unicode = (uint32_t *)
1517             malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1518
1519     /* We don't need to handle a NULL pointer in *ppsz_unicode
1520      * if we are instead testing for a non NULL value like we are here */
1521
1522     if( *ppsz_unicode )
1523     {
1524 #if defined(WORDS_BIGENDIAN)
1525         iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1526 #else
1527         iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1528 #endif
1529         if( iconv_handle != (vlc_iconv_t)-1 )
1530         {
1531             char *p_in_buffer, *p_out_buffer;
1532             size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1533             i_in_bytes = strlen( psz_string );
1534             i_out_bytes = i_in_bytes * sizeof( uint32_t );
1535             i_out_bytes_left = i_out_bytes;
1536             p_in_buffer = (char *) psz_string;
1537             p_out_buffer = (char *) *ppsz_unicode;
1538             i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1539                     &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1540
1541             vlc_iconv_close( iconv_handle );
1542
1543             if( i_in_bytes )
1544             {
1545                 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
1546                           "bytes left %u", (unsigned)i_in_bytes );
1547             }
1548             else
1549             {
1550                 *(uint32_t*)p_out_buffer = 0;
1551                 *i_string_length =
1552                     (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1553             }
1554         }
1555         else
1556         {
1557             msg_Warn( p_filter, "unable to do conversion" );
1558         }
1559     }
1560 }
1561
1562 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1563         font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1564         vlc_bool_t b_uline )
1565 {
1566     ft_style_t   *p_style = NULL;
1567
1568     char       *psz_fontname = NULL;
1569     uint32_t    i_font_color = p_sys->i_font_color & 0x00ffffff;
1570     uint32_t    i_karaoke_bg_color = i_font_color;
1571     int         i_font_size  = p_sys->i_font_size;
1572
1573     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1574                                  &i_font_color, &i_karaoke_bg_color ))
1575     {
1576         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1577                 i_karaoke_bg_color, b_bold, b_italic, b_uline );
1578     }
1579     return p_style;
1580 }
1581
1582 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1583                       vlc_bool_t b_uline, int i_karaoke_bgcolor,
1584                       line_desc_t *p_line, uint32_t *psz_unicode,
1585                       int *pi_pen_x, int i_pen_y, int *pi_start,
1586                       FT_Vector *p_result )
1587 {
1588     FT_BBox      line;
1589     int          i_yMin, i_yMax;
1590     int          i;
1591     vlc_bool_t   b_first_on_line = VLC_TRUE;
1592
1593     int          i_previous = 0;
1594     int          i_pen_x_start = *pi_pen_x;
1595
1596     uint32_t *psz_unicode_start = psz_unicode;
1597
1598     line.xMin = line.xMax = line.yMin = line.yMax = 0;
1599
1600     /* Account for part of line already in position */
1601     for( i=0; i<*pi_start; i++ )
1602     {
1603         FT_BBox glyph_size;
1604
1605         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1606                             ft_glyph_bbox_pixels, &glyph_size );
1607
1608         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1609             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1610         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1611         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1612     }
1613     i_yMin = line.yMin;
1614     i_yMax = line.yMax;
1615
1616     if( line.xMax > 0 )
1617         b_first_on_line = VLC_FALSE;
1618
1619     while( *psz_unicode && ( *psz_unicode != '\n' ) )
1620     {
1621         FT_BBox glyph_size;
1622         FT_Glyph tmp_glyph;
1623         int i_error;
1624
1625         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1626         if( FT_HAS_KERNING( p_face ) && i_glyph_index
1627             && i_previous )
1628         {
1629             FT_Vector delta;
1630             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1631                             ft_kerning_default, &delta );
1632             *pi_pen_x += delta.x >> 6;
1633         }
1634         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1635         p_line->p_glyph_pos[ i ].y = i_pen_y;
1636
1637         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1638         if( i_error )
1639         {
1640             msg_Err( p_filter,
1641                    "unable to render text FT_Load_Glyph returned %d", i_error );
1642             p_line->pp_glyphs[ i ] = NULL;
1643             return VLC_EGENERIC;
1644         }
1645         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1646         if( i_error )
1647         {
1648             msg_Err( p_filter,
1649                     "unable to render text FT_Get_Glyph returned %d", i_error );
1650             p_line->pp_glyphs[ i ] = NULL;
1651             return VLC_EGENERIC;
1652         }
1653         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1654         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1655         if( i_error )
1656         {
1657             FT_Done_Glyph( tmp_glyph );
1658             continue;
1659         }
1660         if( b_uline )
1661         {
1662             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1663                                                p_face->size->metrics.y_scale));
1664             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1665                                             p_face->size->metrics.y_scale));
1666
1667             p_line->pi_underline_offset[ i ]  =
1668                                        ( aOffset < 0 ) ? -aOffset : aOffset;
1669             p_line->pi_underline_thickness[ i ] =
1670                                        ( aSize < 0 ) ? -aSize   : aSize;
1671         }
1672         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1673         p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
1674         p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
1675         p_line->p_fg_bg_ratio[ i ] = 0x00;
1676
1677         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1678                     glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1679         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1680         {
1681             while( --i > *pi_start )
1682             {
1683                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1684             }
1685
1686             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1687             {
1688                 psz_unicode--;
1689             }
1690             if( psz_unicode == psz_unicode_start )
1691             {
1692                 if( b_first_on_line )
1693                 {
1694                     msg_Warn( p_filter, "unbreakable string" );
1695                     p_line->pp_glyphs[ i ] = NULL;
1696                     return VLC_EGENERIC;
1697                 }
1698                 *pi_pen_x = i_pen_x_start;
1699
1700                 p_line->i_width = line.xMax;
1701                 p_line->i_height = __MAX( p_line->i_height,
1702                                           p_face->size->metrics.height >> 6 );
1703                 p_line->pp_glyphs[ i ] = NULL;
1704
1705                 p_result->x = __MAX( p_result->x, line.xMax );
1706                 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1707                                                          i_yMax - i_yMin ) );
1708
1709                 *pi_start = i;
1710                 return VLC_SUCCESS;
1711             }
1712             else
1713             {
1714                 *psz_unicode = '\n';
1715             }
1716             psz_unicode = psz_unicode_start;
1717             *pi_pen_x = i_pen_x_start;
1718             i_previous = 0;
1719
1720             line.yMax = i_yMax;
1721             line.yMin = i_yMin;
1722
1723             continue;
1724         }
1725         line.yMax = __MAX( line.yMax, glyph_size.yMax );
1726         line.yMin = __MIN( line.yMin, glyph_size.yMin );
1727
1728         i_previous = i_glyph_index;
1729         *pi_pen_x += p_face->glyph->advance.x >> 6;
1730         i++;
1731     }
1732     p_line->i_width = line.xMax;
1733     p_line->i_height = __MAX( p_line->i_height,
1734                               p_face->size->metrics.height >> 6 );
1735     p_line->pp_glyphs[ i ] = NULL;
1736
1737     p_result->x = __MAX( p_result->x, line.xMax );
1738     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1739                          line.yMax - line.yMin ) );
1740
1741     *pi_start = i;
1742
1743     /* Get rid of any text processed - if necessary repositioning
1744      * at the start of a new line of text
1745      */
1746     if( !*psz_unicode )
1747     {
1748         *psz_unicode_start = '\0';
1749     }
1750     else if( psz_unicode > psz_unicode_start )
1751     {
1752         for( i=0; psz_unicode[ i ]; i++ )
1753             psz_unicode_start[ i ] = psz_unicode[ i ];
1754         psz_unicode_start[ i ] = '\0';
1755     }
1756
1757     return VLC_SUCCESS;
1758 }
1759
1760 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1761                                   font_stack_t **p_fonts, int i_scale )
1762 {
1763     int        rv;
1764     char      *psz_fontname = NULL;
1765     uint32_t   i_font_color = 0xffffff;
1766     int        i_font_alpha = 0;
1767     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1768     int        i_font_size  = 24;
1769
1770     /* Default all attributes to the top font in the stack -- in case not
1771      * all attributes are specified in the sub-font
1772      */
1773     if( VLC_SUCCESS == PeekFont( p_fonts,
1774                                  &psz_fontname,
1775                                  &i_font_size,
1776                                  &i_font_color,
1777                                  &i_karaoke_bg_color ))
1778     {
1779         psz_fontname = strdup( psz_fontname );
1780         i_font_size = i_font_size * 1000 / i_scale;
1781     }
1782     i_font_alpha = (i_font_color >> 24) & 0xff;
1783     i_font_color &= 0x00ffffff;
1784
1785     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1786     {
1787         char *psz_name = xml_ReaderName( p_xml_reader );
1788         char *psz_value = xml_ReaderValue( p_xml_reader );
1789
1790         if( psz_name && psz_value )
1791         {
1792             if( !strcasecmp( "face", psz_name ) )
1793             {
1794                 if( psz_fontname ) free( psz_fontname );
1795                 psz_fontname = strdup( psz_value );
1796             }
1797             else if( !strcasecmp( "size", psz_name ) )
1798             {
1799                 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1800                 {
1801                     int i_value = atoi( psz_value );
1802
1803                     if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1804                         i_font_size += ( i_value * i_font_size ) / 10;
1805                     else if( i_value < -5 )
1806                         i_font_size = - i_value;
1807                     else if( i_value > 5 )
1808                         i_font_size = i_value;
1809                 }
1810                 else
1811                     i_font_size = atoi( psz_value );
1812             }
1813             else if( !strcasecmp( "color", psz_name )  &&
1814                      ( psz_value[0] == '#' ) )
1815             {
1816                 i_font_color = strtol( psz_value + 1, NULL, 16 );
1817                 i_font_color &= 0x00ffffff;
1818             }
1819             else if( !strcasecmp( "alpha", psz_name ) &&
1820                      ( psz_value[0] == '#' ) )
1821             {
1822                 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1823                 i_font_alpha &= 0xff;
1824             }
1825             free( psz_name );
1826             free( psz_value );
1827         }
1828     }
1829     rv = PushFont( p_fonts,
1830                    psz_fontname,
1831                    i_font_size * i_scale / 1000,
1832                    (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1833                    i_karaoke_bg_color );
1834
1835     free( psz_fontname );
1836
1837     return rv;
1838 }
1839
1840 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1841                        uint32_t **psz_text_out, uint32_t *pi_runs,
1842                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1843                        ft_style_t *p_style )
1844 {
1845     uint32_t      i_string_length = 0;
1846
1847     IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1848     *psz_text_out += i_string_length;
1849
1850     if( ppp_styles && ppi_run_lengths )
1851     {
1852         (*pi_runs)++;
1853
1854         if( *ppp_styles )
1855         {
1856             *ppp_styles = (ft_style_t **)
1857                 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1858         }
1859         else if( *pi_runs == 1 )
1860         {
1861             *ppp_styles = (ft_style_t **)
1862                 malloc( *pi_runs * sizeof( ft_style_t * ) );
1863         }
1864
1865         /* We have just malloc'ed this memory successfully -
1866          * *pi_runs HAS to be within the memory area of *ppp_styles */
1867         if( *ppp_styles )
1868         {
1869             (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1870             p_style = NULL;
1871         }
1872
1873         if( *ppi_run_lengths )
1874         {
1875             *ppi_run_lengths = (uint32_t *)
1876                 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1877         }
1878         else if( *pi_runs == 1 )
1879         {
1880             *ppi_run_lengths = (uint32_t *)
1881                 malloc( *pi_runs * sizeof( uint32_t ) );
1882         }
1883
1884         /* same remarks here */
1885         if( *ppi_run_lengths )
1886         {
1887             (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1888         }
1889     }
1890     /* If we couldn't use the p_style argument due to memory allocation
1891      * problems above, release it here.
1892      */
1893     if( p_style ) DeleteStyle( p_style );
1894 }
1895
1896 static void SetKaraokeLen( uint32_t i_runs, uint32_t *pi_run_lengths,
1897                            uint32_t i_k_runs, uint32_t *pi_k_run_lengths )
1898 {
1899     /* Karaoke tags _PRECEDE_ the text they specify a duration
1900      * for, therefore we are working out the length for the
1901      * previous tag, and first time through we have nothing
1902      */
1903     if( pi_k_run_lengths )
1904     {
1905         int i_chars = 0;
1906         uint32_t i;
1907
1908         /* Work out how many characters are presently in the string
1909          */
1910         for( i = 0; i < i_runs; i++ )
1911             i_chars += pi_run_lengths[ i ];
1912
1913         /* Subtract away those we've already allocated to other
1914          * karaoke tags
1915          */
1916         for( i = 0; i < i_k_runs; i++ )
1917             i_chars -= pi_k_run_lengths[ i ];
1918
1919         pi_k_run_lengths[ i_k_runs - 1 ] = i_chars;
1920     }
1921 }
1922
1923 static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
1924                           uint32_t **ppi_k_run_lengths,
1925                           uint32_t **ppi_k_durations )
1926 {
1927     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1928     {
1929         char *psz_name = xml_ReaderName( p_xml_reader );
1930         char *psz_value = xml_ReaderValue( p_xml_reader );
1931
1932         if( psz_name && psz_value &&
1933             !strcasecmp( "t", psz_name ) )
1934         {
1935             if( ppi_k_durations && ppi_k_run_lengths )
1936             {
1937                 (*pi_k_runs)++;
1938
1939                 if( *ppi_k_durations )
1940                 {
1941                     *ppi_k_durations = (uint32_t *)
1942                         realloc( *ppi_k_durations,
1943                                  *pi_k_runs * sizeof( uint32_t ) );
1944                 }
1945                 else if( *pi_k_runs == 1 )
1946                 {
1947                     *ppi_k_durations = (uint32_t *)
1948                         malloc( *pi_k_runs * sizeof( uint32_t ) );
1949                 }
1950
1951                 if( *ppi_k_run_lengths )
1952                 {
1953                     *ppi_k_run_lengths = (uint32_t *)
1954                         realloc( *ppi_k_run_lengths,
1955                                  *pi_k_runs * sizeof( uint32_t ) );
1956                 }
1957                 else if( *pi_k_runs == 1 )
1958                 {
1959                     *ppi_k_run_lengths = (uint32_t *)
1960                         malloc( *pi_k_runs * sizeof( uint32_t ) );
1961                 }
1962                 if( *ppi_k_durations )
1963                     (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
1964
1965                 if( *ppi_k_run_lengths )
1966                     (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
1967             }
1968         }
1969         if( psz_name )  free( psz_name );
1970         if( psz_value ) free( psz_value );
1971     }
1972 }
1973
1974 static int ProcessNodes( filter_t *p_filter,
1975                          xml_reader_t *p_xml_reader,
1976                          text_style_t *p_font_style,
1977                          uint32_t *psz_text,
1978                          int *pi_len,
1979
1980                          uint32_t *pi_runs,
1981                          uint32_t **ppi_run_lengths,
1982                          ft_style_t ***ppp_styles,
1983
1984                          vlc_bool_t b_karaoke,
1985                          uint32_t *pi_k_runs,
1986                          uint32_t **ppi_k_run_lengths,
1987                          uint32_t **ppi_k_durations )
1988 {
1989     int           rv             = VLC_SUCCESS;
1990     filter_sys_t *p_sys          = p_filter->p_sys;
1991     uint32_t     *psz_text_orig  = psz_text;
1992     font_stack_t *p_fonts        = NULL;
1993     vlc_value_t   val;
1994     int           i_scale        = 1000;
1995
1996     char *psz_node  = NULL;
1997
1998     vlc_bool_t b_italic = VLC_FALSE;
1999     vlc_bool_t b_bold   = VLC_FALSE;
2000     vlc_bool_t b_uline  = VLC_FALSE;
2001
2002     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2003         i_scale = val.i_int;
2004
2005     if( p_font_style )
2006     {
2007         rv = PushFont( &p_fonts,
2008                p_font_style->psz_fontname,
2009                p_font_style->i_font_size * i_scale / 1000,
2010                (p_font_style->i_font_color & 0xffffff) |
2011                    ((p_font_style->i_font_alpha & 0xff) << 24),
2012                (p_font_style->i_karaoke_background_color & 0xffffff) |
2013                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
2014
2015         if( p_font_style->i_style_flags & STYLE_BOLD )
2016             b_bold = VLC_TRUE;
2017         if( p_font_style->i_style_flags & STYLE_ITALIC )
2018             b_italic = VLC_TRUE;
2019         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
2020             b_uline = VLC_TRUE;
2021     }
2022     else
2023     {
2024         rv = PushFont( &p_fonts,
2025                        FC_DEFAULT_FONT,
2026                        p_sys->i_font_size,
2027                        0x00ffffff,
2028                        0x00ffffff );
2029     }
2030     if( rv != VLC_SUCCESS )
2031         return rv;
2032
2033     while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
2034     {
2035         switch ( xml_ReaderNodeType( p_xml_reader ) )
2036         {
2037             case XML_READER_NONE:
2038                 break;
2039             case XML_READER_ENDELEM:
2040                 psz_node = xml_ReaderName( p_xml_reader );
2041
2042                 if( psz_node )
2043                 {
2044                     if( !strcasecmp( "font", psz_node ) )
2045                         PopFont( &p_fonts );
2046                     else if( !strcasecmp( "b", psz_node ) )
2047                         b_bold   = VLC_FALSE;
2048                     else if( !strcasecmp( "i", psz_node ) )
2049                         b_italic = VLC_FALSE;
2050                     else if( !strcasecmp( "u", psz_node ) )
2051                         b_uline  = VLC_FALSE;
2052
2053                     free( psz_node );
2054                 }
2055                 break;
2056             case XML_READER_STARTELEM:
2057                 psz_node = xml_ReaderName( p_xml_reader );
2058                 if( psz_node )
2059                 {
2060                     if( !strcasecmp( "font", psz_node ) )
2061                         rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
2062                     else if( !strcasecmp( "b", psz_node ) )
2063                         b_bold = VLC_TRUE;
2064                     else if( !strcasecmp( "i", psz_node ) )
2065                         b_italic = VLC_TRUE;
2066                     else if( !strcasecmp( "u", psz_node ) )
2067                         b_uline = VLC_TRUE;
2068                     else if( !strcasecmp( "br", psz_node ) )
2069                     {
2070                         SetupLine( p_filter, "\n", &psz_text,
2071                                    pi_runs, ppi_run_lengths, ppp_styles,
2072                                    GetStyleFromFontStack( p_sys,
2073                                                           &p_fonts,
2074                                                           b_bold,
2075                                                           b_italic,
2076                                                           b_uline ) );
2077                     }
2078                     else if( !strcasecmp( "k", psz_node ) )
2079                     {
2080                         /* Only valid in karaoke */
2081                         if( b_karaoke )
2082                         {
2083                             if( *pi_k_runs > 0 )
2084                             {
2085                                 SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2086                                                *pi_k_runs, *ppi_k_run_lengths );
2087                             }
2088                             SetupKaraoke( p_xml_reader, pi_k_runs,
2089                                           ppi_k_run_lengths, ppi_k_durations );
2090                         }
2091                     }
2092
2093                     free( psz_node );
2094                 }
2095                 break;
2096             case XML_READER_TEXT:
2097                 psz_node = xml_ReaderValue( p_xml_reader );
2098                 if( psz_node )
2099                 {
2100                     /* Turn any multiple-whitespaces into single spaces */
2101                     char *s = strpbrk( psz_node, "\t\r\n " );
2102                     while( s )
2103                     {
2104                         int i_whitespace = strspn( s, "\t\r\n " );
2105
2106                         if( i_whitespace > 1 )
2107                             memmove( &s[1],
2108                                      &s[i_whitespace],
2109                                      strlen( s ) - i_whitespace + 1 );
2110                         *s++ = ' ';
2111
2112                         s = strpbrk( s, "\t\r\n " );
2113                     }
2114                     SetupLine( p_filter, psz_node, &psz_text,
2115                                pi_runs, ppi_run_lengths, ppp_styles,
2116                                GetStyleFromFontStack( p_sys,
2117                                                       &p_fonts,
2118                                                       b_bold,
2119                                                       b_italic,
2120                                                       b_uline ) );
2121                     free( psz_node );
2122                 }
2123                 break;
2124         }
2125         if( rv != VLC_SUCCESS )
2126         {
2127             psz_text = psz_text_orig;
2128             break;
2129         }
2130     }
2131     if( b_karaoke )
2132     {
2133         SetKaraokeLen( *pi_runs, *ppi_run_lengths,
2134                        *pi_k_runs, *ppi_k_run_lengths );
2135     }
2136
2137     *pi_len = psz_text - psz_text_orig;
2138
2139     while( VLC_SUCCESS == PopFont( &p_fonts ) );
2140
2141     return rv;
2142 }
2143
2144 static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
2145 {
2146     int k;
2147
2148     for( k=0; k < p_sys->i_font_attachments; k++ )
2149     {
2150         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
2151         int                 i_font_idx = 0;
2152         FT_Face             p_face = NULL;
2153
2154         while( 0 == FT_New_Memory_Face( p_sys->p_library,
2155                                         p_attach->p_data,
2156                                         p_attach->i_data,
2157                                         i_font_idx,
2158                                         &p_face ))
2159         {
2160             if( p_face )
2161             {
2162                 vlc_bool_t match = !strcasecmp( p_face->family_name,
2163                                                 p_style->psz_fontname );
2164
2165                 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
2166                     match = match && p_style->b_bold;
2167                 else
2168                     match = match && !p_style->b_bold;
2169
2170                 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
2171                     match = match && p_style->b_italic;
2172                 else
2173                     match = match && !p_style->b_italic;
2174
2175                 if(  match )
2176                 {
2177                     *pp_face = p_face;
2178                     return VLC_SUCCESS;
2179                 }
2180
2181                 FT_Done_Face( p_face );
2182             }
2183             i_font_idx++;
2184         }
2185     }
2186     return VLC_EGENERIC;
2187 }
2188
2189 static int CheckIfFontBuildComplete( filter_t *p_filter )
2190 {
2191     filter_sys_t   *p_sys = p_filter->p_sys;
2192     vlc_object_t   *p_fb = vlc_object_find_name( p_filter->p_libvlc,
2193                                                  "fontlist builder",
2194                                                  FIND_CHILD );
2195     if( p_fb )
2196     {
2197         vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
2198         vlc_value_t  val;
2199
2200         if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
2201         {
2202             p_sys->b_fontconfig_ok = val.b_bool;
2203
2204             if( p_sys->b_fontconfig_ok )
2205             {
2206                 FcInit();
2207                 p_sys->p_fontconfig = FcConfigGetCurrent();
2208             }
2209             else
2210                 msg_Dbg( p_filter, "Font Build still not complete" );
2211         }
2212         vlc_mutex_unlock( lock );
2213         vlc_object_release( p_fb );
2214
2215         return VLC_SUCCESS;
2216     }
2217     return VLC_EGENERIC;
2218 }
2219
2220 static int ProcessLines( filter_t *p_filter,
2221                          uint32_t *psz_text,
2222                          int i_len,
2223
2224                          uint32_t i_runs,
2225                          uint32_t *pi_run_lengths,
2226                          ft_style_t **pp_styles,
2227                          line_desc_t **pp_lines,
2228
2229                          FT_Vector *p_result,
2230
2231                          vlc_bool_t b_karaoke,
2232                          uint32_t i_k_runs,
2233                          uint32_t *pi_k_run_lengths,
2234                          uint32_t *pi_k_durations )
2235 {
2236     filter_sys_t   *p_sys = p_filter->p_sys;
2237     ft_style_t    **pp_char_styles;
2238     int            *p_new_positions = NULL;
2239     int8_t         *p_levels = NULL;
2240     uint8_t        *pi_karaoke_bar = NULL;
2241     uint32_t        i, j, k;
2242     int             i_prev;
2243
2244     /* Assign each character in the text string its style explicitly, so that
2245      * after the characters have been shuffled around by Fribidi, we can re-apply
2246      * the styles, and to simplify the calculation of runs within a line.
2247      */
2248     pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
2249     if( !pp_char_styles )
2250         return VLC_ENOMEM;
2251
2252     if( b_karaoke )
2253     {
2254         pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
2255         /* If we can't allocate sufficient memory for karaoke, continue anyway -
2256          * we just won't be able to display the progress bar; at least we'll
2257          * get the text.
2258          */
2259     }
2260
2261     i = 0;
2262     for( j = 0; j < i_runs; j++ )
2263         for( k = 0; k < pi_run_lengths[ j ]; k++ )
2264             pp_char_styles[ i++ ] = pp_styles[ j ];
2265
2266 #if defined(HAVE_FRIBIDI)
2267     {
2268         ft_style_t  **pp_char_styles_new;
2269         int         *p_old_positions;
2270         uint32_t    *p_fribidi_string;
2271         int start_pos, pos = 0;
2272
2273         pp_char_styles_new  = (ft_style_t **)
2274             malloc( i_len * sizeof( ft_style_t * ));
2275
2276         p_fribidi_string = (uint32_t *)
2277             malloc( (i_len + 1) * sizeof(uint32_t) );
2278         p_old_positions = (int *)
2279             malloc( (i_len + 1) * sizeof( int ) );
2280         p_new_positions = (int *)
2281             malloc( (i_len + 1) * sizeof( int ) );
2282         p_levels = (int8_t *)
2283             malloc( (i_len + 1) * sizeof( int8_t ) );
2284
2285         if( ! pp_char_styles_new ||
2286             ! p_fribidi_string ||
2287             ! p_old_positions ||
2288             ! p_new_positions ||
2289             ! p_levels )
2290         {
2291             msg_Err( p_filter, "out of memory" );
2292             if( p_levels )           free( p_levels );
2293             if( p_old_positions )    free( p_old_positions );
2294             if( p_new_positions )    free( p_new_positions );
2295             if( p_fribidi_string )   free( p_fribidi_string );
2296             if( pp_char_styles_new ) free( pp_char_styles_new );
2297             if( pi_karaoke_bar )     free( pi_karaoke_bar );
2298
2299             free( pp_char_styles );
2300             return VLC_ENOMEM;
2301         }
2302
2303         /* Do bidi conversion line-by-line */
2304         while(pos < i_len)
2305         {
2306             while(pos < i_len) {
2307                 if (psz_text[pos] != '\n')
2308                     break;
2309                 p_fribidi_string[pos] = psz_text[pos];
2310                 pp_char_styles_new[pos] = pp_char_styles[pos];
2311                 p_new_positions[pos] = pos;
2312                 p_levels[pos] = 0;
2313                 ++pos;
2314             }
2315             start_pos = pos;
2316             while(pos < i_len) {
2317                 if (psz_text[pos] == '\n')
2318                     break;
2319                 ++pos;
2320             }
2321             if (pos > start_pos)
2322             {
2323                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2324                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2325                         pos - start_pos, &base_dir,
2326                         (FriBidiChar*)p_fribidi_string + start_pos,
2327                         p_new_positions + start_pos,
2328                         p_old_positions,
2329                         p_levels + start_pos );
2330                 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
2331                 {
2332                     pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
2333                                                 p_old_positions[ j - start_pos ] ];
2334                     p_new_positions[ j ] += start_pos;
2335                 }
2336             }
2337         }
2338         free( p_old_positions );
2339         free( pp_char_styles );
2340         pp_char_styles = pp_char_styles_new;
2341         psz_text = p_fribidi_string;
2342         p_fribidi_string[ i_len ] = 0;
2343     }
2344 #endif
2345     /* Work out the karaoke */
2346     if( pi_karaoke_bar )
2347     {
2348         int64_t i_last_duration = 0;
2349         int64_t i_duration = 0;
2350         int64_t i_start_pos = 0;
2351         int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2352
2353         for( k = 0; k< i_k_runs; k++ )
2354         {
2355              double fraction = 0.0;
2356
2357              i_duration += pi_k_durations[ k ];
2358
2359              if( i_duration < i_elapsed )
2360              {
2361                  /* Completely finished this run-length -
2362                   * let it render normally */
2363
2364                  fraction = 1.0;
2365              }
2366              else if( i_elapsed < i_last_duration )
2367              {
2368                  /* Haven't got up to this segment yet -
2369                   * render it completely in karaoke BG mode */
2370
2371                  fraction = 0.0;
2372              }
2373              else
2374              {
2375                  /* Partway through this run */
2376
2377                  fraction = (double)(i_elapsed - i_last_duration) /
2378                             (double)pi_k_durations[ k ];
2379              }
2380              for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
2381              {
2382                  double shade = pi_k_run_lengths[ k ] * fraction;
2383
2384                  if( p_new_positions )
2385                      j = p_new_positions[ i_start_pos + i ];
2386                  else
2387                      j = i_start_pos + i;
2388
2389                  if( i < (uint32_t)shade )
2390                      pi_karaoke_bar[ j ] = 0xff;
2391                  else if( (double)i > shade )
2392                      pi_karaoke_bar[ j ] = 0x00;
2393                  else
2394                  {
2395                      shade -= (int)shade;
2396                      pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
2397                                    ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
2398                  }
2399              }
2400
2401              i_last_duration = i_duration;
2402              i_start_pos += pi_k_run_lengths[ k ];
2403         }
2404     }
2405     if( p_levels )         free( p_levels );
2406     if( p_new_positions )  free( p_new_positions );
2407
2408     FT_Vector tmp_result;
2409
2410     line_desc_t *p_line = NULL;
2411     line_desc_t *p_prev = NULL;
2412
2413     int i_pen_x = 0;
2414     int i_pen_y = 0;
2415     int i_posn  = 0;
2416
2417     p_result->x = p_result->y = 0;
2418     tmp_result.x = tmp_result.y = 0;
2419
2420     i_prev = 0;
2421     for( k = 0; k <= (uint32_t) i_len; k++ )
2422     {
2423         if( ( k == (uint32_t) i_len ) ||
2424           ( ( k > 0 ) &&
2425             !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
2426         {
2427             ft_style_t *p_style = pp_char_styles[ k - 1 ];
2428
2429             /* End of the current style run */
2430             FT_Face p_face = NULL;
2431             int      i_idx = 0;
2432
2433             /* Look for a match amongst our attachments first */
2434             CheckForEmbeddedFont( p_sys, &p_face, p_style );
2435
2436             if( !p_sys->b_fontconfig_ok )
2437             {
2438                 if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
2439                     msg_Err( p_filter, "Can't find FontBuilder thread!" );
2440             }
2441
2442             if( ! p_face && p_sys->b_fontconfig_ok )
2443             {
2444                 char *psz_fontfile;
2445                 vlc_mutex_lock( &p_sys->fontconfig_lock );
2446
2447                 psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
2448                                                   p_style->psz_fontname,
2449                                                   p_style->b_bold,
2450                                                   p_style->b_italic,
2451                                                   &i_idx );
2452                 vlc_mutex_unlock( &p_sys->fontconfig_lock );
2453
2454                 if( psz_fontfile && ! *psz_fontfile )
2455                 {
2456                     msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
2457                         " so using default font", p_style->psz_fontname,
2458                         ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
2459                                                (p_style->b_bold ? "(Bold)" :
2460                                              (p_style->b_italic ? "(Italic)" : ""))) );
2461                     free( psz_fontfile );
2462                     psz_fontfile = NULL;
2463                 }
2464
2465                 if( psz_fontfile )
2466                 {
2467                     if( FT_New_Face( p_sys->p_library,
2468                                 psz_fontfile, i_idx, &p_face ) )
2469                     {
2470                         free( psz_fontfile );
2471                         free( pp_char_styles );
2472 #if defined(HAVE_FRIBIDI)
2473                         free( psz_text );
2474 #endif
2475                         if( pi_karaoke_bar )
2476                             free( pi_karaoke_bar );
2477                         return VLC_EGENERIC;
2478                     }
2479                     free( psz_fontfile );
2480                 }
2481             }
2482             if( p_face &&
2483                 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2484             {
2485                 /* We've loaded a font face which is unhelpful for actually
2486                  * rendering text - fallback to the default one.
2487                  */
2488                  FT_Done_Face( p_face );
2489                  p_face = NULL;
2490             }
2491
2492             if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
2493                         ft_encoding_unicode ) ||
2494                 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
2495                     p_style->i_font_size ) )
2496             {
2497                 if( p_face ) FT_Done_Face( p_face );
2498                 free( pp_char_styles );
2499 #if defined(HAVE_FRIBIDI)
2500                 free( psz_text );
2501 #endif
2502                 if( pi_karaoke_bar )
2503                     free( pi_karaoke_bar );
2504                 return VLC_EGENERIC;
2505             }
2506             p_sys->i_use_kerning =
2507                         FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
2508
2509
2510             uint32_t *psz_unicode = (uint32_t *)
2511                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
2512             if( !psz_unicode )
2513             {
2514                 msg_Err( p_filter, "out of memory" );
2515                 if( p_face ) FT_Done_Face( p_face );
2516                 free( pp_char_styles );
2517                 free( psz_unicode );
2518 #if defined(HAVE_FRIBIDI)
2519                 free( psz_text );
2520 #endif
2521                 if( pi_karaoke_bar )
2522                     free( pi_karaoke_bar );
2523                 return VLC_ENOMEM;
2524             }
2525             memcpy( psz_unicode, psz_text + i_prev,
2526                                         sizeof( uint32_t ) * ( k - i_prev ) );
2527             psz_unicode[ k - i_prev ] = 0;
2528             while( *psz_unicode )
2529             {
2530                 if( !p_line )
2531                 {
2532                     if( !(p_line = NewLine( i_len - i_prev)) )
2533                     {
2534                         msg_Err( p_filter, "out of memory" );
2535                         if( p_face ) FT_Done_Face( p_face );
2536                         free( pp_char_styles );
2537                         free( psz_unicode );
2538 #if defined(HAVE_FRIBIDI)
2539                         free( psz_text );
2540 #endif
2541                         if( pi_karaoke_bar )
2542                             free( pi_karaoke_bar );
2543                         return VLC_ENOMEM;
2544                     }
2545                     /* New Color mode only works in YUVA rendering mode --
2546                      * (RGB mode has palette constraints on it). We therefore
2547                      * need to populate the legacy colour fields also.
2548                      */
2549                     p_line->b_new_color_mode = VLC_TRUE;
2550                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2551                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2552                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
2553                     p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
2554                     p_line->p_next = NULL;
2555                     i_pen_x = 0;
2556                     i_pen_y += tmp_result.y;
2557                     tmp_result.x = 0;
2558                     tmp_result.y = 0;
2559                     i_posn = 0;
2560                     if( p_prev ) p_prev->p_next = p_line;
2561                     else *pp_lines = p_line;
2562                 }
2563
2564                 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2565                                p_style->i_font_color, p_style->b_underline,
2566                                p_style->i_karaoke_bg_color,
2567                                p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2568                                &tmp_result ) != VLC_SUCCESS )
2569                 {
2570                     if( p_face ) FT_Done_Face( p_face );
2571                     free( pp_char_styles );
2572                     free( psz_unicode );
2573 #if defined(HAVE_FRIBIDI)
2574                     free( psz_text );
2575 #endif
2576                     if( pi_karaoke_bar )
2577                         free( pi_karaoke_bar );
2578                     return VLC_EGENERIC;
2579                 }
2580
2581                 if( *psz_unicode )
2582                 {
2583                     p_result->x = __MAX( p_result->x, tmp_result.x );
2584                     p_result->y += tmp_result.y;
2585
2586                     p_prev = p_line;
2587                     p_line = NULL;
2588
2589                     if( *psz_unicode == '\n')
2590                     {
2591                         uint32_t *c_ptr;
2592
2593                         for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
2594                         {
2595                             *c_ptr = *(c_ptr+1);
2596                         }
2597                     }
2598                 }
2599             }
2600             free( psz_unicode );
2601             if( p_face ) FT_Done_Face( p_face );
2602             i_prev = k;
2603         }
2604     }
2605     free( pp_char_styles );
2606 #if defined(HAVE_FRIBIDI)
2607     free( psz_text );
2608 #endif
2609     if( p_line )
2610     {
2611         p_result->x = __MAX( p_result->x, tmp_result.x );
2612         p_result->y += tmp_result.y;
2613     }
2614
2615     if( pi_karaoke_bar )
2616     {
2617         int i = 0;
2618         for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
2619         {
2620             for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
2621             {
2622                 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
2623                 {
2624                     /* do nothing */
2625                 }
2626                 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
2627                 {
2628                     /* 100% BG colour will render faster if we
2629                      * instead make it 100% FG colour, so leave
2630                      * the ratio alone and copy the value across
2631                      */
2632                     p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2633                 }
2634                 else
2635                 {
2636                     if( pi_karaoke_bar[ i ] & 0x80 )
2637                     {
2638                         /* Swap Left and Right sides over for Right aligned
2639                          * language text (eg. Arabic, Hebrew)
2640                          */
2641                         uint32_t i_tmp = p_line->p_fg_rgb[ k ];
2642
2643                         p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
2644                         p_line->p_bg_rgb[ k ] = i_tmp;
2645                     }
2646                     p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
2647                 }
2648             }
2649             /* Jump over the '\n' at the line-end */
2650             i++;
2651         }
2652         free( pi_karaoke_bar );
2653     }
2654
2655     return VLC_SUCCESS;
2656 }
2657
2658 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2659                        subpicture_region_t *p_region_in )
2660 {
2661     int          rv = VLC_SUCCESS;
2662     stream_t     *p_sub = NULL;
2663     xml_t        *p_xml = NULL;
2664     xml_reader_t *p_xml_reader = NULL;
2665
2666     if( !p_region_in || !p_region_in->psz_html )
2667         return VLC_EGENERIC;
2668
2669     /* Reset the default fontsize in case screen metrics have changed */
2670     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2671
2672     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2673                               (uint8_t *) p_region_in->psz_html,
2674                               strlen( p_region_in->psz_html ),
2675                               VLC_TRUE );
2676     if( p_sub )
2677     {
2678         p_xml = xml_Create( p_filter );
2679         if( p_xml )
2680         {
2681             vlc_bool_t b_karaoke = VLC_FALSE;
2682
2683             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2684             if( p_xml_reader )
2685             {
2686                 /* Look for Root Node */
2687                 if( xml_ReaderRead( p_xml_reader ) == 1 )
2688                 {
2689                     char *psz_node = xml_ReaderName( p_xml_reader );
2690
2691                     if( !strcasecmp( "karaoke", psz_node ) )
2692                     {
2693                         /* We're going to have to render the text a number
2694                          * of times to show the progress marker on the text.
2695                          */
2696                         var_SetBool( p_filter, "text-rerender", VLC_TRUE );
2697                         b_karaoke = VLC_TRUE;
2698                     }
2699                     else if( !strcasecmp( "text", psz_node ) )
2700                     {
2701                         b_karaoke = VLC_FALSE;
2702                     }
2703                     else
2704                     {
2705                         /* Only text and karaoke tags are supported */
2706                         msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
2707                         xml_ReaderDelete( p_xml, p_xml_reader );
2708                         p_xml_reader = NULL;
2709                         rv = VLC_EGENERIC;
2710                     }
2711
2712                     free( psz_node );
2713                 }
2714             }
2715
2716             if( p_xml_reader )
2717             {
2718                 uint32_t   *psz_text;
2719                 int         i_len = 0;
2720                 uint32_t    i_runs = 0;
2721                 uint32_t    i_k_runs = 0;
2722                 uint32_t   *pi_run_lengths = NULL;
2723                 uint32_t   *pi_k_run_lengths = NULL;
2724                 uint32_t   *pi_k_durations = NULL;
2725                 ft_style_t  **pp_styles = NULL;
2726                 FT_Vector    result;
2727                 line_desc_t  *p_lines = NULL;
2728
2729                 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2730                                                 sizeof( uint32_t ) );
2731                 if( psz_text )
2732                 {
2733                     uint32_t k;
2734
2735                     rv = ProcessNodes( p_filter, p_xml_reader,
2736                                   p_region_in->p_style, psz_text, &i_len,
2737                                   &i_runs, &pi_run_lengths, &pp_styles,
2738                                   b_karaoke, &i_k_runs, &pi_k_run_lengths,
2739                                   &pi_k_durations );
2740
2741                     p_region_out->i_x = p_region_in->i_x;
2742                     p_region_out->i_y = p_region_in->i_y;
2743
2744                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2745                     {
2746                         rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2747                                 pi_run_lengths, pp_styles, &p_lines, &result,
2748                                 b_karaoke, i_k_runs, pi_k_run_lengths,
2749                                 pi_k_durations );
2750                     }
2751
2752                     for( k=0; k<i_runs; k++)
2753                         DeleteStyle( pp_styles[k] );
2754                     free( pp_styles );
2755                     free( pi_run_lengths );
2756                     free( psz_text );
2757
2758                     /* Don't attempt to render text that couldn't be layed out
2759                      * properly.
2760                      */
2761                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
2762                     {
2763                         if( config_GetInt( p_filter, "freetype-yuvp" ) )
2764                         {
2765                             Render( p_filter, p_region_out, p_lines,
2766                                     result.x, result.y );
2767                         }
2768                         else
2769                         {
2770                             RenderYUVA( p_filter, p_region_out, p_lines,
2771                                     result.x, result.y );
2772                         }
2773                     }
2774                 }
2775                 FreeLines( p_lines );
2776
2777                 xml_ReaderDelete( p_xml, p_xml_reader );
2778             }
2779             xml_Delete( p_xml );
2780         }
2781         stream_Delete( p_sub );
2782     }
2783
2784     return rv;
2785 }
2786
2787 static char* FontConfig_Select( FcConfig* priv, const char* family,
2788                           vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2789 {
2790     FcResult result;
2791     FcPattern *pat, *p_pat;
2792     FcChar8* val_s;
2793     FcBool val_b;
2794
2795     pat = FcPatternCreate();
2796     if (!pat) return NULL;
2797
2798     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2799     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2800     FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2801     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2802
2803     FcDefaultSubstitute( pat );
2804
2805     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2806     {
2807         FcPatternDestroy( pat );
2808         return NULL;
2809     }
2810
2811     p_pat = FcFontMatch( priv, pat, &result );
2812     FcPatternDestroy( pat );
2813     if( !p_pat ) return NULL;
2814
2815     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2816         || ( val_b != FcTrue ) )
2817     {
2818         FcPatternDestroy( p_pat );
2819         return NULL;
2820     }
2821     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2822     {
2823         *i_idx = 0;
2824     }
2825
2826     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2827     {
2828         FcPatternDestroy( p_pat );
2829         return NULL;
2830     }
2831
2832     /*
2833     if( strcasecmp((const char*)val_s, family ) != 0 )
2834         msg_Warn( p_filter, "fontconfig: selected font family is not"
2835                             "the requested one: '%s' != '%s'\n",
2836                             (const char*)val_s, family );
2837     */
2838
2839     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2840     {
2841         FcPatternDestroy( p_pat );
2842         return NULL;
2843     }
2844
2845     FcPatternDestroy( p_pat );
2846     return strdup( (const char*)val_s );
2847 }
2848 #endif
2849
2850 static void FreeLine( line_desc_t *p_line )
2851 {
2852     unsigned int i;
2853     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2854     {
2855         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2856     }
2857     free( p_line->pp_glyphs );
2858     free( p_line->p_glyph_pos );
2859     free( p_line->p_fg_rgb );
2860     free( p_line->p_bg_rgb );
2861     free( p_line->p_fg_bg_ratio );
2862     free( p_line->pi_underline_offset );
2863     free( p_line->pi_underline_thickness );
2864     free( p_line );
2865 }
2866
2867 static void FreeLines( line_desc_t *p_lines )
2868 {
2869     line_desc_t *p_line, *p_next;
2870
2871     if( !p_lines ) return;
2872
2873     for( p_line = p_lines; p_line != NULL; p_line = p_next )
2874     {
2875         p_next = p_line->p_next;
2876         FreeLine( p_line );
2877     }
2878 }
2879
2880 static line_desc_t *NewLine( int i_count )
2881 {
2882     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2883
2884     if( !p_line ) return NULL;
2885     p_line->i_height = 0;
2886     p_line->i_width = 0;
2887     p_line->p_next = NULL;
2888
2889     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2890     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2891     p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2892     p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2893     p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
2894     p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2895     p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2896     if( ( p_line->pp_glyphs == NULL ) ||
2897         ( p_line->p_glyph_pos == NULL ) ||
2898         ( p_line->p_fg_rgb == NULL ) ||
2899         ( p_line->p_bg_rgb == NULL ) ||
2900         ( p_line->p_fg_bg_ratio == NULL ) ||
2901         ( p_line->pi_underline_offset == NULL ) ||
2902         ( p_line->pi_underline_thickness == NULL ) )
2903     {
2904         if( p_line->pi_underline_thickness )
2905             free( p_line->pi_underline_thickness );
2906         if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2907         if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
2908         if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
2909         if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
2910         if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2911         if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2912         free( p_line );
2913         return NULL;
2914     }
2915     p_line->pp_glyphs[0] = NULL;
2916     p_line->b_new_color_mode = VLC_FALSE;
2917
2918     return p_line;
2919 }
2920
2921 static int GetFontSize( filter_t *p_filter )
2922 {
2923     filter_sys_t *p_sys = p_filter->p_sys;
2924     vlc_value_t   val;
2925     int           i_size = 0;
2926
2927     if( p_sys->i_default_font_size )
2928     {
2929         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2930             i_size = p_sys->i_default_font_size * val.i_int / 1000;
2931         else
2932             i_size = p_sys->i_default_font_size;
2933     }
2934     else
2935     {
2936         var_Get( p_filter, "freetype-rel-fontsize", &val );
2937         i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2938         p_filter->p_sys->i_display_height =
2939             p_filter->fmt_out.video.i_height;
2940     }
2941     if( i_size <= 0 )
2942     {
2943         msg_Warn( p_filter, "invalid fontsize, using 12" );
2944         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
2945             i_size = 12 * val.i_int / 1000;
2946         else
2947             i_size = 12;
2948     }
2949     return i_size;
2950 }
2951
2952 static int SetFontSize( filter_t *p_filter, int i_size )
2953 {
2954     filter_sys_t *p_sys = p_filter->p_sys;
2955
2956     if( !i_size )
2957     {
2958         i_size = GetFontSize( p_filter );
2959
2960         msg_Dbg( p_filter, "using fontsize: %i", i_size );
2961     }
2962
2963     p_sys->i_font_size = i_size;
2964
2965     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2966     {
2967         msg_Err( p_filter, "couldn't set font size to %d", i_size );
2968         return VLC_EGENERIC;
2969     }
2970
2971     return VLC_SUCCESS;
2972 }
2973
2974 static void YUVFromRGB( uint32_t i_argb,
2975                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
2976 {
2977     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
2978     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
2979     int i_blue  = ( i_argb & 0x000000ff );
2980
2981     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
2982                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
2983     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
2984                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
2985     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
2986                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
2987 }