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