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