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