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