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