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->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
490 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
492 i_align_offset = i_width - p_line->i_width;
494 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
495 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
497 i_align_offset = ( i_width - p_line->i_width ) / 2;
501 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
503 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
505 i_offset = ( p_line->p_glyph_pos[ i ].y +
506 i_glyph_tmax - p_glyph->top + 2 ) *
507 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
510 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
512 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
514 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
516 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
523 /* Outlining (find something better than nearest neighbour filtering ?) */
526 uint8_t *p_dst = p_region->picture.Y_PIXELS;
527 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
528 uint8_t left, current;
530 for( y = 1; y < (int)fmt.i_height - 1; y++ )
532 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
533 p_dst += p_region->picture.Y_PITCH;
536 for( x = 1; x < (int)fmt.i_width - 1; x++ )
539 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
540 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;
544 memset( p_top, 0, fmt.i_width );
550 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
551 FT_BitmapGlyph p_this_glyph, FT_Vector *p_this_glyph_pos,
552 FT_BitmapGlyph p_next_glyph, FT_Vector *p_next_glyph_pos,
553 int i_glyph_tmax, int i_align_offset,
554 uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
555 subpicture_region_t *p_region)
559 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
561 p_dst_y = p_region->picture.Y_PIXELS;
562 p_dst_u = p_region->picture.U_PIXELS;
563 p_dst_v = p_region->picture.V_PIXELS;
564 p_dst_a = p_region->picture.A_PIXELS;
565 i_pitch = p_region->picture.A_PITCH;
567 int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
568 p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
570 for( y = 0; y < i_line_thickness; y++ )
572 int i_extra = p_this_glyph->bitmap.width;
576 i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
577 (p_this_glyph_pos->x + p_this_glyph->left);
579 for( x = 0; x < i_extra; x++ )
581 vlc_bool_t b_ok = VLC_TRUE;
583 /* break the underline around the tails of any glyphs which cross it */
584 for( z = x - i_line_thickness;
585 z < x + i_line_thickness && b_ok;
588 if( p_next_glyph && ( z >= i_extra ) )
590 int i_row = i_line_offset + p_next_glyph->top + y;
592 if( ( p_next_glyph->bitmap.rows > i_row ) &&
593 p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
598 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
600 int i_row = i_line_offset + p_this_glyph->top + y;
602 if( ( p_this_glyph->bitmap.rows > i_row ) &&
603 p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
612 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
613 p_dst_u[i_offset+x] = i_u;
614 p_dst_v[i_offset+x] = i_v;
615 p_dst_a[i_offset+x] = 255;
622 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
624 uint8_t *p_dst = p_region->picture.A_PIXELS;
625 int i_pitch = p_region->picture.A_PITCH;
628 for( ; p_line != NULL; p_line = p_line->p_next )
630 int i_glyph_tmax=0, i = 0;
631 int i_bitmap_offset, i_offset, i_align_offset = 0;
632 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
634 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
635 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
638 if( p_line->i_width < i_width )
640 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
641 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
643 i_align_offset = i_width - p_line->i_width;
645 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
646 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
648 i_align_offset = ( i_width - p_line->i_width ) / 2;
652 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
654 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
656 i_offset = ( p_line->p_glyph_pos[ i ].y +
657 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
658 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
659 i_align_offset +xoffset;
661 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
663 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
665 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
666 if( p_dst[i_offset+x] <
667 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
669 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
678 /*****************************************************************************
679 * Render: place string in picture
680 *****************************************************************************
681 * This function merges the previously rendered freetype glyphs into a picture
682 *****************************************************************************/
683 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
684 line_desc_t *p_line, int i_width, int i_height )
686 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
688 int i, x, y, i_pitch, i_alpha;
689 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
690 subpicture_region_t *p_region_tmp;
692 if( i_width == 0 || i_height == 0 )
695 /* Create a new subpicture region */
696 memset( &fmt, 0, sizeof(video_format_t) );
697 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
699 fmt.i_width = fmt.i_visible_width = i_width + 6;
700 fmt.i_height = fmt.i_visible_height = i_height + 6;
701 if( p_region->fmt.i_visible_width > 0 )
702 fmt.i_visible_width = p_region->fmt.i_visible_width;
703 if( p_region->fmt.i_visible_height > 0 )
704 fmt.i_visible_height = p_region->fmt.i_visible_height;
705 fmt.i_x_offset = fmt.i_y_offset = 0;
706 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
709 msg_Err( p_filter, "cannot allocate SPU region" );
713 p_region->fmt = p_region_tmp->fmt;
714 p_region->picture = p_region_tmp->picture;
715 free( p_region_tmp );
717 /* Calculate text color components */
718 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
719 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
720 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
721 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
722 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
723 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
724 i_alpha = p_line->i_alpha;
726 p_dst_y = p_region->picture.Y_PIXELS;
727 p_dst_u = p_region->picture.U_PIXELS;
728 p_dst_v = p_region->picture.V_PIXELS;
729 p_dst_a = p_region->picture.A_PIXELS;
730 i_pitch = p_region->picture.A_PITCH;
732 /* Initialize the region pixels */
733 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
735 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
736 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
737 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
738 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
742 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
743 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
744 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
745 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
747 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
748 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
750 DrawBlack( p_line, i_width, p_region, 0, 0);
751 DrawBlack( p_line, i_width, p_region, -1, 0);
752 DrawBlack( p_line, i_width, p_region, 0, -1);
753 DrawBlack( p_line, i_width, p_region, 1, 0);
754 DrawBlack( p_line, i_width, p_region, 0, 1);
757 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
759 DrawBlack( p_line, i_width, p_region, -1, -1);
760 DrawBlack( p_line, i_width, p_region, -1, 1);
761 DrawBlack( p_line, i_width, p_region, 1, -1);
762 DrawBlack( p_line, i_width, p_region, 1, 1);
764 DrawBlack( p_line, i_width, p_region, -2, 0);
765 DrawBlack( p_line, i_width, p_region, 0, -2);
766 DrawBlack( p_line, i_width, p_region, 2, 0);
767 DrawBlack( p_line, i_width, p_region, 0, 2);
769 DrawBlack( p_line, i_width, p_region, -2, -2);
770 DrawBlack( p_line, i_width, p_region, -2, 2);
771 DrawBlack( p_line, i_width, p_region, 2, -2);
772 DrawBlack( p_line, i_width, p_region, 2, 2);
774 DrawBlack( p_line, i_width, p_region, -3, 0);
775 DrawBlack( p_line, i_width, p_region, 0, -3);
776 DrawBlack( p_line, i_width, p_region, 3, 0);
777 DrawBlack( p_line, i_width, p_region, 0, 3);
780 for( ; p_line != NULL; p_line = p_line->p_next )
782 int i_glyph_tmax = 0;
783 int i_bitmap_offset, i_offset, i_align_offset = 0;
784 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
786 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
787 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
790 if( p_line->i_width < i_width )
792 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
793 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
795 i_align_offset = i_width - p_line->i_width;
797 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
798 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
800 i_align_offset = ( i_width - p_line->i_width ) / 2;
804 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
806 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
808 i_offset = ( p_line->p_glyph_pos[ i ].y +
809 i_glyph_tmax - p_glyph->top + 3 ) *
810 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
813 if( p_line->b_new_color_mode )
815 /* Every glyph can (and in fact must) have its own color */
816 int i_red = ( p_line->p_rgb[ i ] & 0x00ff0000 ) >> 16;
817 int i_green = ( p_line->p_rgb[ i ] & 0x0000ff00 ) >> 8;
818 int i_blue = ( p_line->p_rgb[ i ] & 0x000000ff );
820 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
821 802 * i_blue + 4096 + 131072 ) >> 13, 235);
822 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
823 3598 * i_blue + 4096 + 1048576) >> 13, 240);
824 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
825 -585 * i_blue + 4096 + 1048576) >> 13, 240);
828 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
830 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
832 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
834 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
835 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
837 p_dst_u[i_offset+x] = i_u;
838 p_dst_v[i_offset+x] = i_v;
840 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
841 p_dst_a[i_offset+x] = 0xff;
847 if( p_line->pi_underline_thickness[ i ] )
849 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
850 p_line->pi_underline_offset[ i ],
851 (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
852 p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
853 p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
854 i_glyph_tmax, i_align_offset,
855 i_y, i_u, i_v, i_alpha,
861 /* Apply the alpha setting */
862 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
863 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
869 * This function renders a text subpicture region into another one.
870 * It also calculates the size needed for this string, and renders the
871 * needed glyphs into memory. It is used as pf_add_string callback in
872 * the vout method by this module
874 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
875 subpicture_region_t *p_region_in )
877 filter_sys_t *p_sys = p_filter->p_sys;
878 line_desc_t *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
879 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
880 uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
883 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
884 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
892 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
893 psz_string = p_region_in->psz_text;
894 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
896 if( p_region_in->p_style )
898 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
899 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
900 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
904 i_font_color = p_sys->i_font_color;
905 i_font_alpha = 255 - p_sys->i_font_opacity;
906 i_font_size = p_sys->i_default_font_size;
909 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
910 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
911 SetFontSize( p_filter, i_font_size );
913 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
914 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
915 i_blue = i_font_color & 0x000000FF;
917 result.x = result.y = 0;
918 line.xMin = line.xMax = line.yMin = line.yMax = 0;
920 psz_unicode = psz_unicode_orig =
921 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
922 if( psz_unicode == NULL )
924 msg_Err( p_filter, "out of memory" );
927 #if defined(WORDS_BIGENDIAN)
928 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
930 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
932 if( iconv_handle == (vlc_iconv_t)-1 )
934 msg_Warn( p_filter, "unable to do conversion" );
940 const char *p_in_buffer = psz_string;
941 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
942 i_in_bytes = strlen( psz_string );
943 i_out_bytes = i_in_bytes * sizeof( uint32_t );
944 i_out_bytes_left = i_out_bytes;
945 p_out_buffer = (char *)psz_unicode;
946 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
947 &p_out_buffer, &i_out_bytes_left );
949 vlc_iconv_close( iconv_handle );
953 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
954 "bytes left %d", strerror(errno), (int)i_in_bytes );
957 *(uint32_t*)p_out_buffer = 0;
958 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
961 #if defined(HAVE_FRIBIDI)
963 uint32_t *p_fribidi_string;
964 int start_pos, pos = 0;
966 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
967 if( !p_fribidi_string )
969 msg_Err( p_filter, "out of memory" );
973 /* Do bidi conversion line-by-line */
974 while(pos < i_string_length)
976 while(pos < i_string_length) {
977 i_char = psz_unicode[pos];
978 if (i_char != '\r' && i_char != '\n')
980 p_fribidi_string[pos] = i_char;
984 while(pos < i_string_length) {
985 i_char = psz_unicode[pos];
986 if (i_char == '\r' || i_char == '\n')
992 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
993 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
994 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
998 free( psz_unicode_orig );
999 psz_unicode = psz_unicode_orig = p_fribidi_string;
1000 p_fribidi_string[ i_string_length ] = 0;
1004 /* Calculate relative glyph positions and a bounding box for the
1006 if( !(p_line = NewLine( strlen( psz_string ))) )
1008 msg_Err( p_filter, "out of memory" );
1012 i_pen_x = i_pen_y = 0;
1014 psz_line_start = psz_unicode;
1016 #define face p_sys->p_face
1017 #define glyph face->glyph
1019 while( *psz_unicode )
1021 i_char = *psz_unicode++;
1022 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
1027 if( i_char == '\n' )
1029 psz_line_start = psz_unicode;
1030 if( !(p_next = NewLine( strlen( psz_string ))) )
1032 msg_Err( p_filter, "out of memory" );
1035 p_line->p_next = p_next;
1036 p_line->i_width = line.xMax;
1037 p_line->i_height = face->size->metrics.height >> 6;
1038 p_line->pp_glyphs[ i ] = NULL;
1039 p_line->i_alpha = i_font_alpha;
1040 p_line->i_red = i_red;
1041 p_line->i_green = i_green;
1042 p_line->i_blue = i_blue;
1045 result.x = __MAX( result.x, line.xMax );
1046 result.y += face->size->metrics.height >> 6;
1049 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1050 i_pen_y += face->size->metrics.height >> 6;
1052 msg_Dbg( p_filter, "Creating new line, i is %d", i );
1057 i_glyph_index = FT_Get_Char_Index( face, i_char );
1058 if( p_sys->i_use_kerning && i_glyph_index
1062 FT_Get_Kerning( face, i_previous, i_glyph_index,
1063 ft_kerning_default, &delta );
1064 i_pen_x += delta.x >> 6;
1067 p_line->p_glyph_pos[ i ].x = i_pen_x;
1068 p_line->p_glyph_pos[ i ].y = i_pen_y;
1069 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
1072 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
1076 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
1079 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
1083 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1084 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
1087 FT_Done_Glyph( tmp_glyph );
1090 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1093 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1094 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1095 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1097 p_line->pp_glyphs[ i ] = NULL;
1099 p_line = NewLine( strlen( psz_string ));
1100 if( p_prev ) p_prev->p_next = p_line;
1101 else p_lines = p_line;
1103 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
1107 if( psz_unicode == psz_line_start )
1109 msg_Warn( p_filter, "unbreakable string" );
1114 *psz_unicode = '\n';
1116 psz_unicode = psz_line_start;
1119 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1122 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1123 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1125 i_previous = i_glyph_index;
1126 i_pen_x += glyph->advance.x >> 6;
1130 p_line->i_width = line.xMax;
1131 p_line->i_height = face->size->metrics.height >> 6;
1132 p_line->pp_glyphs[ i ] = NULL;
1133 p_line->i_alpha = i_font_alpha;
1134 p_line->i_red = i_red;
1135 p_line->i_green = i_green;
1136 p_line->i_blue = i_blue;
1137 result.x = __MAX( result.x, line.xMax );
1138 result.y += line.yMax - line.yMin;
1143 p_region_out->i_x = p_region_in->i_x;
1144 p_region_out->i_y = p_region_in->i_y;
1146 if( config_GetInt( p_filter, "freetype-yuvp" ) )
1147 Render( p_filter, p_region_out, p_lines, result.x, result.y );
1149 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
1151 if( psz_unicode_orig ) free( psz_unicode_orig );
1152 FreeLines( p_lines );
1156 if( psz_unicode_orig ) free( psz_unicode_orig );
1157 FreeLines( p_lines );
1158 return VLC_EGENERIC;
1161 #ifdef HAVE_FONTCONFIG
1162 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1163 int i_font_color, int i_font_alpha, vlc_bool_t b_bold,
1164 vlc_bool_t b_italic, vlc_bool_t b_uline )
1166 ft_style_t *p_style = malloc( sizeof( ft_style_t ));
1170 p_style->i_font_size = i_font_size;
1171 p_style->i_font_color = ( i_font_color & 0x00ffffff )
1172 | (( i_font_alpha & 0xff ) << 24 );
1173 p_style->b_italic = b_italic;
1174 p_style->b_bold = b_bold;
1175 p_style->b_underline = b_uline;
1176 /* p_style has just been malloc'ed in this function -
1177 * it CAN'T have a previous assignment, and hence we
1178 * don't need to do a free() for any previous value -
1179 * which will in fact be undefined. */
1180 p_style->psz_fontname = strdup( psz_fontname );
1185 static void DeleteStyle( ft_style_t *p_style )
1189 if( p_style->psz_fontname )
1190 free( p_style->psz_fontname );
1195 static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
1202 if(( s1->i_font_size == s2->i_font_size ) &&
1203 ( s1->i_font_color == s2->i_font_color ) &&
1204 ( s1->b_italic == s2->b_italic ) &&
1205 ( s1->b_bold == s2->b_bold ) &&
1206 ( s1->b_underline == s2->b_underline ) &&
1207 ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
1214 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1215 int i_color, int i_alpha )
1217 font_stack_t *p_new;
1220 return VLC_EGENERIC;
1222 p_new = malloc( sizeof( font_stack_t ) );
1226 p_new->p_next = NULL;
1229 p_new->psz_name = strdup( psz_name );
1231 p_new->psz_name = NULL;
1233 p_new->i_size = i_size;
1234 p_new->i_color = i_color;
1235 p_new->i_alpha = i_alpha;
1243 font_stack_t *p_last;
1245 for( p_last = *p_font;
1247 p_last = p_last->p_next )
1250 p_last->p_next = p_new;
1255 static int PopFont( font_stack_t **p_font )
1257 font_stack_t *p_last, *p_next_to_last;
1259 if( !p_font || !*p_font )
1260 return VLC_EGENERIC;
1262 p_next_to_last = NULL;
1263 for( p_last = *p_font;
1265 p_last = p_last->p_next )
1267 p_next_to_last = p_last;
1270 if( p_next_to_last )
1271 p_next_to_last->p_next = NULL;
1275 free( p_last->psz_name );
1281 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1282 int *i_color, int *i_alpha )
1284 font_stack_t *p_last;
1286 if( !p_font || !*p_font )
1287 return VLC_EGENERIC;
1289 for( p_last=*p_font;
1291 p_last=p_last->p_next )
1294 *psz_name = p_last->psz_name;
1295 *i_size = p_last->i_size;
1296 *i_color = p_last->i_color;
1297 *i_alpha = p_last->i_alpha;
1302 static void IconvText( filter_t *p_filter, const char *psz_string,
1303 uint32_t *i_string_length, uint32_t **ppsz_unicode )
1305 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
1307 /* If memory hasn't been allocated for our output string, allocate it here
1308 * - the calling function must now be responsible for freeing it.
1310 if( !*ppsz_unicode )
1311 *ppsz_unicode = (uint32_t *)
1312 malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
1314 /* We don't need to handle a NULL pointer in *ppsz_unicode
1315 * if we are instead testing for a non NULL value like we are here */
1319 #if defined(WORDS_BIGENDIAN)
1320 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
1322 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
1324 if( iconv_handle != (vlc_iconv_t)-1 )
1326 char *p_in_buffer, *p_out_buffer;
1327 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
1328 i_in_bytes = strlen( psz_string );
1329 i_out_bytes = i_in_bytes * sizeof( uint32_t );
1330 i_out_bytes_left = i_out_bytes;
1331 p_in_buffer = (char *) psz_string;
1332 p_out_buffer = (char *) *ppsz_unicode;
1333 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
1334 &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
1336 vlc_iconv_close( iconv_handle );
1340 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
1341 "bytes left %d", strerror(errno), (int)i_in_bytes );
1345 *(uint32_t*)p_out_buffer = 0;
1347 (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
1352 msg_Warn( p_filter, "unable to do conversion" );
1357 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1358 font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic,
1359 vlc_bool_t b_uline )
1361 ft_style_t *p_style = NULL;
1363 char *psz_fontname = NULL;
1364 int i_font_color = p_sys->i_font_color;
1365 int i_font_alpha = 0;
1366 int i_font_size = p_sys->i_font_size;
1368 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
1369 &i_font_color, &i_font_alpha ) )
1371 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
1372 i_font_alpha, b_bold, b_italic, b_uline );
1377 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
1378 vlc_bool_t b_uline, line_desc_t *p_line,
1379 uint32_t *psz_unicode, int *pi_pen_x, int i_pen_y,
1380 int *pi_start, FT_Vector *p_result )
1385 vlc_bool_t b_first_on_line = VLC_TRUE;
1388 int i_pen_x_start = *pi_pen_x;
1390 uint32_t *psz_unicode_start = psz_unicode;
1392 line.xMin = line.xMax = line.yMin = line.yMax = 0;
1394 /* Account for part of line already in position */
1395 for( i=0; i<*pi_start; i++ )
1399 FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
1400 ft_glyph_bbox_pixels, &glyph_size );
1402 line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
1403 glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
1404 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1405 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1411 b_first_on_line = VLC_FALSE;
1413 while( *psz_unicode && ( *psz_unicode != '\n' ) )
1419 int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
1420 if( FT_HAS_KERNING( p_face ) && i_glyph_index
1424 FT_Get_Kerning( p_face, i_previous, i_glyph_index,
1425 ft_kerning_default, &delta );
1426 *pi_pen_x += delta.x >> 6;
1428 p_line->p_glyph_pos[ i ].x = *pi_pen_x;
1429 p_line->p_glyph_pos[ i ].y = i_pen_y;
1431 i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
1435 "unable to render text FT_Load_Glyph returned %d", i_error );
1436 p_line->pp_glyphs[ i ] = NULL;
1437 return VLC_EGENERIC;
1439 i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
1443 "unable to render text FT_Get_Glyph returned %d", i_error );
1444 p_line->pp_glyphs[ i ] = NULL;
1445 return VLC_EGENERIC;
1447 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
1448 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1451 FT_Done_Glyph( tmp_glyph );
1456 float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
1457 p_face->size->metrics.y_scale));
1458 float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
1459 p_face->size->metrics.y_scale));
1461 p_line->pi_underline_offset[ i ] =
1462 ( aOffset < 0 ) ? -aOffset : aOffset;
1463 p_line->pi_underline_thickness[ i ] =
1464 ( aSize < 0 ) ? -aSize : aSize;
1466 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
1467 p_line->p_rgb[ i ] = i_font_color & 0x00ffffff;
1469 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
1470 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
1471 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
1473 while( --i > *pi_start )
1475 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
1478 while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
1482 if( psz_unicode == psz_unicode_start )
1484 if( b_first_on_line )
1486 msg_Warn( p_filter, "unbreakable string" );
1487 p_line->pp_glyphs[ i ] = NULL;
1488 return VLC_EGENERIC;
1490 *pi_pen_x = i_pen_x_start;
1492 p_line->i_width = line.xMax;
1493 p_line->i_height = __MAX( p_line->i_height,
1494 p_face->size->metrics.height >> 6 );
1495 p_line->pp_glyphs[ i ] = NULL;
1497 p_result->x = __MAX( p_result->x, line.xMax );
1498 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1499 i_yMax - i_yMin ) );
1506 *psz_unicode = '\n';
1508 psz_unicode = psz_unicode_start;
1509 *pi_pen_x = i_pen_x_start;
1517 line.yMax = __MAX( line.yMax, glyph_size.yMax );
1518 line.yMin = __MIN( line.yMin, glyph_size.yMin );
1520 i_previous = i_glyph_index;
1521 *pi_pen_x += p_face->glyph->advance.x >> 6;
1524 p_line->i_width = line.xMax;
1525 p_line->i_height = __MAX( p_line->i_height,
1526 p_face->size->metrics.height >> 6 );
1527 p_line->pp_glyphs[ i ] = NULL;
1529 p_result->x = __MAX( p_result->x, line.xMax );
1530 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
1531 line.yMax - line.yMin ) );
1535 /* Get rid of any text processed - if necessary repositioning
1536 * at the start of a new line of text
1540 *psz_unicode_start = '\0';
1545 for( i=0; psz_unicode[ i ]; i++ )
1546 psz_unicode_start[ i ] = psz_unicode[ i ];
1547 psz_unicode_start[ i ] = '\0';
1553 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1554 font_stack_t **p_fonts )
1557 char *psz_fontname = NULL;
1558 int i_font_color = 0xffffff;
1559 int i_font_alpha = 0;
1560 int i_font_size = 24;
1562 /* Default all attributes to the top font in the stack -- in case not
1563 * all attributes are specified in the sub-font
1565 if( VLC_SUCCESS == PeekFont( p_fonts,
1571 psz_fontname = strdup( psz_fontname );
1574 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
1576 char *psz_name = xml_ReaderName ( p_xml_reader );
1577 char *psz_value = xml_ReaderValue ( p_xml_reader );
1579 if( psz_name && psz_value )
1581 if( !strcasecmp( "face", psz_name ) )
1583 if( psz_fontname ) free( psz_fontname );
1584 psz_fontname = strdup( psz_value );
1586 else if( !strcasecmp( "size", psz_name ) )
1588 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
1590 int i_value = atoi( psz_value );
1592 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1593 i_font_size += ( i_value * i_font_size ) / 10;
1594 else if( i_value < -5 )
1595 i_font_size = - i_value;
1596 else if( i_value > 5 )
1597 i_font_size = i_value;
1600 i_font_size = atoi( psz_value );
1602 else if( !strcasecmp( "color", psz_name ) &&
1603 ( psz_value[0] == '#' ) )
1605 i_font_color = strtol( psz_value + 1, NULL, 16 );
1606 i_font_color &= 0x00ffffff;
1608 else if( !strcasecmp( "alpha", psz_name ) &&
1609 ( psz_value[0] == '#' ) )
1611 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
1612 i_font_alpha &= 0xff;
1618 rv = PushFont( p_fonts,
1624 free( psz_fontname );
1629 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
1630 uint32_t **psz_text_out, uint32_t *pi_runs,
1631 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
1632 ft_style_t *p_style )
1634 uint32_t i_string_length = 0;
1636 IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
1637 *psz_text_out += i_string_length;
1639 if( ppp_styles && ppi_run_lengths )
1645 *ppp_styles = (ft_style_t **)
1646 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
1648 else if( *pi_runs == 1 )
1650 *ppp_styles = (ft_style_t **)
1651 malloc( *pi_runs * sizeof( ft_style_t * ) );
1654 /* We have just malloc'ed this memory successfully -
1655 * *pi_runs HAS to be within the memory area of *ppp_styles */
1658 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
1662 if( *ppi_run_lengths )
1664 *ppi_run_lengths = (uint32_t *)
1665 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
1667 else if( *pi_runs == 1 )
1669 *ppi_run_lengths = (uint32_t *)
1670 malloc( *pi_runs * sizeof( uint32_t ) );
1673 /* same remarks here */
1674 if( *ppi_run_lengths )
1676 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
1679 /* If we couldn't use the p_style argument due to memory allocation
1680 * problems above, release it here.
1682 if( p_style ) DeleteStyle( p_style );
1685 static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader,
1686 text_style_t *p_font_style, uint32_t *psz_text,
1687 int *pi_len, uint32_t *pi_runs,
1688 uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles)
1690 int rv = VLC_SUCCESS;
1691 filter_sys_t *p_sys = p_filter->p_sys;
1692 uint32_t *psz_text_orig = psz_text;
1693 font_stack_t *p_fonts = NULL;
1695 char *psz_node = NULL;
1697 vlc_bool_t b_italic = VLC_FALSE;
1698 vlc_bool_t b_bold = VLC_FALSE;
1699 vlc_bool_t b_uline = VLC_FALSE;
1703 rv = PushFont( &p_fonts,
1704 p_font_style->psz_fontname,
1705 p_font_style->i_font_size,
1706 p_font_style->i_font_color,
1707 p_font_style->i_font_alpha );
1709 if( p_font_style->i_style_flags & STYLE_BOLD )
1711 if( p_font_style->i_style_flags & STYLE_ITALIC )
1712 b_italic = VLC_TRUE;
1713 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
1718 rv = PushFont( &p_fonts,
1724 if( rv != VLC_SUCCESS )
1727 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
1729 switch ( xml_ReaderNodeType( p_xml_reader ) )
1731 case XML_READER_NONE:
1733 case XML_READER_ENDELEM:
1734 psz_node = xml_ReaderName( p_xml_reader );
1738 if( !strcasecmp( "font", psz_node ) )
1739 PopFont( &p_fonts );
1740 else if( !strcasecmp( "b", psz_node ) )
1742 else if( !strcasecmp( "i", psz_node ) )
1743 b_italic = VLC_FALSE;
1744 else if( !strcasecmp( "u", psz_node ) )
1745 b_uline = VLC_FALSE;
1750 case XML_READER_STARTELEM:
1751 psz_node = xml_ReaderName( p_xml_reader );
1754 if( !strcasecmp( "font", psz_node ) )
1755 rv = HandleFontAttributes( p_xml_reader, &p_fonts );
1756 else if( !strcasecmp( "b", psz_node ) )
1758 else if( !strcasecmp( "i", psz_node ) )
1759 b_italic = VLC_TRUE;
1760 else if( !strcasecmp( "u", psz_node ) )
1762 else if( !strcasecmp( "br", psz_node ) )
1764 SetupLine( p_filter, "\n", &psz_text,
1765 pi_runs, ppi_run_lengths, ppp_styles,
1766 GetStyleFromFontStack( p_sys,
1776 case XML_READER_TEXT:
1777 psz_node = xml_ReaderValue( p_xml_reader );
1780 /* Turn any multiple-whitespaces into single spaces */
1781 char *s = strpbrk( psz_node, "\t\r\n " );
1784 int i_whitespace = strspn( s, "\t\r\n " );
1786 if( i_whitespace > 1 )
1789 strlen( s ) - i_whitespace + 1 );
1792 s = strpbrk( s+1, "\t\r\n " );
1794 SetupLine( p_filter, psz_node, &psz_text,
1795 pi_runs, ppi_run_lengths, ppp_styles,
1796 GetStyleFromFontStack( p_sys,
1805 if( rv != VLC_SUCCESS )
1807 psz_text = psz_text_orig;
1812 *pi_len = psz_text - psz_text_orig;
1814 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1819 static int ProcessLines( filter_t *p_filter, uint32_t *psz_text,
1820 int i_len, uint32_t i_runs,
1821 uint32_t *pi_run_lengths, ft_style_t **pp_styles,
1822 line_desc_t **pp_lines, FT_Vector *p_result )
1824 filter_sys_t *p_sys = p_filter->p_sys;
1825 ft_style_t **pp_char_styles;
1829 /* Assign each character in the text string its style explicitly, so that
1830 * after the characters have been shuffled around by Fribidi, we can re-apply
1831 * the styles, and to simplify the calculation of runs within a line.
1833 pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
1834 if( !pp_char_styles )
1838 for( j = 0; j < i_runs; j++ )
1839 for( k = 0; k < pi_run_lengths[ j ]; k++ )
1840 pp_char_styles[ i++ ] = pp_styles[ j ];
1842 #if defined(HAVE_FRIBIDI)
1844 ft_style_t **pp_char_styles_new;
1846 uint32_t *p_fribidi_string;
1847 int start_pos, pos = 0;
1849 p_fribidi_string = malloc( (i_len + 1) * sizeof(uint32_t) );
1850 if(! p_fribidi_string )
1852 msg_Err( p_filter, "out of memory" );
1853 free( pp_char_styles );
1856 pp_char_styles_new = (ft_style_t **)
1857 malloc( i_len * sizeof( ft_style_t * ));
1858 if(! pp_char_styles_new )
1860 msg_Err( p_filter, "out of memory" );
1861 free( p_fribidi_string );
1862 free( pp_char_styles );
1865 p_positions = (int *) malloc( (i_len + 1) * sizeof( int ) );
1868 msg_Err( p_filter, "out of memory" );
1869 free( pp_char_styles_new );
1870 free( p_fribidi_string );
1871 free( pp_char_styles );
1875 /* Do bidi conversion line-by-line */
1878 while(pos < i_len) {
1879 if (psz_text[pos] != '\n')
1881 p_fribidi_string[pos] = psz_text[pos];
1882 pp_char_styles_new[pos] = pp_char_styles[pos];
1886 while(pos < i_len) {
1887 if (psz_text[pos] == '\n')
1891 if (pos > start_pos)
1893 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1894 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1895 pos - start_pos, &base_dir,
1896 (FriBidiChar*)p_fribidi_string + start_pos,
1900 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
1902 pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
1903 p_positions[ j - start_pos ] ];
1907 free( p_positions );
1908 free( pp_char_styles );
1909 pp_char_styles = pp_char_styles_new;
1910 psz_text = p_fribidi_string;
1911 p_fribidi_string[ i_len ] = 0;
1914 FT_Vector tmp_result;
1916 line_desc_t *p_line = NULL;
1917 line_desc_t *p_prev = NULL;
1923 p_result->x = p_result->y = 0;
1924 tmp_result.x = tmp_result.y = 0;
1927 for( k = 0; k <= (uint32_t) i_len; k++ )
1929 if( ( k == (uint32_t) i_len ) ||
1931 !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
1933 ft_style_t *p_style = pp_char_styles[ k - 1 ];
1935 /* End of the current style run */
1936 FT_Face p_face = NULL;
1938 char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
1939 p_style->psz_fontname,
1945 if( FT_New_Face( p_sys->p_library,
1946 psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
1948 free( psz_fontfile );
1949 free( pp_char_styles );
1950 #if defined(HAVE_FRIBIDI)
1953 return VLC_EGENERIC;
1955 free( psz_fontfile );
1958 if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
1959 ft_encoding_unicode ) ||
1960 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
1961 p_style->i_font_size ) )
1963 if( p_face ) FT_Done_Face( p_face );
1964 free( pp_char_styles );
1965 #if defined(HAVE_FRIBIDI)
1968 return VLC_EGENERIC;
1970 p_sys->i_use_kerning =
1971 FT_HAS_KERNING( ( p_face ? p_face : p_sys->p_face ) );
1974 uint32_t *psz_unicode = (uint32_t *)
1975 malloc( (k - i_prev + 1) * sizeof( uint32_t ));
1978 msg_Err( p_filter, "out of memory" );
1979 if( p_face ) FT_Done_Face( p_face );
1980 free( pp_char_styles );
1981 free( psz_unicode );
1982 #if defined(HAVE_FRIBIDI)
1987 memcpy( psz_unicode, psz_text + i_prev,
1988 sizeof( uint32_t ) * ( k - i_prev ) );
1989 psz_unicode[ k - i_prev ] = 0;
1990 while( *psz_unicode )
1994 if( !(p_line = NewLine( i_len - i_prev)) )
1996 msg_Err( p_filter, "out of memory" );
1997 if( p_face ) FT_Done_Face( p_face );
1998 free( pp_char_styles );
1999 free( psz_unicode );
2000 #if defined(HAVE_FRIBIDI)
2005 /* New Color mode only works in YUVA rendering mode --
2006 * (RGB mode has palette constraints on it). We therefore
2007 * need to populate the legacy colour fields also.
2009 p_line->b_new_color_mode = VLC_TRUE;
2010 p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
2011 p_line->i_red = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
2012 p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >> 8;
2013 p_line->i_blue = ( p_style->i_font_color & 0x000000ff );
2014 p_line->p_next = NULL;
2016 i_pen_y += tmp_result.y;
2020 if( p_prev ) p_prev->p_next = p_line;
2021 else *pp_lines = p_line;
2023 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
2024 p_style->i_font_color, p_style->b_underline,
2025 p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
2026 &tmp_result ) != VLC_SUCCESS )
2028 if( p_face ) FT_Done_Face( p_face );
2029 free( pp_char_styles );
2030 free( psz_unicode );
2031 #if defined(HAVE_FRIBIDI)
2034 return VLC_EGENERIC;
2038 p_result->x = __MAX( p_result->x, tmp_result.x );
2039 p_result->y += tmp_result.y;
2045 free( psz_unicode );
2046 if( p_face ) FT_Done_Face( p_face );
2050 free( pp_char_styles );
2051 #if defined(HAVE_FRIBIDI)
2057 p_result->x = __MAX( p_result->x, tmp_result.x );
2058 p_result->y += tmp_result.y;
2063 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2064 subpicture_region_t *p_region_in )
2066 int rv = VLC_SUCCESS;
2067 stream_t *p_sub = NULL;
2068 xml_t *p_xml = NULL;
2069 xml_reader_t *p_xml_reader = NULL;
2071 if( !p_region_in || !p_region_in->psz_html )
2072 return VLC_EGENERIC;
2074 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2075 (uint8_t *) p_region_in->psz_html,
2076 strlen( p_region_in->psz_html ),
2080 p_xml = xml_Create( p_filter );
2083 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
2088 uint32_t i_runs = 0;
2089 uint32_t *pi_run_lengths = NULL;
2090 ft_style_t **pp_styles = NULL;
2092 line_desc_t *p_lines = NULL;
2094 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
2095 sizeof( uint32_t ) );
2100 rv = ProcessNodes( p_filter, p_xml_reader,
2101 p_region_in->p_style, psz_text, &i_len,
2102 &i_runs, &pi_run_lengths, &pp_styles );
2104 p_region_out->i_x = p_region_in->i_x;
2105 p_region_out->i_y = p_region_in->i_y;
2107 if( rv == VLC_SUCCESS )
2109 rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
2110 pi_run_lengths, pp_styles, &p_lines, &result );
2113 for( k=0; k<i_runs; k++)
2114 DeleteStyle( pp_styles[k] );
2116 free( pi_run_lengths );
2119 /* Don't attempt to render text that couldn't be layed out
2122 if( rv == VLC_SUCCESS )
2124 if( config_GetInt( p_filter, "freetype-yuvp" ) )
2126 Render( p_filter, p_region_out, p_lines,
2127 result.x, result.y );
2131 RenderYUVA( p_filter, p_region_out, p_lines,
2132 result.x, result.y );
2136 FreeLines( p_lines );
2138 xml_ReaderDelete( p_xml, p_xml_reader );
2140 xml_Delete( p_xml );
2142 stream_Delete( p_sub );
2148 static char* FontConfig_Select( FcConfig* priv, const char* family,
2149 vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
2152 FcPattern *pat, *p_pat;
2156 pat = FcPatternCreate();
2157 if (!pat) return NULL;
2159 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
2160 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
2161 FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
2162 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
2164 FcDefaultSubstitute( pat );
2166 if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
2168 FcPatternDestroy( pat );
2172 p_pat = FcFontMatch( priv, pat, &result );
2173 FcPatternDestroy( pat );
2174 if( !p_pat ) return NULL;
2176 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
2177 || ( val_b != FcTrue ) )
2179 FcPatternDestroy( p_pat );
2182 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
2187 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
2189 FcPatternDestroy( p_pat );
2194 if( strcasecmp((const char*)val_s, family ) != 0 )
2195 msg_Warn( p_filter, "fontconfig: selected font family is not"
2196 "the requested one: '%s' != '%s'\n",
2197 (const char*)val_s, family );
2200 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
2202 FcPatternDestroy( p_pat );
2206 FcPatternDestroy( p_pat );
2207 return strdup( (const char*)val_s );
2211 static void FreeLine( line_desc_t *p_line )
2214 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
2216 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
2218 free( p_line->pp_glyphs );
2219 free( p_line->p_glyph_pos );
2220 free( p_line->p_rgb );
2221 free( p_line->pi_underline_offset );
2222 free( p_line->pi_underline_thickness );
2226 static void FreeLines( line_desc_t *p_lines )
2228 line_desc_t *p_line, *p_next;
2230 if( !p_lines ) return;
2232 for( p_line = p_lines; p_line != NULL; p_line = p_next )
2234 p_next = p_line->p_next;
2239 static line_desc_t *NewLine( int i_count )
2241 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
2243 if( !p_line ) return NULL;
2244 p_line->i_height = 0;
2245 p_line->i_width = 0;
2246 p_line->p_next = NULL;
2248 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
2249 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
2250 p_line->p_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
2251 p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
2252 p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
2253 if( ( p_line->pp_glyphs == NULL ) ||
2254 ( p_line->p_glyph_pos == NULL ) ||
2255 ( p_line->p_rgb == NULL ) ||
2256 ( p_line->pi_underline_offset == NULL ) ||
2257 ( p_line->pi_underline_thickness == NULL ) )
2259 if( p_line->pi_underline_thickness )
2260 free( p_line->pi_underline_thickness );
2261 if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
2262 if( p_line->p_rgb ) free( p_line->p_rgb );
2263 if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
2264 if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
2268 p_line->pp_glyphs[0] = NULL;
2269 p_line->b_new_color_mode = VLC_FALSE;
2274 static int SetFontSize( filter_t *p_filter, int i_size )
2276 filter_sys_t *p_sys = p_filter->p_sys;
2278 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
2284 if( !p_sys->i_default_font_size &&
2285 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
2288 if( p_sys->i_default_font_size )
2290 i_size = p_sys->i_default_font_size;
2294 var_Get( p_filter, "freetype-rel-fontsize", &val );
2295 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
2296 p_filter->p_sys->i_display_height =
2297 p_filter->fmt_out.video.i_height;
2301 msg_Warn( p_filter, "invalid fontsize, using 12" );
2305 msg_Dbg( p_filter, "using fontsize: %i", i_size );
2308 p_sys->i_font_size = i_size;
2310 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
2312 msg_Err( p_filter, "couldn't set font size to %d", i_size );
2313 return VLC_EGENERIC;