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