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