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