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