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