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