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