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