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