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