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