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