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