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