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