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