1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2007 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Bernie Purcell <b dot purcell at adbglobal dot com>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
32 #ifdef HAVE_LINUX_LIMITS_H
33 # include <linux/limits.h>
39 #include <vlc_block.h>
40 #include <vlc_filter.h>
41 #include <vlc_stream.h>
51 #include FT_FREETYPE_H
53 #define FT_FLOOR(X) ((X & -64) >> 6)
54 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
55 #define FT_MulFix(v, s) (((v)*(s))>>16)
58 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
59 #define FC_DEFAULT_FONT "Lucida Grande"
60 #elif defined( SYS_BEOS )
61 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
62 #define FC_DEFAULT_FONT "Swiss"
63 #elif defined( WIN32 )
64 #define DEFAULT_FONT "" /* Default font found at run-time */
65 #define FC_DEFAULT_FONT "Arial"
67 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
68 #define FC_DEFAULT_FONT "Serif Bold"
71 #if defined(HAVE_FRIBIDI)
72 #include <fribidi/fribidi.h>
75 #ifdef HAVE_FONTCONFIG
76 #include <fontconfig/fontconfig.h>
79 typedef struct line_desc_t line_desc_t;
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Create ( vlc_object_t * );
85 static void Destroy( vlc_object_t * );
87 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
88 static int RenderText( filter_t *, subpicture_region_t *,
89 subpicture_region_t * );
90 #ifdef HAVE_FONTCONFIG
91 static int RenderHtml( filter_t *, subpicture_region_t *,
92 subpicture_region_t * );
93 static char *FontConfig_Select( FcConfig *, const char *,
94 vlc_bool_t, vlc_bool_t, int * );
96 static line_desc_t *NewLine( int );
98 static int SetFontSize( filter_t *, int );
100 /*****************************************************************************
102 *****************************************************************************/
103 #define FONT_TEXT N_("Font")
104 #define FONT_LONGTEXT N_("Filename for the font you want to use")
105 #define FONTSIZE_TEXT N_("Font size in pixels")
106 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
107 "that will be rendered on the video. " \
108 "If set to something different than 0 this option will override the " \
109 "relative font size." )
110 #define OPACITY_TEXT N_("Opacity")
111 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
112 "text that will be rendered on the video. 0 = transparent, " \
113 "255 = totally opaque. " )
114 #define COLOR_TEXT N_("Text default color")
115 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
116 "the video. This must be an hexadecimal (like HTML colors). The first two "\
117 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
118 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
119 #define FONTSIZER_TEXT N_("Relative font size")
120 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
121 "fonts that will be rendered on the video. If absolute font size is set, "\
122 "relative size will be overriden." )
124 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
125 static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
126 N_("Large"), N_("Larger") };
127 #define YUVP_TEXT N_("Use YUVP renderer")
128 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
129 "This option is only needed if you want to encode into DVB subtitles" )
130 #define EFFECT_TEXT N_("Font Effect")
131 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
132 "text to improve its readability." )
134 #define EFFECT_BACKGROUND 1
135 #define EFFECT_OUTLINE 2
136 #define EFFECT_OUTLINE_FAT 3
138 static int pi_effects[] = { 1, 2, 3 };
139 static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
141 static int pi_color_values[] = {
142 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
146 static const char *ppsz_color_descriptions[] = {
147 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
152 set_shortname( _("Text renderer"));
153 set_description( _("Freetype2 font renderer") );
154 set_category( CAT_VIDEO );
155 set_subcategory( SUBCAT_VIDEO_SUBPIC );
157 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
160 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
161 FONTSIZE_LONGTEXT, VLC_TRUE );
163 /* opacity valid on 0..255, with default 255 = fully opaque */
164 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
165 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
167 /* hook to the color values list, with default 0x00ffffff = white */
168 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
169 COLOR_LONGTEXT, VLC_FALSE );
170 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
172 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
173 FONTSIZER_LONGTEXT, VLC_FALSE );
174 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
175 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
176 EFFECT_LONGTEXT, VLC_FALSE );
177 change_integer_list( pi_effects, ppsz_effects_text, 0 );
179 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
180 YUVP_LONGTEXT, VLC_TRUE );
181 set_capability( "text renderer", 100 );
182 add_shortcut( "text" );
183 set_callbacks( Create, Destroy );
188 /** NULL-terminated list of glyphs making the string */
189 FT_BitmapGlyph *pp_glyphs;
190 /** list of relative positions for the glyphs */
191 FT_Vector *p_glyph_pos;
192 /** list of RGB information for styled text
193 * -- if the rendering mode supports it (RenderYUVA) and
194 * b_new_color_mode is set, then it becomes possible to
195 * have multicoloured text within the subtitles. */
197 vlc_bool_t b_new_color_mode;
198 /** underline information -- only supplied if text should be underlined */
199 uint16_t *pi_underline_offset;
200 uint16_t *pi_underline_thickness;
204 int i_red, i_green, i_blue;
210 typedef struct font_stack_t font_stack_t;
218 font_stack_t *p_next;
224 uint32_t i_font_color; /* ARGB */
227 vlc_bool_t b_underline;
231 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
232 static void FreeLines( line_desc_t * );
233 static void FreeLine( line_desc_t * );
235 /*****************************************************************************
236 * filter_sys_t: freetype local data
237 *****************************************************************************
238 * This structure is part of the video output thread descriptor.
239 * It describes the freetype specific properties of an output thread.
240 *****************************************************************************/
243 FT_Library p_library; /* handle to library */
244 FT_Face p_face; /* handle to face object */
245 vlc_bool_t i_use_kerning;
246 uint8_t i_font_opacity;
251 int i_default_font_size;
252 int i_display_height;
253 #ifdef HAVE_FONTCONFIG
254 FcConfig *p_fontconfig;
258 /*****************************************************************************
259 * Create: allocates osd-text video thread output method
260 *****************************************************************************
261 * This function allocates and initializes a Clone vout method.
262 *****************************************************************************/
263 static int Create( vlc_object_t *p_this )
265 filter_t *p_filter = (filter_t *)p_this;
267 char *psz_fontfile = NULL;
271 /* Allocate structure */
272 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
275 msg_Err( p_filter, "out of memory" );
279 p_sys->p_library = 0;
280 p_sys->i_font_size = 0;
281 p_sys->i_display_height = 0;
283 var_Create( p_filter, "freetype-font",
284 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
285 var_Create( p_filter, "freetype-fontsize",
286 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
287 var_Create( p_filter, "freetype-rel-fontsize",
288 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
289 var_Create( p_filter, "freetype-opacity",
290 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
291 var_Create( p_filter, "freetype-effect",
292 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
293 var_Get( p_filter, "freetype-opacity", &val );
294 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
295 var_Create( p_filter, "freetype-color",
296 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
297 var_Get( p_filter, "freetype-color", &val );
298 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
299 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
301 /* Look what method was requested */
302 var_Get( p_filter, "freetype-font", &val );
303 psz_fontfile = val.psz_string;
304 if( !psz_fontfile || !*psz_fontfile )
306 if( psz_fontfile ) free( psz_fontfile );
307 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
310 msg_Err( p_filter, "out of memory" );
314 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
315 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
316 #elif defined(__APPLE__)
317 strcpy( psz_fontfile, DEFAULT_FONT );
319 msg_Err( p_filter, "user didn't specify a font" );
324 #ifdef HAVE_FONTCONFIG
326 p_sys->p_fontconfig = FcConfigGetCurrent();
328 p_sys->p_fontconfig = NULL;
330 i_error = FT_Init_FreeType( &p_sys->p_library );
333 msg_Err( p_filter, "couldn't initialize freetype" );
336 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
338 if( i_error == FT_Err_Unknown_File_Format )
340 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
345 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
349 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
352 msg_Err( p_filter, "font has no unicode translation table" );
356 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
358 var_Get( p_filter, "freetype-fontsize", &val );
359 p_sys->i_default_font_size = val.i_int;
360 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
362 if( psz_fontfile ) free( psz_fontfile );
363 p_filter->pf_render_text = RenderText;
364 #ifdef HAVE_FONTCONFIG
365 p_filter->pf_render_html = RenderHtml;
367 p_filter->pf_render_html = NULL;
372 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
373 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
374 if( psz_fontfile ) free( psz_fontfile );
379 /*****************************************************************************
380 * Destroy: destroy Clone video thread output method
381 *****************************************************************************
382 * Clean up all data and library connections
383 *****************************************************************************/
384 static void Destroy( vlc_object_t *p_this )
386 filter_t *p_filter = (filter_t *)p_this;
387 filter_sys_t *p_sys = p_filter->p_sys;
389 #ifdef HAVE_FONTCONFIG
390 FcConfigDestroy( p_sys->p_fontconfig );
391 p_sys->p_fontconfig = NULL;
392 /* FcFini asserts calling the subfunction FcCacheFini()
393 * even if no other library functions have been made since FcInit(),
394 * so don't call it. */
396 FT_Done_Face( p_sys->p_face );
397 FT_Done_FreeType( p_sys->p_library );
401 /*****************************************************************************
402 * Render: place string in picture
403 *****************************************************************************
404 * This function merges the previously rendered freetype glyphs into a picture
405 *****************************************************************************/
406 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
407 line_desc_t *p_line, int i_width, int i_height )
409 static uint8_t pi_gamma[16] =
410 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
411 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
415 int i, x, y, i_pitch;
416 uint8_t i_y; /* YUV values, derived from incoming RGB */
418 subpicture_region_t *p_region_tmp;
420 /* Create a new subpicture region */
421 memset( &fmt, 0, sizeof(video_format_t) );
422 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
424 fmt.i_width = fmt.i_visible_width = i_width + 4;
425 fmt.i_height = fmt.i_visible_height = i_height + 4;
426 if( p_region->fmt.i_visible_width > 0 )
427 fmt.i_visible_width = p_region->fmt.i_visible_width;
428 if( p_region->fmt.i_visible_height > 0 )
429 fmt.i_visible_height = p_region->fmt.i_visible_height;
430 fmt.i_x_offset = fmt.i_y_offset = 0;
431 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
434 msg_Err( p_filter, "cannot allocate SPU region" );
438 p_region->fmt = p_region_tmp->fmt;
439 p_region->picture = p_region_tmp->picture;
440 free( p_region_tmp );
442 /* Calculate text color components */
443 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
444 25 * p_line->i_blue + 128) >> 8) + 16;
445 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
446 112 * p_line->i_blue + 128) >> 8) + 128;
447 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
448 18 * p_line->i_blue + 128) >> 8) + 128;
451 fmt.p_palette->i_entries = 16;
452 for( i = 0; i < 8; i++ )
454 fmt.p_palette->palette[i][0] = 0;
455 fmt.p_palette->palette[i][1] = 0x80;
456 fmt.p_palette->palette[i][2] = 0x80;
457 fmt.p_palette->palette[i][3] = pi_gamma[i];
458 fmt.p_palette->palette[i][3] =
459 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
461 for( i = 8; i < fmt.p_palette->i_entries; i++ )
463 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
464 fmt.p_palette->palette[i][1] = i_u;
465 fmt.p_palette->palette[i][2] = i_v;
466 fmt.p_palette->palette[i][3] = pi_gamma[i];
467 fmt.p_palette->palette[i][3] =
468 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
471 p_dst = p_region->picture.Y_PIXELS;
472 i_pitch = p_region->picture.Y_PITCH;
474 /* Initialize the region pixels */
475 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
477 for( ; p_line != NULL; p_line = p_line->p_next )
479 int i_glyph_tmax = 0;
480 int i_bitmap_offset, i_offset, i_align_offset = 0;
481 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
483 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
484 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
487 if( p_line->i_width < i_width )
489 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
491 i_align_offset = i_width - p_line->i_width;
493 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
495 i_align_offset = ( i_width - p_line->i_width ) / 2;
499 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
501 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
503 i_offset = ( p_line->p_glyph_pos[ i ].y +
504 i_glyph_tmax - p_glyph->top + 2 ) *
505 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
508 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
510 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
512 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
514 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
521 /* Outlining (find something better than nearest neighbour filtering ?) */
524 uint8_t *p_dst = p_region->picture.Y_PIXELS;
525 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
526 uint8_t left, current;
528 for( y = 1; y < (int)fmt.i_height - 1; y++ )
530 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
531 p_dst += p_region->picture.Y_PITCH;
534 for( x = 1; x < (int)fmt.i_width - 1; x++ )
537 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
538 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;
542 memset( p_top, 0, fmt.i_width );
548 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
549 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
550 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
551 int i_glyph_tmax, int i_align_offset,
552 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
553 subpicture_region_t *p_region)
557 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
559 p_dst_y = p_region->picture.Y_PIXELS;
560 p_dst_u = p_region->picture.U_PIXELS;
561 p_dst_v = p_region->picture.V_PIXELS;
562 p_dst_a = p_region->picture.A_PIXELS;
563 i_pitch = p_region->picture.A_PITCH;
565 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
566 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
568 for( y = 0; y < i_line_thickness; y++ )
570 int i_extra = p_this_glyph->bitmap.width;
574 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
575 (p_this_glyph_pos->x + p_this_glyph->left);
577 for( x = 0; x < i_extra; x++ )
579 vlc_bool_t b_ok = VLC_TRUE;
581 /* break the underline around the tails of any glyphs which cross it */
582 for( z = x - i_line_thickness;
583 z < x + i_line_thickness && b_ok;
586 if( p_next_glyph && ( z >= i_extra ) )
588 int i_row = i_line_offset + p_next_glyph->top + y;
590 if( ( p_next_glyph->bitmap.rows > i_row ) &&
591 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
596 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
598 int i_row = i_line_offset + p_this_glyph->top + y;
600 if( ( p_this_glyph->bitmap.rows > i_row ) &&
601 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
610 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
611 p_dst_u[i_offset+x] = i_u;
612 p_dst_v[i_offset+x] = i_v;
613 p_dst_a[i_offset+x] = 255;
620 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
622 uint8_t *p_dst = p_region->picture.A_PIXELS;
623 int i_pitch = p_region->picture.A_PITCH;
626 for( ; p_line != NULL; p_line = p_line->p_next )
628 int i_glyph_tmax=0, i = 0;
629 int i_bitmap_offset, i_offset, i_align_offset = 0;
630 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
632 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
633 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
636 if( p_line->i_width < i_width )
638 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
640 i_align_offset = i_width - p_line->i_width;
642 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
644 i_align_offset = ( i_width - p_line->i_width ) / 2;
648 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
650 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
652 i_offset = ( p_line->p_glyph_pos[ i ].y +
653 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
654 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
655 i_align_offset +xoffset;
657 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
659 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
661 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
662 if( p_dst[i_offset+x] <
663 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
665 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
674 /*****************************************************************************
675 * Render: place string in picture
676 *****************************************************************************
677 * This function merges the previously rendered freetype glyphs into a picture
678 *****************************************************************************/
679 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
680 line_desc_t *p_line, int i_width, int i_height )
682 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
684 int i, x, y, i_pitch, i_alpha;
685 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
686 subpicture_region_t *p_region_tmp;
688 if( i_width == 0 || i_height == 0 )
691 /* Create a new subpicture region */
692 memset( &fmt, 0, sizeof(video_format_t) );
693 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
695 fmt.i_width = fmt.i_visible_width = i_width + 6;
696 fmt.i_height = fmt.i_visible_height = i_height + 6;
697 if( p_region->fmt.i_visible_width > 0 )
698 fmt.i_visible_width = p_region->fmt.i_visible_width;
699 if( p_region->fmt.i_visible_height > 0 )
700 fmt.i_visible_height = p_region->fmt.i_visible_height;
701 fmt.i_x_offset = fmt.i_y_offset = 0;
702 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
705 msg_Err( p_filter, "cannot allocate SPU region" );
709 p_region->fmt = p_region_tmp->fmt;
710 p_region->picture = p_region_tmp->picture;
711 free( p_region_tmp );
713 /* Calculate text color components */
714 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
715 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
716 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
717 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
718 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
719 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
720 i_alpha = p_line->i_alpha;
722 p_dst_y = p_region->picture.Y_PIXELS;
723 p_dst_u = p_region->picture.U_PIXELS;
724 p_dst_v = p_region->picture.V_PIXELS;
725 p_dst_a = p_region->picture.A_PIXELS;
726 i_pitch = p_region->picture.A_PITCH;
728 /* Initialize the region pixels */
729 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
731 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
732 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
733 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
734 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
738 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
739 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
740 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
741 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
743 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
744 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
746 DrawBlack( p_line, i_width, p_region, 0, 0);
747 DrawBlack( p_line, i_width, p_region, -1, 0);
748 DrawBlack( p_line, i_width, p_region, 0, -1);
749 DrawBlack( p_line, i_width, p_region, 1, 0);
750 DrawBlack( p_line, i_width, p_region, 0, 1);
753 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
755 DrawBlack( p_line, i_width, p_region, -1, -1);
756 DrawBlack( p_line, i_width, p_region, -1, 1);
757 DrawBlack( p_line, i_width, p_region, 1, -1);
758 DrawBlack( p_line, i_width, p_region, 1, 1);
760 DrawBlack( p_line, i_width, p_region, -2, 0);
761 DrawBlack( p_line, i_width, p_region, 0, -2);
762 DrawBlack( p_line, i_width, p_region, 2, 0);
763 DrawBlack( p_line, i_width, p_region, 0, 2);
765 DrawBlack( p_line, i_width, p_region, -2, -2);
766 DrawBlack( p_line, i_width, p_region, -2, 2);
767 DrawBlack( p_line, i_width, p_region, 2, -2);
768 DrawBlack( p_line, i_width, p_region, 2, 2);
770 DrawBlack( p_line, i_width, p_region, -3, 0);
771 DrawBlack( p_line, i_width, p_region, 0, -3);
772 DrawBlack( p_line, i_width, p_region, 3, 0);
773 DrawBlack( p_line, i_width, p_region, 0, 3);
776 for( ; p_line != NULL; p_line = p_line->p_next )
778 int i_glyph_tmax = 0;
779 int i_bitmap_offset, i_offset, i_align_offset = 0;
780 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
782 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
783 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
786 if( p_line->i_width < i_width )
788 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
790 i_align_offset = i_width - p_line->i_width;
792 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
794 i_align_offset = ( i_width - p_line->i_width ) / 2;
798 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
800 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
802 i_offset = ( p_line->p_glyph_pos[ i ].y +
803 i_glyph_tmax - p_glyph->top + 3 ) *
804 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
807 if( p_line->b_new_color_mode )
809 /* Every glyph can (and in fact must) have its own color */
810 int i_red = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
811 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >> 8;
812 int i_blue = ( p_line->p_rgb[ i ] & 0x000000ff );
814 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
815 802 * i_blue + 4096 + 131072 ) >> 13, 235);
816 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
817 3598 * i_blue + 4096 + 1048576) >> 13, 240);
818 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
819 -585 * i_blue + 4096 + 1048576) >> 13, 240);
822 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
824 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
826 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
828 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
829 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
831 p_dst_u[i_offset+x] = i_u;
832 p_dst_v[i_offset+x] = i_v;
834 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
835 p_dst_a[i_offset+x] = 0xff;
841 if( p_line->pi_underline_thickness[ i ] )
843 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
844 p_line->pi_underline_offset[ i ],
845 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
846 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
847 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
848 i_glyph_tmax, i_align_offset,
849 i_y, i_u, i_v, i_alpha,
855 /* Apply the alpha setting */
856 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
857 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
863 * This function renders a text subpicture region into another one.
864 * It also calculates the size needed for this string, and renders the
865 * needed glyphs into memory. It is used as pf_add_string callback in
866 * the vout method by this module
868 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
869 subpicture_region_t *p_region_in )
871 filter_sys_t *p_sys = p_filter->p_sys;
872 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
873 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
874 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
877 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
878 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
886 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
887 psz_string = p_region_in->psz_text;
888 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
890 if( p_region_in->p_style )
892 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
893 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
894 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
898 i_font_color = p_sys->i_font_color;
899 i_font_alpha = 255 - p_sys->i_font_opacity;
900 i_font_size = p_sys->i_default_font_size;
903 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
904 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
905 SetFontSize( p_filter, i_font_size );
907 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
908 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
909 i_blue = i_font_color & 0x000000FF;
911 result.x = result.y = 0;
912 line.xMin = line.xMax = line.yMin = line.yMax = 0;
914 psz_unicode = psz_unicode_orig =
915 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
916 if( psz_unicode == NULL )
918 msg_Err( p_filter, "out of memory" );
921 #if defined(WORDS_BIGENDIAN)
922 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
924 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
926 if( iconv_handle == (vlc_iconv_t)-1 )
928 msg_Warn( p_filter, "unable to do conversion" );
934 const char *p_in_buffer = psz_string;
935 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
936 i_in_bytes = strlen( psz_string );
937 i_out_bytes = i_in_bytes * sizeof( uint32_t );
938 i_out_bytes_left = i_out_bytes;
939 p_out_buffer = (char *)psz_unicode;
940 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
941 &p_out_buffer, &i_out_bytes_left );
943 vlc_iconv_close( iconv_handle );
947 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
948 "bytes left %d", strerror(errno), (int)i_in_bytes );
951 *(uint32_t*)p_out_buffer = 0;
952 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
955 #if defined(HAVE_FRIBIDI)
957 uint32_t *p_fribidi_string;
958 int start_pos, pos = 0;
960 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
961 if( !p_fribidi_string )
963 msg_Err( p_filter, "out of memory" );
967 /* Do bidi conversion line-by-line */
968 while(pos < i_string_length)
970 while(pos < i_string_length) {
971 i_char = psz_unicode[pos];
972 if (i_char != '\r' && i_char != '\n')
974 p_fribidi_string[pos] = i_char;
978 while(pos < i_string_length) {
979 i_char = psz_unicode[pos];
980 if (i_char == '\r' || i_char == '\n')
986 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
987 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
988 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
992 free( psz_unicode_orig );
993 psz_unicode = psz_unicode_orig = p_fribidi_string;
994 p_fribidi_string[ i_string_length ] = 0;
998 /* Calculate relative glyph positions and a bounding box for the
1000 if( !(p_line = NewLine( strlen( psz_string ))) )
1002 msg_Err( p_filter, "out of memory" );
1006 i_pen_x = i_pen_y = 0;
1008 psz_line_start = psz_unicode;
1010 #define face p_sys->p_face
1011 #define glyph face->glyph
1013 while( *psz_unicode )
1015 i_char = *psz_unicode++;
1016 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1021 if( i_char == '\n' )
1023 psz_line_start = psz_unicode;
1024 if( !(p_next = NewLine( strlen( psz_string ))) )
1026 msg_Err( p_filter, "out of memory" );
1029 p_line->p_next = p_next;
1030 p_line->i_width = line.xMax;
1031 p_line->i_height = face->size->metrics.height >> 6;
1032 p_line->pp_glyphs[ i ] = NULL;
1033 p_line->i_alpha = i_font_alpha;
1034 p_line->i_red = i_red;
1035 p_line->i_green = i_green;
1036 p_line->i_blue = i_blue;
1039 result.x = __MAX( result.x, line.xMax );
1040 result.y += face->size->metrics.height >> 6;
1043 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1044 i_pen_y += face->size->metrics.height >> 6;
1046 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1051 i_glyph_index = FT_Get_Char_Index( face, i_char );
1052 if( p_sys->i_use_kerning && i_glyph_index
1056 FT_Get_Kerning( face, i_previous, i_glyph_index,
1057 ft_kerning_default, &delta );
1058 i_pen_x += delta.x >> 6;
1061 p_line->p_glyph_pos[ i ].x = i_pen_x;
1062 p_line->p_glyph_pos[ i ].y = i_pen_y;
1063 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1066 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1070 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1073 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1077 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1078 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1081 FT_Done_Glyph( tmp_glyph );
1084 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1087 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1088 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1089 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1091 p_line->pp_glyphs[ i ] = NULL;
1093 p_line = NewLine( strlen( psz_string ));
1094 if( p_prev ) p_prev->p_next = p_line;
1095 else p_lines = p_line;
1097 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1101 if( psz_unicode == psz_line_start )
1103 msg_Warn( p_filter, "unbreakable string" );
1108 *psz_unicode = '\n';
1110 psz_unicode = psz_line_start;
1113 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1116 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1117 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1119 i_previous = i_glyph_index;
1120 i_pen_x += glyph->advance.x >> 6;
1124 p_line->i_width = line.xMax;
1125 p_line->i_height = face->size->metrics.height >> 6;
1126 p_line->pp_glyphs[ i ] = NULL;
1127 p_line->i_alpha = i_font_alpha;
1128 p_line->i_red = i_red;
1129 p_line->i_green = i_green;
1130 p_line->i_blue = i_blue;
1131 result.x = __MAX( result.x, line.xMax );
1132 result.y += line.yMax - line.yMin;
1137 p_region_out->i_x = p_region_in->i_x;
1138 p_region_out->i_y = p_region_in->i_y;
1140 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1141 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1143 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1145 if( psz_unicode_orig ) free( psz_unicode_orig );
1146 FreeLines( p_lines );
1150 if( psz_unicode_orig ) free( psz_unicode_orig );
1151 FreeLines( p_lines );
1152 return VLC_EGENERIC;
1155 #ifdef HAVE_FONTCONFIG
1156 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1157 int i_font_color, int i_font_alpha, vlc_bool_t b_bold,
1158 vlc_bool_t b_italic, vlc_bool_t b_uline )
1160 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1164 p_style->i_font_size = i_font_size;
1165 p_style->i_font_color = ( i_font_color & 0x00ffffff )
1166 | (( i_font_alpha & 0xff ) << 24 );
1167 p_style->b_italic = b_italic;
1168 p_style->b_bold = b_bold;
1169 p_style->b_underline = b_uline;
1170 /* p_style has just been malloc'ed in this function -
1171 * it CAN'T have a previous assignment, and hence we
1172 * don't need to do a free() for any previous value -
1173 * which will in fact be undefined. */
1174 p_style->psz_fontname = strdup( psz_fontname );
1179 static void DeleteStyle( ft_style_t *p_style )
1183 if( p_style->psz_fontname )
1184 free( p_style->psz_fontname );
1189 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1196 if(( s1->i_font_size == s2->i_font_size ) &&
1197 ( s1->i_font_color == s2->i_font_color ) &&
1198 ( s1->b_italic == s2->b_italic ) &&
1199 ( s1->b_bold == s2->b_bold ) &&
1200 ( s1->b_underline == s2->b_underline ) &&
1201 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1208 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1209 int i_color, int i_alpha )
1211 font_stack_t *p_new;
1214 return VLC_EGENERIC;
1216 p_new = malloc( sizeof( font_stack_t ) );
1220 p_new->p_next = NULL;
1223 p_new->psz_name = strdup( psz_name );
1225 p_new->psz_name = NULL;
1227 p_new->i_size = i_size;
1228 p_new->i_color = i_color;
1229 p_new->i_alpha = i_alpha;
1237 font_stack_t *p_last;
1239 for( p_last = *p_font;
1241 p_last = p_last->p_next )
1244 p_last->p_next = p_new;
1249 static int PopFont( font_stack_t **p_font )
1251 font_stack_t *p_last, *p_next_to_last;
1253 if( !p_font || !*p_font )
1254 return VLC_EGENERIC;
1256 p_next_to_last = NULL;
1257 for( p_last = *p_font;
1259 p_last = p_last->p_next )
1261 p_next_to_last = p_last;
1264 if( p_next_to_last )
1265 p_next_to_last->p_next = NULL;
1269 free( p_last->psz_name );
1275 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1276 int *i_color, int *i_alpha )
1278 font_stack_t *p_last;
1280 if( !p_font || !*p_font )
1281 return VLC_EGENERIC;
1283 for( p_last=*p_font;
1285 p_last=p_last->p_next )
1288 *psz_name = p_last->psz_name;
1289 *i_size = p_last->i_size;
1290 *i_color = p_last->i_color;
1291 *i_alpha = p_last->i_alpha;
1296 static void IconvText( filter_t *p_filter, const char *psz_string,
1297 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1299 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1301 /* If memory hasn't been allocated for our output string, allocate it here
1302 * - the calling function must now be responsible for freeing it.
1304 if( !*ppsz_unicode )
1305 *ppsz_unicode = (uint32_t *)
1306 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1308 /* We don't need to handle a NULL pointer in *ppsz_unicode
1309 * if we are instead testing for a non NULL value like we are here */
1313 #if defined(WORDS_BIGENDIAN)
1314 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1316 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1318 if( iconv_handle != (vlc_iconv_t)-1 )
1320 char *p_in_buffer, *p_out_buffer;
1321 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1322 i_in_bytes = strlen( psz_string );
1323 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1324 i_out_bytes_left = i_out_bytes;
1325 p_in_buffer = (char *) psz_string;
1326 p_out_buffer = (char *) *ppsz_unicode;
1327 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1328 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1330 vlc_iconv_close( iconv_handle );
1334 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1335 "bytes left %d", strerror(errno), (int)i_in_bytes );
1339 *(uint32_t*)p_out_buffer = 0;
1341 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1346 msg_Warn( p_filter, "unable to do conversion" );
1351 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1352 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1353 vlc_bool_t b_uline )
1355 ft_style_t *p_style = NULL;
1357 char *psz_fontname = NULL;
1358 int i_font_color = p_sys->i_font_color;
1359 int i_font_alpha = 0;
1360 int i_font_size = p_sys->i_font_size;
1362 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1363 &i_font_color, &i_font_alpha ) )
1365 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1366 i_font_alpha, b_bold, b_italic, b_uline );
1371 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1372 vlc_bool_t b_uline, line_desc_t *p_line,
1373 uint32_t *psz_unicode, int *pi_pen_x, int i_pen_y,
1374 int *pi_start, FT_Vector *p_result )
1379 vlc_bool_t b_first_on_line = VLC_TRUE;
1382 int i_pen_x_start = *pi_pen_x;
1384 uint32_t *psz_unicode_start = psz_unicode;
1386 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1388 /* Account for part of line already in position */
1389 for( i=0; i<*pi_start; i++ )
1393 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1394 ft_glyph_bbox_pixels, &glyph_size );
1396 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1397 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1398 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1399 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1405 b_first_on_line = VLC_FALSE;
1407 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1413 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1414 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1418 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1419 ft_kerning_default, &delta );
1420 *pi_pen_x += delta.x >> 6;
1422 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1423 p_line->p_glyph_pos[ i ].y = i_pen_y;
1425 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1429 "unable to render text FT_Load_Glyph returned %d", i_error );
1430 p_line->pp_glyphs[ i ] = NULL;
1431 return VLC_EGENERIC;
1433 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1437 "unable to render text FT_Get_Glyph returned %d", i_error );
1438 p_line->pp_glyphs[ i ] = NULL;
1439 return VLC_EGENERIC;
1441 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1442 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1445 FT_Done_Glyph( tmp_glyph );
1450 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1451 p_face->size->metrics.y_scale));
1452 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1453 p_face->size->metrics.y_scale));
1455 p_line->pi_underline_offset[ i ] =
1456 ( aOffset < 0 ) ? -aOffset : aOffset;
1457 p_line->pi_underline_thickness[ i ] =
1458 ( aSize < 0 ) ? -aSize : aSize;
1460 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1461 p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1463 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1464 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1465 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1467 while( --i > *pi_start )
1469 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1472 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1476 if( psz_unicode == psz_unicode_start )
1478 if( b_first_on_line )
1480 msg_Warn( p_filter, "unbreakable string" );
1481 p_line->pp_glyphs[ i ] = NULL;
1482 return VLC_EGENERIC;
1484 *pi_pen_x = i_pen_x_start;
1486 p_line->i_width = line.xMax;
1487 p_line->i_height = __MAX( p_line->i_height,
1488 p_face->size->metrics.height >> 6 );
1489 p_line->pp_glyphs[ i ] = NULL;
1491 p_result->x = __MAX( p_result->x, line.xMax );
1492 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1493 i_yMax - i_yMin ) );
1500 *psz_unicode = '\n';
1502 psz_unicode = psz_unicode_start;
1503 *pi_pen_x = i_pen_x_start;
1511 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1512 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1514 i_previous = i_glyph_index;
1515 *pi_pen_x += p_face->glyph->advance.x >> 6;
1518 p_line->i_width = line.xMax;
1519 p_line->i_height = __MAX( p_line->i_height,
1520 p_face->size->metrics.height >> 6 );
1521 p_line->pp_glyphs[ i ] = NULL;
1523 p_result->x = __MAX( p_result->x, line.xMax );
1524 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1525 line.yMax - line.yMin ) );
1529 /* Get rid of any text processed - if necessary repositioning
1530 * at the start of a new line of text
1534 *psz_unicode_start = '\0';
1539 for( i=0; psz_unicode[ i ]; i++ )
1540 psz_unicode_start[ i ] = psz_unicode[ i ];
1541 psz_unicode_start[ i ] = '\0';
1547 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1548 font_stack_t **p_fonts )
1551 char *psz_fontname = NULL;
1552 int i_font_color = 0xffffff;
1553 int i_font_alpha = 0;
1554 int i_font_size = 24;
1556 /* Default all attributes to the top font in the stack -- in case not
1557 * all attributes are specified in the sub-font
1559 if( VLC_SUCCESS == PeekFont( p_fonts,
1565 psz_fontname = strdup( psz_fontname );
1568 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1570 char *psz_name = xml_ReaderName ( p_xml_reader );
1571 char *psz_value = xml_ReaderValue ( p_xml_reader );
1573 if( psz_name && psz_value )
1575 if( !strcasecmp( "face", psz_name ) )
1577 if( psz_fontname ) free( psz_fontname );
1578 psz_fontname = strdup( psz_value );
1580 else if( !strcasecmp( "size", psz_name ) )
1582 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1584 int i_value = atoi( psz_value );
1586 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1587 i_font_size += ( i_value * i_font_size ) / 10;
1588 else if( i_value < -5 )
1589 i_font_size = - i_value;
1590 else if( i_value > 5 )
1591 i_font_size = i_value;
1594 i_font_size = atoi( psz_value );
1596 else if( !strcasecmp( "color", psz_name ) &&
1597 ( psz_value[0] == '#' ) )
1599 i_font_color = strtol( psz_value + 1, NULL, 16 );
1600 i_font_color &= 0x00ffffff;
1602 else if( !strcasecmp( "alpha", psz_name ) &&
1603 ( psz_value[0] == '#' ) )
1605 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1606 i_font_alpha &= 0xff;
1612 rv = PushFont( p_fonts,
1618 free( psz_fontname );
1623 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1624 uint32_t **psz_text_out, uint32_t *pi_runs,
1625 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1626 ft_style_t *p_style )
1628 uint32_t i_string_length = 0;
1630 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1631 *psz_text_out += i_string_length;
1633 if( ppp_styles && ppi_run_lengths )
1639 *ppp_styles = (ft_style_t **)
1640 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1642 else if( *pi_runs == 1 )
1644 *ppp_styles = (ft_style_t **)
1645 malloc( *pi_runs * sizeof( ft_style_t * ) );
1648 /* We have just malloc'ed this memory successfully -
1649 * *pi_runs HAS to be within the memory area of *ppp_styles */
1652 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1656 if( *ppi_run_lengths )
1658 *ppi_run_lengths = (uint32_t *)
1659 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1661 else if( *pi_runs == 1 )
1663 *ppi_run_lengths = (uint32_t *)
1664 malloc( *pi_runs * sizeof( uint32_t ) );
1667 /* same remarks here */
1668 if( *ppi_run_lengths )
1670 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1673 /* If we couldn't use the p_style argument due to memory allocation
1674 * problems above, release it here.
1676 if( p_style ) DeleteStyle( p_style );
1679 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader,
1680 text_style_t *p_font_style, uint32_t *psz_text,
1681 int *pi_len, uint32_t *pi_runs,
1682 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles)
1684 int rv = VLC_SUCCESS;
1685 filter_sys_t *p_sys = p_filter->p_sys;
1686 uint32_t *psz_text_orig = psz_text;
1687 font_stack_t *p_fonts = NULL;
1689 char *psz_node = NULL;
1691 vlc_bool_t b_italic = VLC_FALSE;
1692 vlc_bool_t b_bold = VLC_FALSE;
1693 vlc_bool_t b_uline = VLC_FALSE;
1697 rv = PushFont( &p_fonts,
1698 p_font_style->psz_fontname,
1699 p_font_style->i_font_size,
1700 p_font_style->i_font_color,
1701 p_font_style->i_font_alpha );
1703 if( p_font_style->i_style_flags & STYLE_BOLD )
1705 if( p_font_style->i_style_flags & STYLE_ITALIC )
1706 b_italic = VLC_TRUE;
1707 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1712 rv = PushFont( &p_fonts,
1718 if( rv != VLC_SUCCESS )
1721 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1723 switch ( xml_ReaderNodeType( p_xml_reader ) )
1725 case XML_READER_NONE:
1727 case XML_READER_ENDELEM:
1728 psz_node = xml_ReaderName( p_xml_reader );
1732 if( !strcasecmp( "font", psz_node ) )
1733 PopFont( &p_fonts );
1734 else if( !strcasecmp( "b", psz_node ) )
1736 else if( !strcasecmp( "i", psz_node ) )
1737 b_italic = VLC_FALSE;
1738 else if( !strcasecmp( "u", psz_node ) )
1739 b_uline = VLC_FALSE;
1744 case XML_READER_STARTELEM:
1745 psz_node = xml_ReaderName( p_xml_reader );
1748 if( !strcasecmp( "font", psz_node ) )
1749 rv = HandleFontAttributes( p_xml_reader, &p_fonts );
1750 else if( !strcasecmp( "b", psz_node ) )
1752 else if( !strcasecmp( "i", psz_node ) )
1753 b_italic = VLC_TRUE;
1754 else if( !strcasecmp( "u", psz_node ) )
1756 else if( !strcasecmp( "br", psz_node ) )
1758 SetupLine( p_filter, "\n", &psz_text,
1759 pi_runs, ppi_run_lengths, ppp_styles,
1760 GetStyleFromFontStack( p_sys,
1770 case XML_READER_TEXT:
1771 psz_node = xml_ReaderValue( p_xml_reader );
1774 /* Turn any multiple-whitespaces into single spaces */
1775 char *s = strpbrk( psz_node, "\t\r\n " );
1778 int i_whitespace = strspn( s, "\t\r\n " );
1780 if( i_whitespace > 1 )
1783 strlen( s ) - i_whitespace + 1 );
1786 s = strpbrk( s, "\t\r\n " );
1788 SetupLine( p_filter, psz_node, &psz_text,
1789 pi_runs, ppi_run_lengths, ppp_styles,
1790 GetStyleFromFontStack( p_sys,
1799 if( rv != VLC_SUCCESS )
1801 psz_text = psz_text_orig;
1806 *pi_len = psz_text - psz_text_orig;
1808 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1813 static int ProcessLines( filter_t *p_filter, uint32_t *psz_text,
1814 int i_len, uint32_t i_runs,
1815 uint32_t *pi_run_lengths, ft_style_t **pp_styles,
1816 line_desc_t **pp_lines, FT_Vector *p_result )
1818 filter_sys_t *p_sys = p_filter->p_sys;
1819 ft_style_t **pp_char_styles;
1823 /* Assign each character in the text string its style explicitly, so that
1824 * after the characters have been shuffled around by Fribidi, we can re-apply
1825 * the styles, and to simplify the calculation of runs within a line.
1827 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1828 if( !pp_char_styles )
1832 for( j = 0; j < i_runs; j++ )
1833 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1834 pp_char_styles[ i++ ] = pp_styles[ j ];
1836 #if defined(HAVE_FRIBIDI)
1838 ft_style_t **pp_char_styles_new;
1840 uint32_t *p_fribidi_string;
1841 int start_pos, pos = 0;
1843 p_fribidi_string = malloc( (i_len + 1) * sizeof(uint32_t) );
1844 if(! p_fribidi_string )
1846 msg_Err( p_filter, "out of memory" );
1847 free( pp_char_styles );
1850 pp_char_styles_new = (ft_style_t **)
1851 malloc( i_len * sizeof( ft_style_t * ));
1852 if(! pp_char_styles_new )
1854 msg_Err( p_filter, "out of memory" );
1855 free( p_fribidi_string );
1856 free( pp_char_styles );
1859 p_positions = (int *) malloc( (i_len + 1) * sizeof( int ) );
1862 msg_Err( p_filter, "out of memory" );
1863 free( pp_char_styles_new );
1864 free( p_fribidi_string );
1865 free( pp_char_styles );
1869 /* Do bidi conversion line-by-line */
1872 while(pos < i_len) {
1873 if (psz_text[pos] != '\n')
1875 p_fribidi_string[pos] = psz_text[pos];
1876 pp_char_styles_new[pos] = pp_char_styles[pos];
1880 while(pos < i_len) {
1881 if (psz_text[pos] == '\n')
1885 if (pos > start_pos)
1887 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1888 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1889 pos - start_pos, &base_dir,
1890 (FriBidiChar*)p_fribidi_string + start_pos,
1894 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1896 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1897 p_positions[ j - start_pos ] ];
1901 free( p_positions );
1902 free( pp_char_styles );
1903 pp_char_styles = pp_char_styles_new;
1904 psz_text = p_fribidi_string;
1905 p_fribidi_string[ i_len ] = 0;
1908 FT_Vector tmp_result;
1910 line_desc_t *p_line = NULL;
1911 line_desc_t *p_prev = NULL;
1917 p_result->x = p_result->y = 0;
1918 tmp_result.x = tmp_result.y = 0;
1921 for( k = 0; k <= (uint32_t) i_len; k++ )
1923 if( ( k == (uint32_t) i_len ) ||
1925 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1927 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1929 /* End of the current style run */
1930 FT_Face p_face = NULL;
1932 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
1933 p_style->psz_fontname,
1939 if( FT_New_Face( p_sys->p_library,
1940 psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
1942 free( psz_fontfile );
1943 free( pp_char_styles );
1944 #if defined(HAVE_FRIBIDI)
1947 return VLC_EGENERIC;
1949 free( psz_fontfile );
1952 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1953 ft_encoding_unicode ) ||
1954 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1955 p_style->i_font_size ) )
1957 if( p_face ) FT_Done_Face( p_face );
1958 free( pp_char_styles );
1959 #if defined(HAVE_FRIBIDI)
1962 return VLC_EGENERIC;
1964 p_sys->i_use_kerning =
1965 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1968 uint32_t *psz_unicode = (uint32_t *)
1969 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1972 msg_Err( p_filter, "out of memory" );
1973 if( p_face ) FT_Done_Face( p_face );
1974 free( pp_char_styles );
1975 free( psz_unicode );
1976 #if defined(HAVE_FRIBIDI)
1981 memcpy( psz_unicode, psz_text + i_prev,
1982 sizeof( uint32_t ) * ( k - i_prev ) );
1983 psz_unicode[ k - i_prev ] = 0;
1984 while( *psz_unicode )
1988 if( !(p_line = NewLine( i_len - i_prev)) )
1990 msg_Err( p_filter, "out of memory" );
1991 if( p_face ) FT_Done_Face( p_face );
1992 free( pp_char_styles );
1993 free( psz_unicode );
1994 #if defined(HAVE_FRIBIDI)
1999 /* New Color mode only works in YUVA rendering mode --
2000 * (RGB mode has palette constraints on it). We therefore
2001 * need to populate the legacy colour fields also.
2003 p_line->b_new_color_mode = VLC_TRUE;
2004 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2005 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2006 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2007 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2008 p_line->p_next = NULL;
2010 i_pen_y += tmp_result.y;
2014 if( p_prev ) p_prev->p_next = p_line;
2015 else *pp_lines = p_line;
2017 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2018 p_style->i_font_color, p_style->b_underline,
2019 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2020 &tmp_result ) != VLC_SUCCESS )
2022 if( p_face ) FT_Done_Face( p_face );
2023 free( pp_char_styles );
2024 free( psz_unicode );
2025 #if defined(HAVE_FRIBIDI)
2028 return VLC_EGENERIC;
2032 p_result->x = __MAX( p_result->x, tmp_result.x );
2033 p_result->y += tmp_result.y;
2039 free( psz_unicode );
2040 if( p_face ) FT_Done_Face( p_face );
2044 free( pp_char_styles );
2045 #if defined(HAVE_FRIBIDI)
2051 p_result->x = __MAX( p_result->x, tmp_result.x );
2052 p_result->y += tmp_result.y;
2057 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2058 subpicture_region_t *p_region_in )
2060 int rv = VLC_SUCCESS;
2061 stream_t *p_sub = NULL;
2062 xml_t *p_xml = NULL;
2063 xml_reader_t *p_xml_reader = NULL;
2065 if( !p_region_in || !p_region_in->psz_html )
2066 return VLC_EGENERIC;
2068 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2069 (uint8_t *) p_region_in->psz_html,
2070 strlen( p_region_in->psz_html ),
2074 p_xml = xml_Create( p_filter );
2077 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2082 uint32_t i_runs = 0;
2083 uint32_t *pi_run_lengths = NULL;
2084 ft_style_t **pp_styles = NULL;
2086 line_desc_t *p_lines = NULL;
2088 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2089 sizeof( uint32_t ) );
2094 rv = ProcessNodes( p_filter, p_xml_reader,
2095 p_region_in->p_style, psz_text, &i_len,
2096 &i_runs, &pi_run_lengths, &pp_styles );
2098 p_region_out->i_x = p_region_in->i_x;
2099 p_region_out->i_y = p_region_in->i_y;
2101 if( rv == VLC_SUCCESS )
2103 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2104 pi_run_lengths, pp_styles, &p_lines, &result );
2107 for( k=0; k<i_runs; k++)
2108 DeleteStyle( pp_styles[k] );
2110 free( pi_run_lengths );
2113 /* Don't attempt to render text that couldn't be layed out
2116 if( rv == VLC_SUCCESS )
2118 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2120 Render( p_filter, p_region_out, p_lines,
2121 result.x, result.y );
2125 RenderYUVA( p_filter, p_region_out, p_lines,
2126 result.x, result.y );
2130 FreeLines( p_lines );
2132 xml_ReaderDelete( p_xml, p_xml_reader );
2134 xml_Delete( p_xml );
2136 stream_Delete( p_sub );
2142 static char* FontConfig_Select( FcConfig* priv, const char* family,
2143 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2146 FcPattern *pat, *p_pat;
2150 pat = FcPatternCreate();
2151 if (!pat) return NULL;
2153 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2154 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2155 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2156 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2158 FcDefaultSubstitute( pat );
2160 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2162 FcPatternDestroy( pat );
2166 p_pat = FcFontMatch( priv, pat, &result );
2167 FcPatternDestroy( pat );
2168 if( !p_pat ) return NULL;
2170 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2171 || ( val_b != FcTrue ) )
2173 FcPatternDestroy( p_pat );
2176 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2181 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2183 FcPatternDestroy( p_pat );
2188 if( strcasecmp((const char*)val_s, family ) != 0 )
2189 msg_Warn( p_filter, "fontconfig: selected font family is not"
2190 "the requested one: '%s' != '%s'\n",
2191 (const char*)val_s, family );
2194 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2196 FcPatternDestroy( p_pat );
2200 FcPatternDestroy( p_pat );
2201 return strdup( (const char*)val_s );
2205 static void FreeLine( line_desc_t *p_line )
2208 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2210 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2212 free( p_line->pp_glyphs );
2213 free( p_line->p_glyph_pos );
2214 free( p_line->p_rgb );
2215 free( p_line->pi_underline_offset );
2216 free( p_line->pi_underline_thickness );
2220 static void FreeLines( line_desc_t *p_lines )
2222 line_desc_t *p_line, *p_next;
2224 if( !p_lines ) return;
2226 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2228 p_next = p_line->p_next;
2233 static line_desc_t *NewLine( int i_count )
2235 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2237 if( !p_line ) return NULL;
2238 p_line->i_height = 0;
2239 p_line->i_width = 0;
2240 p_line->p_next = NULL;
2242 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2243 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2244 p_line->p_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2245 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2246 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2247 if( ( p_line->pp_glyphs == NULL ) ||
2248 ( p_line->p_glyph_pos == NULL ) ||
2249 ( p_line->p_rgb == NULL ) ||
2250 ( p_line->pi_underline_offset == NULL ) ||
2251 ( p_line->pi_underline_thickness == NULL ) )
2253 if( p_line->pi_underline_thickness )
2254 free( p_line->pi_underline_thickness );
2255 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2256 if( p_line->p_rgb ) free( p_line->p_rgb );
2257 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2258 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2262 p_line->pp_glyphs[0] = NULL;
2263 p_line->b_new_color_mode = VLC_FALSE;
2268 static int SetFontSize( filter_t *p_filter, int i_size )
2270 filter_sys_t *p_sys = p_filter->p_sys;
2272 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
2278 if( !p_sys->i_default_font_size &&
2279 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
2282 if( p_sys->i_default_font_size )
2284 i_size = p_sys->i_default_font_size;
2288 var_Get( p_filter, "freetype-rel-fontsize", &val );
2289 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2290 p_filter->p_sys->i_display_height =
2291 p_filter->fmt_out.video.i_height;
2295 msg_Warn( p_filter, "invalid fontsize, using 12" );
2299 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2302 p_sys->i_font_size = i_size;
2304 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2306 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2307 return VLC_EGENERIC;