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