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