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