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