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