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