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