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