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