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