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