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