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