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