Fix double free and a 1 octet per subtitle memleak
[vlc.git] / modules / misc / freetype.c
blobd69d5e678c9a44482f38197aeb8370860396a495
1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2005 the VideoLAN team
5 * $Id$
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <string.h>
31 #ifdef HAVE_LINUX_LIMITS_H
32 # include <linux/limits.h>
33 #endif
35 #include <vlc/vlc.h>
36 #include <vlc_vout.h>
37 #include <vlc_osd.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
41 #include <vlc_xml.h>
43 #include <math.h>
45 #ifdef HAVE_ERRNO_H
46 # include <errno.h>
47 #endif
49 #include <ft2build.h>
50 #include FT_FREETYPE_H
51 #include FT_GLYPH_H
52 #define FT_FLOOR(X) ((X & -64) >> 6)
53 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
54 #define FT_MulFix(v, s) (((v)*(s))>>16)
56 #ifdef __APPLE__
57 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
58 #define FC_DEFAULT_FONT "Lucida Grande"
59 #elif defined( SYS_BEOS )
60 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
61 #define FC_DEFAULT_FONT "Swiss"
62 #elif defined( WIN32 )
63 #define DEFAULT_FONT "" /* Default font found at run-time */
64 #define FC_DEFAULT_FONT "Arial"
65 #else
66 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
67 #define FC_DEFAULT_FONT "Serif Bold"
68 #endif
70 #if defined(HAVE_FRIBIDI)
71 #include <fribidi/fribidi.h>
72 #endif
74 #ifdef HAVE_FONTCONFIG
75 #include <fontconfig/fontconfig.h>
76 #endif
78 typedef struct line_desc_t line_desc_t;
80 /*****************************************************************************
81 * Local prototypes
82 *****************************************************************************/
83 static int Create ( vlc_object_t * );
84 static void Destroy( vlc_object_t * );
86 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
87 static int RenderText( filter_t *, subpicture_region_t *,
88 subpicture_region_t * );
89 #ifdef HAVE_FONTCONFIG
90 static int RenderHtml( filter_t *, subpicture_region_t *,
91 subpicture_region_t * );
92 static char *FontConfig_Select( FcConfig *, const char *,
93 vlc_bool_t, vlc_bool_t, int * );
94 #endif
95 static line_desc_t *NewLine( byte_t * );
97 static int SetFontSize( filter_t *, int );
99 /*****************************************************************************
100 * Module descriptor
101 *****************************************************************************/
102 #define FONT_TEXT N_("Font")
103 #define FONT_LONGTEXT N_("Filename for the font you want to use")
104 #define FONTSIZE_TEXT N_("Font size in pixels")
105 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
106 "that will be rendered on the video. " \
107 "If set to something different than 0 this option will override the " \
108 "relative font size." )
109 #define OPACITY_TEXT N_("Opacity")
110 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
111 "text that will be rendered on the video. 0 = transparent, " \
112 "255 = totally opaque. " )
113 #define COLOR_TEXT N_("Text default color")
114 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
115 "the video. This must be an hexadecimal (like HTML colors). The first two "\
116 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
117 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
118 #define FONTSIZER_TEXT N_("Relative font size")
119 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
120 "fonts that will be rendered on the video. If absolute font size is set, "\
121 "relative size will be overriden." )
123 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
124 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
125 N_("Large"), N_("Larger") };
126 #define YUVP_TEXT N_("Use YUVP renderer")
127 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
128 "This option is only needed if you want to encode into DVB subtitles" )
129 #define EFFECT_TEXT N_("Font Effect")
130 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
131 "text to improve its readability." )
133 #define EFFECT_BACKGROUND 1
134 #define EFFECT_OUTLINE 2
135 #define EFFECT_OUTLINE_FAT 3
137 static int pi_effects[] = { 1, 2, 3 };
138 static char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
139 N_("Fat Outline") };
140 static int pi_color_values[] = {
141 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
142 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
143 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
145 static char *ppsz_color_descriptions[] = {
146 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
147 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
148 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
150 vlc_module_begin();
151 set_shortname( _("Text renderer"));
152 set_description( _("Freetype2 font renderer") );
153 set_category( CAT_VIDEO );
154 set_subcategory( SUBCAT_VIDEO_SUBPIC );
156 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
157 VLC_FALSE );
159 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
160 FONTSIZE_LONGTEXT, VLC_TRUE );
162 /* opacity valid on 0..255, with default 255 = fully opaque */
163 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
164 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
166 /* hook to the color values list, with default 0x00ffffff = white */
167 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
168 COLOR_LONGTEXT, VLC_FALSE );
169 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
171 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
172 FONTSIZER_LONGTEXT, VLC_FALSE );
173 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
174 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
175 EFFECT_LONGTEXT, VLC_FALSE );
176 change_integer_list( pi_effects, ppsz_effects_text, 0 );
178 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
179 YUVP_LONGTEXT, VLC_TRUE );
180 set_capability( "text renderer", 100 );
181 add_shortcut( "text" );
182 set_callbacks( Create, Destroy );
183 vlc_module_end();
185 struct line_desc_t
187 /** NULL-terminated list of glyphs making the string */
188 FT_BitmapGlyph *pp_glyphs;
189 /** list of relative positions for the glyphs */
190 FT_Vector *p_glyph_pos;
191 /** list of RGB information for styled text
192 * -- if the rendering mode supports it (RenderYUVA) and
193 * b_new_color_mode is set, then it becomes possible to
194 * have multicoloured text within the subtitles. */
195 uint32_t *p_rgb;
196 vlc_bool_t b_new_color_mode;
197 /** underline information -- only supplied if text should be underlined */
198 uint16_t *pi_underline_offset;
199 uint16_t *pi_underline_thickness;
201 int i_height;
202 int i_width;
203 int i_red, i_green, i_blue;
204 int i_alpha;
206 line_desc_t *p_next;
209 typedef struct font_stack_t font_stack_t;
210 struct font_stack_t
212 char *psz_name;
213 int i_size;
214 int i_color;
215 int i_alpha;
217 font_stack_t *p_next;
220 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
221 static void FreeLines( line_desc_t * );
222 static void FreeLine( line_desc_t * );
224 /*****************************************************************************
225 * filter_sys_t: freetype local data
226 *****************************************************************************
227 * This structure is part of the video output thread descriptor.
228 * It describes the freetype specific properties of an output thread.
229 *****************************************************************************/
230 struct filter_sys_t
232 FT_Library p_library; /* handle to library */
233 FT_Face p_face; /* handle to face object */
234 vlc_bool_t i_use_kerning;
235 uint8_t i_font_opacity;
236 int i_font_color;
237 int i_font_size;
238 int i_effect;
240 int i_default_font_size;
241 int i_display_height;
242 #ifdef HAVE_FONTCONFIG
243 FcConfig *p_fontconfig;
244 #endif
247 /*****************************************************************************
248 * Create: allocates osd-text video thread output method
249 *****************************************************************************
250 * This function allocates and initializes a Clone vout method.
251 *****************************************************************************/
252 static int Create( vlc_object_t *p_this )
254 filter_t *p_filter = (filter_t *)p_this;
255 filter_sys_t *p_sys;
256 char *psz_fontfile = NULL;
257 int i_error;
258 vlc_value_t val;
260 /* Allocate structure */
261 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
262 if( !p_sys )
264 msg_Err( p_filter, "out of memory" );
265 return VLC_ENOMEM;
267 p_sys->p_face = 0;
268 p_sys->p_library = 0;
269 p_sys->i_font_size = 0;
270 p_sys->i_display_height = 0;
272 var_Create( p_filter, "freetype-font",
273 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
274 var_Create( p_filter, "freetype-fontsize",
275 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
276 var_Create( p_filter, "freetype-rel-fontsize",
277 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
278 var_Create( p_filter, "freetype-opacity",
279 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
280 var_Create( p_filter, "freetype-effect",
281 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
282 var_Get( p_filter, "freetype-opacity", &val );
283 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
284 var_Create( p_filter, "freetype-color",
285 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
286 var_Get( p_filter, "freetype-color", &val );
287 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
288 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
290 /* Look what method was requested */
291 var_Get( p_filter, "freetype-font", &val );
292 psz_fontfile = val.psz_string;
293 if( !psz_fontfile || !*psz_fontfile )
295 if( psz_fontfile ) free( psz_fontfile );
296 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
297 #ifdef WIN32
298 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
299 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
300 #elif __APPLE__
301 strcpy( psz_fontfile, DEFAULT_FONT );
302 #else
303 msg_Err( p_filter, "user didn't specify a font" );
304 goto error;
305 #endif
308 #ifdef HAVE_FONTCONFIG
309 if( FcInit() )
310 p_sys->p_fontconfig = FcConfigGetCurrent();
311 else
312 p_sys->p_fontconfig = NULL;
313 #endif
314 i_error = FT_Init_FreeType( &p_sys->p_library );
315 if( i_error )
317 msg_Err( p_filter, "couldn't initialize freetype" );
318 goto error;
321 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
322 0, &p_sys->p_face );
323 if( i_error == FT_Err_Unknown_File_Format )
325 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
326 goto error;
328 else if( i_error )
330 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
331 goto error;
334 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
335 if( i_error )
337 msg_Err( p_filter, "font has no unicode translation table" );
338 goto error;
341 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
343 var_Get( p_filter, "freetype-fontsize", &val );
344 p_sys->i_default_font_size = val.i_int;
345 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
347 if( psz_fontfile ) free( psz_fontfile );
348 p_filter->pf_render_text = RenderText;
349 #ifdef HAVE_FONTCONFIG
350 p_filter->pf_render_html = RenderHtml;
351 #else
352 p_filter->pf_render_html = NULL;
353 #endif
354 return VLC_SUCCESS;
356 error:
357 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
358 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
359 if( psz_fontfile ) free( psz_fontfile );
360 free( p_sys );
361 return VLC_EGENERIC;
364 /*****************************************************************************
365 * Destroy: destroy Clone video thread output method
366 *****************************************************************************
367 * Clean up all data and library connections
368 *****************************************************************************/
369 static void Destroy( vlc_object_t *p_this )
371 filter_t *p_filter = (filter_t *)p_this;
372 filter_sys_t *p_sys = p_filter->p_sys;
374 #ifdef HAVE_FONTCONFIG
375 FcConfigDestroy( p_sys->p_fontconfig );
376 p_sys->p_fontconfig = NULL;
377 /* FcFini asserts calling the subfunction FcCacheFini()
378 * even if no other library functions have been made since FcInit(),
379 * so don't call it. */
380 #endif
381 FT_Done_Face( p_sys->p_face );
382 FT_Done_FreeType( p_sys->p_library );
383 free( p_sys );
386 /*****************************************************************************
387 * Render: place string in picture
388 *****************************************************************************
389 * This function merges the previously rendered freetype glyphs into a picture
390 *****************************************************************************/
391 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
392 line_desc_t *p_line, int i_width, int i_height )
394 static uint8_t pi_gamma[16] =
395 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
396 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
398 uint8_t *p_dst;
399 video_format_t fmt;
400 int i, x, y, i_pitch;
401 uint8_t i_y; /* YUV values, derived from incoming RGB */
402 int8_t i_u, i_v;
403 subpicture_region_t *p_region_tmp;
405 /* Create a new subpicture region */
406 memset( &fmt, 0, sizeof(video_format_t) );
407 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
408 fmt.i_aspect = 0;
409 fmt.i_width = fmt.i_visible_width = i_width + 4;
410 fmt.i_height = fmt.i_visible_height = i_height + 4;
411 fmt.i_x_offset = fmt.i_y_offset = 0;
412 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
413 if( !p_region_tmp )
415 msg_Err( p_filter, "cannot allocate SPU region" );
416 return VLC_EGENERIC;
419 p_region->fmt = p_region_tmp->fmt;
420 p_region->picture = p_region_tmp->picture;
421 free( p_region_tmp );
423 /* Calculate text color components */
424 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
425 25 * p_line->i_blue + 128) >> 8) + 16;
426 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
427 112 * p_line->i_blue + 128) >> 8) + 128;
428 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
429 18 * p_line->i_blue + 128) >> 8) + 128;
431 /* Build palette */
432 fmt.p_palette->i_entries = 16;
433 for( i = 0; i < 8; i++ )
435 fmt.p_palette->palette[i][0] = 0;
436 fmt.p_palette->palette[i][1] = 0x80;
437 fmt.p_palette->palette[i][2] = 0x80;
438 fmt.p_palette->palette[i][3] = pi_gamma[i];
439 fmt.p_palette->palette[i][3] =
440 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
442 for( i = 8; i < fmt.p_palette->i_entries; i++ )
444 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
445 fmt.p_palette->palette[i][1] = i_u;
446 fmt.p_palette->palette[i][2] = i_v;
447 fmt.p_palette->palette[i][3] = pi_gamma[i];
448 fmt.p_palette->palette[i][3] =
449 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
452 p_dst = p_region->picture.Y_PIXELS;
453 i_pitch = p_region->picture.Y_PITCH;
455 /* Initialize the region pixels */
456 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
458 for( ; p_line != NULL; p_line = p_line->p_next )
460 int i_glyph_tmax = 0;
461 int i_bitmap_offset, i_offset, i_align_offset = 0;
462 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
464 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
465 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
468 if( p_line->i_width < i_width )
470 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
471 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
473 i_align_offset = i_width - p_line->i_width;
475 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
476 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
478 i_align_offset = ( i_width - p_line->i_width ) / 2;
482 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
484 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
486 i_offset = ( p_line->p_glyph_pos[ i ].y +
487 i_glyph_tmax - p_glyph->top + 2 ) *
488 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
489 i_align_offset;
491 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
493 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
495 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
496 p_dst[i_offset+x] =
497 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
499 i_offset += i_pitch;
504 /* Outlining (find something better than nearest neighbour filtering ?) */
505 if( 1 )
507 uint8_t *p_dst = p_region->picture.Y_PIXELS;
508 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
509 uint8_t left, current;
511 for( y = 1; y < (int)fmt.i_height - 1; y++ )
513 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
514 p_dst += p_region->picture.Y_PITCH;
515 left = 0;
517 for( x = 1; x < (int)fmt.i_width - 1; x++ )
519 current = p_dst[x];
520 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
521 p_dst[x -1 + p_region->picture.Y_PITCH ] + p_dst[x + p_region->picture.Y_PITCH] + p_dst[x + 1 + p_region->picture.Y_PITCH]) / 16;
522 left = current;
525 memset( p_top, 0, fmt.i_width );
528 return VLC_SUCCESS;
531 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
532 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
533 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
534 int i_glyph_tmax, int i_align_offset,
535 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
536 subpicture_region_t *p_region)
538 int y, x, z;
539 int i_pitch;
540 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
542 p_dst_y = p_region->picture.Y_PIXELS;
543 p_dst_u = p_region->picture.U_PIXELS;
544 p_dst_v = p_region->picture.V_PIXELS;
545 p_dst_a = p_region->picture.A_PIXELS;
546 i_pitch = p_region->picture.A_PITCH;
548 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
549 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
551 for( y = 0; y < i_line_thickness; y++ )
553 int i_extra = p_this_glyph->bitmap.width;
555 if( b_ul_next_char )
557 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
558 (p_this_glyph_pos->x + p_this_glyph->left);
560 for( x = 0; x < i_extra; x++ )
562 vlc_bool_t b_ok = VLC_TRUE;
564 /* break the underline around the tails of any glyphs which cross it */
565 for( z = x - i_line_thickness;
566 z < x + i_line_thickness && b_ok;
567 z++ )
569 if( p_next_glyph && ( z >= i_extra ) )
571 int i_row = i_line_offset + p_next_glyph->top + y;
573 if( ( p_next_glyph->bitmap.rows > i_row ) &&
574 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
576 b_ok = VLC_FALSE;
579 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
581 int i_row = i_line_offset + p_this_glyph->top + y;
583 if( ( p_this_glyph->bitmap.rows > i_row ) &&
584 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
586 b_ok = VLC_FALSE;
591 if( b_ok )
593 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
594 p_dst_u[i_offset+x] = i_u;
595 p_dst_v[i_offset+x] = i_v;
596 p_dst_a[i_offset+x] = 255;
599 i_offset += i_pitch;
603 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
605 uint8_t *p_dst = p_region->picture.A_PIXELS;
606 int i_pitch = p_region->picture.A_PITCH;
607 int x,y;
609 for( ; p_line != NULL; p_line = p_line->p_next )
611 int i_glyph_tmax=0, i = 0;
612 int i_bitmap_offset, i_offset, i_align_offset = 0;
613 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
615 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
616 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
619 if( p_line->i_width < i_width )
621 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
622 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
624 i_align_offset = i_width - p_line->i_width;
626 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
627 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
629 i_align_offset = ( i_width - p_line->i_width ) / 2;
633 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
635 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
637 i_offset = ( p_line->p_glyph_pos[ i ].y +
638 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
639 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
640 i_align_offset +xoffset;
642 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
644 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
646 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
647 if( p_dst[i_offset+x] <
648 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
649 p_dst[i_offset+x] =
650 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
652 i_offset += i_pitch;
659 /*****************************************************************************
660 * Render: place string in picture
661 *****************************************************************************
662 * This function merges the previously rendered freetype glyphs into a picture
663 *****************************************************************************/
664 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
665 line_desc_t *p_line, int i_width, int i_height )
667 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
668 video_format_t fmt;
669 int i, x, y, i_pitch, i_alpha;
670 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
671 subpicture_region_t *p_region_tmp;
673 if( i_width == 0 || i_height == 0 )
674 return VLC_SUCCESS;
676 /* Create a new subpicture region */
677 memset( &fmt, 0, sizeof(video_format_t) );
678 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
679 fmt.i_aspect = 0;
680 fmt.i_width = fmt.i_visible_width = i_width + 6;
681 fmt.i_height = fmt.i_visible_height = i_height + 6;
682 fmt.i_x_offset = fmt.i_y_offset = 0;
683 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
684 if( !p_region_tmp )
686 msg_Err( p_filter, "cannot allocate SPU region" );
687 return VLC_EGENERIC;
690 p_region->fmt = p_region_tmp->fmt;
691 p_region->picture = p_region_tmp->picture;
692 free( p_region_tmp );
694 /* Calculate text color components */
695 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
696 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
697 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
698 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
699 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
700 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
701 i_alpha = p_line->i_alpha;
703 p_dst_y = p_region->picture.Y_PIXELS;
704 p_dst_u = p_region->picture.U_PIXELS;
705 p_dst_v = p_region->picture.V_PIXELS;
706 p_dst_a = p_region->picture.A_PIXELS;
707 i_pitch = p_region->picture.A_PITCH;
709 /* Initialize the region pixels */
710 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
712 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
713 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
714 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
715 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
717 else
719 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
720 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
721 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
722 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
724 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
725 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
727 DrawBlack( p_line, i_width, p_region, 0, 0);
728 DrawBlack( p_line, i_width, p_region, -1, 0);
729 DrawBlack( p_line, i_width, p_region, 0, -1);
730 DrawBlack( p_line, i_width, p_region, 1, 0);
731 DrawBlack( p_line, i_width, p_region, 0, 1);
734 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
736 DrawBlack( p_line, i_width, p_region, -1, -1);
737 DrawBlack( p_line, i_width, p_region, -1, 1);
738 DrawBlack( p_line, i_width, p_region, 1, -1);
739 DrawBlack( p_line, i_width, p_region, 1, 1);
741 DrawBlack( p_line, i_width, p_region, -2, 0);
742 DrawBlack( p_line, i_width, p_region, 0, -2);
743 DrawBlack( p_line, i_width, p_region, 2, 0);
744 DrawBlack( p_line, i_width, p_region, 0, 2);
746 DrawBlack( p_line, i_width, p_region, -2, -2);
747 DrawBlack( p_line, i_width, p_region, -2, 2);
748 DrawBlack( p_line, i_width, p_region, 2, -2);
749 DrawBlack( p_line, i_width, p_region, 2, 2);
751 DrawBlack( p_line, i_width, p_region, -3, 0);
752 DrawBlack( p_line, i_width, p_region, 0, -3);
753 DrawBlack( p_line, i_width, p_region, 3, 0);
754 DrawBlack( p_line, i_width, p_region, 0, 3);
757 for( ; p_line != NULL; p_line = p_line->p_next )
759 int i_glyph_tmax = 0;
760 int i_bitmap_offset, i_offset, i_align_offset = 0;
761 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
763 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
764 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
767 if( p_line->i_width < i_width )
769 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
770 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
772 i_align_offset = i_width - p_line->i_width;
774 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
775 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
777 i_align_offset = ( i_width - p_line->i_width ) / 2;
781 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
783 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
785 i_offset = ( p_line->p_glyph_pos[ i ].y +
786 i_glyph_tmax - p_glyph->top + 3 ) *
787 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
788 i_align_offset;
790 if( p_line->b_new_color_mode )
792 /* Every glyph can (and in fact must) have its own color */
793 int i_red = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
794 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >> 8;
795 int i_blue = ( p_line->p_rgb[ i ] & 0x000000ff );
797 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
798 802 * i_blue + 4096 + 131072 ) >> 13, 235);
799 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
800 3598 * i_blue + 4096 + 1048576) >> 13, 240);
801 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
802 -585 * i_blue + 4096 + 1048576) >> 13, 240);
805 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
807 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
809 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
811 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
812 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
814 p_dst_u[i_offset+x] = i_u;
815 p_dst_v[i_offset+x] = i_v;
817 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
818 p_dst_a[i_offset+x] = 0xff;
821 i_offset += i_pitch;
824 if( p_line->pi_underline_thickness[ i ] )
826 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
827 p_line->pi_underline_offset[ i ],
828 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
829 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
830 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
831 i_glyph_tmax, i_align_offset,
832 i_y, i_u, i_v, i_alpha,
833 p_region);
838 /* Apply the alpha setting */
839 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
840 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
842 return VLC_SUCCESS;
846 * This function renders a text subpicture region into another one.
847 * It also calculates the size needed for this string, and renders the
848 * needed glyphs into memory. It is used as pf_add_string callback in
849 * the vout method by this module
851 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
852 subpicture_region_t *p_region_in )
854 filter_sys_t *p_sys = p_filter->p_sys;
855 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
856 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
857 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
858 int i_string_length;
859 char *psz_string;
860 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
861 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
863 FT_BBox line;
864 FT_BBox glyph_size;
865 FT_Vector result;
866 FT_Glyph tmp_glyph;
868 /* Sanity check */
869 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
870 psz_string = p_region_in->psz_text;
871 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
873 if( p_region_in->p_style )
875 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
876 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
877 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
879 else
881 i_font_color = p_sys->i_font_color;
882 i_font_alpha = 255 - p_sys->i_font_opacity;
883 i_font_size = p_sys->i_default_font_size;
886 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
887 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
888 SetFontSize( p_filter, i_font_size );
890 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
891 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
892 i_blue = i_font_color & 0x000000FF;
894 result.x = result.y = 0;
895 line.xMin = line.xMax = line.yMin = line.yMax = 0;
897 psz_unicode = psz_unicode_orig =
898 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
899 if( psz_unicode == NULL )
901 msg_Err( p_filter, "out of memory" );
902 goto error;
904 #if defined(WORDS_BIGENDIAN)
905 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
906 #else
907 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
908 #endif
909 if( iconv_handle == (vlc_iconv_t)-1 )
911 msg_Warn( p_filter, "unable to do conversion" );
912 goto error;
916 char *p_out_buffer;
917 const char *p_in_buffer = psz_string;
918 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
919 i_in_bytes = strlen( psz_string );
920 i_out_bytes = i_in_bytes * sizeof( uint32_t );
921 i_out_bytes_left = i_out_bytes;
922 p_out_buffer = (char *)psz_unicode;
923 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
924 &p_out_buffer, &i_out_bytes_left );
926 vlc_iconv_close( iconv_handle );
928 if( i_in_bytes )
930 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
931 "bytes left %d", strerror(errno), (int)i_in_bytes );
932 goto error;
934 *(uint32_t*)p_out_buffer = 0;
935 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
938 #if defined(HAVE_FRIBIDI)
940 uint32_t *p_fribidi_string;
941 int start_pos, pos = 0;
943 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
945 /* Do bidi conversion line-by-line */
946 while(pos < i_string_length)
948 while(pos < i_string_length) {
949 i_char = psz_unicode[pos];
950 if (i_char != '\r' && i_char != '\n')
951 break;
952 p_fribidi_string[pos] = i_char;
953 ++pos;
955 start_pos = pos;
956 while(pos < i_string_length) {
957 i_char = psz_unicode[pos];
958 if (i_char == '\r' || i_char == '\n')
959 break;
960 ++pos;
962 if (pos > start_pos)
964 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
965 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
966 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
970 free( psz_unicode_orig );
971 psz_unicode = psz_unicode_orig = p_fribidi_string;
972 p_fribidi_string[ i_string_length ] = 0;
974 #endif
976 /* Calculate relative glyph positions and a bounding box for the
977 * entire string */
978 if( !(p_line = NewLine( (byte_t *)psz_string )) )
980 msg_Err( p_filter, "out of memory" );
981 goto error;
983 p_lines = p_line;
984 i_pen_x = i_pen_y = 0;
985 i_previous = i = 0;
986 psz_line_start = psz_unicode;
988 #define face p_sys->p_face
989 #define glyph face->glyph
991 while( *psz_unicode )
993 i_char = *psz_unicode++;
994 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
996 continue;
999 if( i_char == '\n' )
1001 psz_line_start = psz_unicode;
1002 if( !(p_next = NewLine( (byte_t *)psz_string )) )
1004 msg_Err( p_filter, "out of memory" );
1005 goto error;
1007 p_line->p_next = p_next;
1008 p_line->i_width = line.xMax;
1009 p_line->i_height = face->size->metrics.height >> 6;
1010 p_line->pp_glyphs[ i ] = NULL;
1011 p_line->i_alpha = i_font_alpha;
1012 p_line->i_red = i_red;
1013 p_line->i_green = i_green;
1014 p_line->i_blue = i_blue;
1015 p_prev = p_line;
1016 p_line = p_next;
1017 result.x = __MAX( result.x, line.xMax );
1018 result.y += face->size->metrics.height >> 6;
1019 i_pen_x = 0;
1020 i_previous = i = 0;
1021 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1022 i_pen_y += face->size->metrics.height >> 6;
1023 #if 0
1024 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1025 #endif
1026 continue;
1029 i_glyph_index = FT_Get_Char_Index( face, i_char );
1030 if( p_sys->i_use_kerning && i_glyph_index
1031 && i_previous )
1033 FT_Vector delta;
1034 FT_Get_Kerning( face, i_previous, i_glyph_index,
1035 ft_kerning_default, &delta );
1036 i_pen_x += delta.x >> 6;
1039 p_line->p_glyph_pos[ i ].x = i_pen_x;
1040 p_line->p_glyph_pos[ i ].y = i_pen_y;
1041 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1042 if( i_error )
1044 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1045 " %d", i_error );
1046 goto error;
1048 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1049 if( i_error )
1051 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1052 "%d", i_error );
1053 goto error;
1055 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1056 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1057 if( i_error )
1059 FT_Done_Glyph( tmp_glyph );
1060 continue;
1062 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1064 /* Do rest */
1065 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1066 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1067 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1069 p_line->pp_glyphs[ i ] = NULL;
1070 FreeLine( p_line );
1071 p_line = NewLine( (byte_t *)psz_string );
1072 if( p_prev ) p_prev->p_next = p_line;
1073 else p_lines = p_line;
1075 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1077 psz_unicode--;
1079 if( psz_unicode == psz_line_start )
1081 msg_Warn( p_filter, "unbreakable string" );
1082 goto error;
1084 else
1086 *psz_unicode = '\n';
1088 psz_unicode = psz_line_start;
1089 i_pen_x = 0;
1090 i_previous = i = 0;
1091 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1092 continue;
1094 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1095 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1097 i_previous = i_glyph_index;
1098 i_pen_x += glyph->advance.x >> 6;
1099 i++;
1102 p_line->i_width = line.xMax;
1103 p_line->i_height = face->size->metrics.height >> 6;
1104 p_line->pp_glyphs[ i ] = NULL;
1105 p_line->i_alpha = i_font_alpha;
1106 p_line->i_red = i_red;
1107 p_line->i_green = i_green;
1108 p_line->i_blue = i_blue;
1109 result.x = __MAX( result.x, line.xMax );
1110 result.y += line.yMax - line.yMin;
1112 #undef face
1113 #undef glyph
1115 p_region_out->i_x = p_region_in->i_x;
1116 p_region_out->i_y = p_region_in->i_y;
1118 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1119 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1120 else
1121 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1123 if( psz_unicode_orig ) free( psz_unicode_orig );
1124 FreeLines( p_lines );
1125 return VLC_SUCCESS;
1127 error:
1128 if( psz_unicode_orig ) free( psz_unicode_orig );
1129 FreeLines( p_lines );
1130 return VLC_EGENERIC;
1133 #ifdef HAVE_FONTCONFIG
1134 static int PushFont( font_stack_t **p_font, char *psz_name, int i_size,
1135 int i_color, int i_alpha )
1137 font_stack_t *p_new;
1139 if( !p_font )
1140 return VLC_EGENERIC;
1142 p_new = malloc( sizeof( font_stack_t ) );
1143 p_new->p_next = NULL;
1145 if( psz_name )
1146 p_new->psz_name = strdup( psz_name );
1147 else
1148 p_new->psz_name = NULL;
1150 p_new->i_size = i_size;
1151 p_new->i_color = i_color;
1152 p_new->i_alpha = i_alpha;
1154 if( !*p_font )
1156 *p_font = p_new;
1158 else
1160 font_stack_t *p_last;
1162 for( p_last = *p_font;
1163 p_last->p_next;
1164 p_last = p_last->p_next )
1167 p_last->p_next = p_new;
1169 return VLC_SUCCESS;
1172 static int PopFont( font_stack_t **p_font )
1174 font_stack_t *p_last, *p_next_to_last;
1176 if( !p_font || !*p_font )
1177 return VLC_EGENERIC;
1179 p_next_to_last = NULL;
1180 for( p_last = *p_font;
1181 p_last->p_next;
1182 p_last = p_last->p_next )
1184 p_next_to_last = p_last;
1187 if( p_next_to_last )
1188 p_next_to_last->p_next = NULL;
1189 else
1190 *p_font = NULL;
1192 free( p_last->psz_name );
1193 free( p_last );
1195 return VLC_SUCCESS;
1198 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1199 int *i_color, int *i_alpha )
1201 font_stack_t *p_last;
1203 if( !p_font || !*p_font )
1204 return VLC_EGENERIC;
1206 for( p_last=*p_font;
1207 p_last->p_next;
1208 p_last=p_last->p_next )
1211 *psz_name = p_last->psz_name;
1212 *i_size = p_last->i_size;
1213 *i_color = p_last->i_color;
1214 *i_alpha = p_last->i_alpha;
1216 return VLC_SUCCESS;
1219 static uint32_t *IconvText( filter_t *p_filter, char *psz_string )
1221 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1222 uint32_t *psz_unicode;
1223 int i_string_length;
1225 psz_unicode =
1226 malloc( ( strlen( psz_string ) + 1 ) * sizeof( uint32_t ) );
1227 if( psz_unicode == NULL )
1229 msg_Err( p_filter, "out of memory" );
1230 return NULL;;
1232 #if defined(WORDS_BIGENDIAN)
1233 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1234 #else
1235 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1236 #endif
1237 if( iconv_handle == (vlc_iconv_t)-1 )
1239 msg_Warn( p_filter, "unable to do conversion" );
1240 free( psz_unicode );
1241 return NULL;;
1245 char *p_in_buffer, *p_out_buffer;
1246 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1247 i_in_bytes = strlen( psz_string );
1248 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1249 i_out_bytes_left = i_out_bytes;
1250 p_in_buffer = psz_string;
1251 p_out_buffer = (char *)psz_unicode;
1252 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
1253 &p_out_buffer, &i_out_bytes_left );
1255 vlc_iconv_close( iconv_handle );
1257 if( i_in_bytes )
1259 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1260 "bytes left %d", strerror(errno), (int)i_in_bytes );
1261 free( psz_unicode );
1262 return NULL;;
1264 *(uint32_t*)p_out_buffer = 0;
1265 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1268 #if defined(HAVE_FRIBIDI)
1270 uint32_t *p_fribidi_string;
1272 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
1274 /* Do bidi conversion line-by-line */
1275 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1276 fribidi_log2vis((FriBidiChar*)psz_unicode, i_string_length,
1277 &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0);
1279 free( psz_unicode );
1280 psz_unicode = p_fribidi_string;
1281 p_fribidi_string[ i_string_length ] = 0;
1283 #endif
1284 return psz_unicode;
1287 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1288 vlc_bool_t b_uline, line_desc_t *p_line, uint32_t *psz_unicode,
1289 int *pi_pen_x, int i_pen_y, int *pi_start,
1290 FT_Vector *p_result )
1292 FT_BBox line;
1293 int i_yMin, i_yMax;
1294 int i;
1296 int i_previous = 0;
1297 int i_pen_x_start = *pi_pen_x;
1299 uint32_t *psz_unicode_start = psz_unicode;
1301 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1303 /* Account for part of line already in position */
1304 for( i=0; i<*pi_start; i++ )
1306 FT_BBox glyph_size;
1308 FT_Glyph_Get_CBox( p_line->pp_glyphs[ i ], ft_glyph_bbox_pixels, &glyph_size );
1310 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1311 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1312 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1313 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1315 i_yMin = line.yMin;
1316 i_yMax = line.yMax;
1318 while( *psz_unicode && ( *psz_unicode != 0xffff ) )
1320 FT_BBox glyph_size;
1321 FT_Glyph tmp_glyph;
1322 int i_error;
1324 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1325 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1326 && i_previous )
1328 FT_Vector delta;
1329 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1330 ft_kerning_default, &delta );
1331 *pi_pen_x += delta.x >> 6;
1333 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1334 p_line->p_glyph_pos[ i ].y = i_pen_y;
1336 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1337 if( i_error )
1339 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned %d", i_error );
1340 p_line->pp_glyphs[ i ] = NULL;
1341 return VLC_EGENERIC;
1343 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1344 if( i_error )
1346 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned %d", i_error );
1347 p_line->pp_glyphs[ i ] = NULL;
1348 return VLC_EGENERIC;
1350 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1351 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1352 if( i_error )
1354 FT_Done_Glyph( tmp_glyph );
1355 continue;
1357 if( b_uline )
1359 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position, p_face->size->metrics.y_scale));
1360 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness, p_face->size->metrics.y_scale));
1362 p_line->pi_underline_offset[ i ] = ( aOffset < 0 ) ? -aOffset : aOffset;
1363 p_line->pi_underline_thickness[ i ] = ( aSize < 0 ) ? -aSize : aSize;
1365 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1366 p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1368 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1369 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1370 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1372 while( --i > *pi_start )
1374 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1377 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1379 psz_unicode--;
1381 if( psz_unicode == psz_unicode_start )
1383 msg_Warn( p_filter, "unbreakable string" );
1384 p_line->pp_glyphs[ i ] = NULL;
1385 return VLC_EGENERIC;
1387 else
1389 *psz_unicode = 0xffff;
1391 psz_unicode = psz_unicode_start;
1392 *pi_pen_x = i_pen_x_start;
1393 i_previous = 0;
1395 line.yMax = i_yMax;
1396 line.yMin = i_yMin;
1398 continue;
1400 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1401 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1403 i_previous = i_glyph_index;
1404 *pi_pen_x += p_face->glyph->advance.x >> 6;
1405 i++;
1407 p_line->i_width = line.xMax;
1408 p_line->i_height = __MAX( p_line->i_height, p_face->size->metrics.height >> 6 );
1409 p_line->pp_glyphs[ i ] = NULL;
1411 p_result->x = __MAX( p_result->x, line.xMax );
1412 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height, line.yMax - line.yMin ) );
1414 *pi_start = i;
1416 /* Get rid of any text processed - if necessary repositioning
1417 * at the start of a new line of text
1419 if( !*psz_unicode )
1421 *psz_unicode_start = '\0';
1423 else
1425 psz_unicode++;
1426 for( i=0; psz_unicode[ i ]; i++ )
1427 psz_unicode_start[ i ] = psz_unicode[ i ];
1428 psz_unicode_start[ i ] = '\0';
1431 return VLC_SUCCESS;
1434 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, char *psz_html, text_style_t *p_font_style, line_desc_t **p_lines, FT_Vector *p_result )
1436 filter_sys_t *p_sys = p_filter->p_sys;
1438 FT_Vector tmp_result;
1440 font_stack_t *p_fonts = NULL;
1441 vlc_bool_t b_italic = VLC_FALSE;
1442 vlc_bool_t b_bold = VLC_FALSE;
1443 vlc_bool_t b_uline = VLC_FALSE;
1445 line_desc_t *p_line = NULL;
1446 line_desc_t *p_prev = NULL;
1448 char *psz_node = NULL;
1450 int i_pen_x = 0;
1451 int i_pen_y = 0;
1452 int i_posn = 0;
1454 int rv = VLC_SUCCESS;
1456 p_result->x = p_result->y = 0;
1457 tmp_result.x = tmp_result.y = 0;
1459 if( p_font_style )
1461 PushFont( &p_fonts,
1462 p_font_style->psz_fontname,
1463 p_font_style->i_font_size,
1464 p_font_style->i_font_color,
1465 p_font_style->i_font_alpha );
1467 if( p_font_style->i_style_flags & STYLE_BOLD )
1468 b_bold = VLC_TRUE;
1469 if( p_font_style->i_style_flags & STYLE_ITALIC )
1470 b_italic = VLC_TRUE;
1471 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1472 b_uline = VLC_TRUE;
1474 else
1476 PushFont( &p_fonts, FC_DEFAULT_FONT, 24, 0xffffff, 0 );
1479 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) && ( rv == VLC_SUCCESS ) )
1481 switch ( xml_ReaderNodeType( p_xml_reader ) )
1483 case XML_READER_NONE:
1484 break;
1485 case XML_READER_ENDELEM:
1486 psz_node = xml_ReaderName( p_xml_reader );
1488 if( psz_node )
1490 if( !strcasecmp( "font", psz_node ) )
1491 PopFont( &p_fonts );
1492 else if( !strcasecmp( "b", psz_node ) )
1493 b_bold = VLC_FALSE;
1494 else if( !strcasecmp( "i", psz_node ) )
1495 b_italic = VLC_FALSE;
1496 else if( !strcasecmp( "u", psz_node ) )
1497 b_uline = VLC_FALSE;
1499 free( psz_node );
1501 break;
1502 case XML_READER_STARTELEM:
1503 psz_node = xml_ReaderName( p_xml_reader );
1504 if( psz_node )
1506 if( !strcasecmp( "font", psz_node ) )
1508 char *psz_fontname = NULL;
1509 int i_font_color = 0xffffff;
1510 int i_font_alpha = 0;
1511 int i_font_size = 24;
1513 /* Default all attributes to the top font in the stack -- in case not
1514 * all attributes are specified in the sub-font
1516 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ))
1518 psz_fontname = strdup( psz_fontname );
1521 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1523 char *psz_name = xml_ReaderName ( p_xml_reader );
1524 char *psz_value = xml_ReaderValue ( p_xml_reader );
1526 if( psz_name && psz_value )
1528 if( !strcasecmp( "face", psz_name ) )
1530 if( psz_fontname ) free( psz_fontname );
1531 psz_fontname = strdup( psz_value );
1533 else if( !strcasecmp( "size", psz_name ) )
1535 i_font_size = atoi( psz_value );
1537 else if( !strcasecmp( "color", psz_name ) &&
1538 ( psz_value[0] == '#' ) )
1540 i_font_color = strtol( psz_value+1, NULL, 16 );
1541 i_font_color &= 0x00ffffff;
1543 else if( !strcasecmp( "alpha", psz_name ) &&
1544 ( psz_value[0] == '#' ) )
1546 i_font_alpha = strtol( psz_value+1, NULL, 16 );
1547 i_font_alpha &= 0xff;
1549 free( psz_name );
1550 free( psz_value );
1553 PushFont( &p_fonts, psz_fontname, i_font_size, i_font_color, i_font_alpha );
1554 free( psz_fontname );
1556 else if( !strcasecmp( "b", psz_node ) )
1558 b_bold = VLC_TRUE;
1560 else if( !strcasecmp( "i", psz_node ) )
1562 b_italic = VLC_TRUE;
1564 else if( !strcasecmp( "u", psz_node ) )
1566 b_uline = VLC_TRUE;
1568 else if( !strcasecmp( "br", psz_node ) )
1570 if( p_line )
1572 p_prev = p_line;
1573 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1575 msg_Err( p_filter, "out of memory" );
1576 free( psz_node );
1577 rv = VLC_EGENERIC;
1578 break;
1580 p_line->b_new_color_mode = VLC_TRUE;
1581 p_result->x = __MAX( p_result->x, tmp_result.x );
1582 p_result->y += tmp_result.y;
1584 p_line->p_next = NULL;
1585 i_pen_x = 0;
1586 i_pen_y += tmp_result.y;
1587 i_posn = 0;
1588 p_prev->p_next = p_line;
1589 tmp_result.x = 0;
1590 tmp_result.y = 0;
1593 free( psz_node );
1595 break;
1596 case XML_READER_TEXT:
1597 psz_node = xml_ReaderValue( p_xml_reader );
1598 if( psz_node )
1600 char *psz_fontname = NULL;
1601 int i_font_color = 0xffffff;
1602 int i_font_alpha = 0;
1603 int i_font_size = 24;
1604 FT_Face p_face = NULL;
1606 if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
1608 int i_idx = 0;
1609 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig, psz_fontname, b_bold, b_italic, &i_idx );
1611 if( psz_fontfile )
1613 if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1614 i_idx, &p_face ) )
1616 free( psz_fontfile );
1617 free( psz_node );
1618 rv = VLC_EGENERIC;
1619 break;
1621 free( psz_fontfile );
1625 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
1626 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, i_font_size ) )
1628 free( psz_node );
1629 rv = VLC_EGENERIC;
1630 break;
1632 p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1634 uint32_t *psz_unicode = IconvText( p_filter, psz_node );
1636 if( !psz_unicode )
1638 free( psz_node );
1639 if( p_face ) FT_Done_Face( p_face );
1640 rv = VLC_EGENERIC;
1641 break;
1644 while( *psz_unicode )
1646 if( !p_line )
1648 if( !(p_line = NewLine( (byte_t *)psz_html )) )
1650 msg_Err( p_filter, "out of memory" );
1651 free( psz_node );
1652 if( p_face ) FT_Done_Face( p_face );
1653 rv = VLC_EGENERIC;
1654 break;
1656 /* New Color mode only works in YUVA rendering mode --
1657 * (RGB mode has palette constraints on it). We therefore
1658 * need to populate the legacy colour fields also.
1660 p_line->b_new_color_mode = VLC_TRUE;
1661 p_line->i_alpha = i_font_alpha;
1662 p_line->i_red = ( i_font_color & 0xff0000 ) >> 16;
1663 p_line->i_green = ( i_font_color & 0x00ff00 ) >> 8;
1664 p_line->i_blue = ( i_font_color & 0x0000ff );
1665 p_line->p_next = NULL;
1666 i_pen_x = 0;
1667 i_pen_y += tmp_result.y;
1668 tmp_result.x = 0;
1669 tmp_result.y = 0;
1670 i_posn = 0;
1671 if( p_prev ) p_prev->p_next = p_line;
1672 else *p_lines = p_line;
1675 if( RenderTag( p_filter, p_face, i_font_color, b_uline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
1677 free( psz_node );
1678 if( p_face ) FT_Done_Face( p_face );
1679 rv = VLC_EGENERIC;
1680 break;
1682 if( *psz_unicode )
1684 p_result->x = __MAX( p_result->x, tmp_result.x );
1685 p_result->y += tmp_result.y;
1687 p_prev = p_line;
1688 p_line = NULL;
1691 if( rv != VLC_SUCCESS ) break;
1693 if( p_face ) FT_Done_Face( p_face );
1694 free( psz_unicode );
1695 free( psz_node );
1697 break;
1700 if( p_line )
1702 p_result->x = __MAX( p_result->x, tmp_result.x );
1703 p_result->y += tmp_result.y;
1707 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1709 return rv;
1713 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1714 subpicture_region_t *p_region_in )
1716 int rv = VLC_SUCCESS;
1717 stream_t *p_sub = NULL;
1718 xml_t *p_xml = NULL;
1719 xml_reader_t *p_xml_reader = NULL;
1721 if( !p_region_in || !p_region_in->psz_html )
1722 return VLC_EGENERIC;
1724 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1725 p_region_in->psz_html,
1726 strlen( p_region_in->psz_html ),
1727 VLC_TRUE );
1728 if( p_sub )
1730 p_xml = xml_Create( p_filter );
1731 if( p_xml )
1733 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
1734 if( p_xml_reader )
1736 FT_Vector result;
1737 line_desc_t *p_lines = NULL;
1739 rv = ProcessNodes( p_filter, p_xml_reader, p_region_in->psz_html, p_region_in->p_style, &p_lines, &result );
1741 if( rv == VLC_SUCCESS )
1743 p_region_out->i_x = p_region_in->i_x;
1744 p_region_out->i_y = p_region_in->i_y;
1746 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1747 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1748 else
1749 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1751 FreeLines( p_lines );
1753 xml_ReaderDelete( p_xml, p_xml_reader );
1755 xml_Delete( p_xml );
1757 stream_Delete( p_sub );
1759 /* No longer need a HTML version of the text */
1760 free( p_region_in->psz_html );
1761 p_region_in->psz_html = NULL;
1763 return rv;
1766 static char* FontConfig_Select( FcConfig* priv, const char* family, vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
1768 FcResult result;
1769 FcPattern *pat, *p_pat;
1770 FcChar8* val_s;
1771 FcBool val_b;
1773 pat = FcPatternCreate();
1774 if (!pat) return NULL;
1776 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
1777 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
1778 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
1779 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
1781 FcDefaultSubstitute( pat );
1783 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
1785 FcPatternDestroy( pat );
1786 return NULL;
1789 p_pat = FcFontMatch( priv, pat, &result );
1790 FcPatternDestroy( pat );
1791 if( !p_pat ) return NULL;
1793 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) ) ||
1794 ( val_b != FcTrue ) )
1796 FcPatternDestroy( p_pat );
1797 return NULL;
1799 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
1801 *i_idx = 0;
1804 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
1806 FcPatternDestroy( p_pat );
1807 return NULL;
1811 if( strcasecmp((const char*)val_s, family ) != 0 )
1812 msg_Warn( p_filter, "fontconfig: selected font family is not the requested one: '%s' != '%s'\n",
1813 (const char*)val_s, family );
1816 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
1818 FcPatternDestroy( p_pat );
1819 return NULL;
1822 FcPatternDestroy( p_pat );
1823 return strdup( (const char*)val_s );
1825 #endif
1827 static void FreeLine( line_desc_t *p_line )
1829 unsigned int i;
1830 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
1832 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1834 free( p_line->pp_glyphs );
1835 free( p_line->p_glyph_pos );
1836 free( p_line->p_rgb );
1837 free( p_line->pi_underline_offset );
1838 free( p_line->pi_underline_thickness );
1839 free( p_line );
1842 static void FreeLines( line_desc_t *p_lines )
1844 line_desc_t *p_line, *p_next;
1846 if( !p_lines ) return;
1848 for( p_line = p_lines; p_line != NULL; p_line = p_next )
1850 p_next = p_line->p_next;
1851 FreeLine( p_line );
1855 static line_desc_t *NewLine( byte_t *psz_string )
1857 int i_count;
1858 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1860 if( !p_line ) return NULL;
1861 p_line->i_height = 0;
1862 p_line->i_width = 0;
1863 p_line->p_next = NULL;
1865 /* We don't use CountUtf8Characters() here because we are not acutally
1866 * sure the string is utf8. Better be safe than sorry. */
1867 i_count = strlen( (char *)psz_string );
1869 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1870 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1871 p_line->p_rgb = malloc( sizeof( uint32_t ) * i_count + 1 );
1872 p_line->pi_underline_offset = calloc( i_count+ + 1, sizeof( uint16_t ) );
1873 p_line->pi_underline_thickness = calloc( i_count+ + 1, sizeof( uint16_t ) );
1874 if( ( p_line->pp_glyphs == NULL ) ||
1875 ( p_line->p_glyph_pos == NULL ) ||
1876 ( p_line->p_rgb == NULL ) ||
1877 ( p_line->pi_underline_offset == NULL ) ||
1878 ( p_line->pi_underline_thickness == NULL ) )
1880 if( p_line->pi_underline_thickness ) free( p_line->pi_underline_thickness );
1881 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
1882 if( p_line->p_rgb ) free( p_line->p_rgb );
1883 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
1884 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
1885 free( p_line );
1886 return NULL;
1888 p_line->pp_glyphs[0] = NULL;
1889 p_line->b_new_color_mode = VLC_FALSE;
1891 return p_line;
1894 static int SetFontSize( filter_t *p_filter, int i_size )
1896 filter_sys_t *p_sys = p_filter->p_sys;
1898 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1900 if( !i_size )
1902 vlc_value_t val;
1904 if( !p_sys->i_default_font_size &&
1905 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1906 return VLC_SUCCESS;
1908 if( p_sys->i_default_font_size )
1910 i_size = p_sys->i_default_font_size;
1912 else
1914 var_Get( p_filter, "freetype-rel-fontsize", &val );
1915 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1916 p_filter->p_sys->i_display_height =
1917 p_filter->fmt_out.video.i_height;
1919 if( i_size <= 0 )
1921 msg_Warn( p_filter, "invalid fontsize, using 12" );
1922 i_size = 12;
1925 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1928 p_sys->i_font_size = i_size;
1930 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1932 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1933 return VLC_EGENERIC;
1936 return VLC_SUCCESS;