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