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