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