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