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