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