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