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