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