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