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