1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2005 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
31 #ifdef HAVE_LINUX_LIMITS_H
32 # include <linux/limits.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
48 #include FT_FREETYPE_H
52 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
53 #elif defined( SYS_BEOS )
54 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
55 #elif defined( WIN32 )
56 #define DEFAULT_FONT "" /* Default font found at run-time */
58 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
61 #if defined(HAVE_FRIBIDI)
62 #include <fribidi/fribidi.h>
65 typedef struct line_desc_t line_desc_t;
67 /*****************************************************************************
69 *****************************************************************************/
70 static int Create ( vlc_object_t * );
71 static void Destroy( vlc_object_t * );
73 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
74 static int RenderText( filter_t *, subpicture_region_t *,
75 subpicture_region_t * );
76 static line_desc_t *NewLine( byte_t * );
78 static int SetFontSize( filter_t *, int );
80 /*****************************************************************************
82 *****************************************************************************/
83 #define FONT_TEXT N_("Font")
84 #define FONT_LONGTEXT N_("Filename for the font you want to use")
85 #define FONTSIZE_TEXT N_("Font size in pixels")
86 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
87 "that will be rendered on the video. " \
88 "If set to something different than 0 this option will override the " \
89 "relative font size." )
90 #define OPACITY_TEXT N_("Opacity")
91 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
92 "text that will be rendered on the video. 0 = transparent, " \
93 "255 = totally opaque. " )
94 #define COLOR_TEXT N_("Text default color")
95 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
96 "the video. This must be an hexadecimal (like HTML colors). The first two "\
97 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
98 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
99 #define FONTSIZER_TEXT N_("Relative font size")
100 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
101 "fonts that will be rendered on the video. If absolute font size is set, "\
102 "relative size will be overriden." )
104 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
105 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
106 N_("Large"), N_("Larger") };
107 #define YUVP_TEXT N_("Use YUVP renderer")
108 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
109 "This option is only needed if you want to encode into DVB subtitles" )
110 #define EFFECT_TEXT N_("Font Effect")
111 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
112 "text to improve its readability." )
114 #define EFFECT_BACKGROUND 1
115 #define EFFECT_OUTLINE 2
116 #define EFFECT_OUTLINE_FAT 3
118 static int pi_effects[] = { 1, 2, 3 };
119 static char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
121 static int pi_color_values[] = {
122 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
123 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
124 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
126 static char *ppsz_color_descriptions[] = {
127 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
128 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
129 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
132 set_shortname( _("Text renderer"));
133 set_description( _("Freetype2 font renderer") );
134 set_category( CAT_VIDEO );
135 set_subcategory( SUBCAT_VIDEO_SUBPIC );
137 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
140 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
141 FONTSIZE_LONGTEXT, VLC_TRUE );
143 /* opacity valid on 0..255, with default 255 = fully opaque */
144 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
145 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
147 /* hook to the color values list, with default 0x00ffffff = white */
148 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
149 COLOR_LONGTEXT, VLC_FALSE );
150 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
152 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
153 FONTSIZER_LONGTEXT, VLC_FALSE );
154 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
155 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
156 EFFECT_LONGTEXT, VLC_FALSE );
157 change_integer_list( pi_effects, ppsz_effects_text, 0 );
159 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
160 YUVP_LONGTEXT, VLC_TRUE );
161 set_capability( "text renderer", 100 );
162 add_shortcut( "text" );
163 set_callbacks( Create, Destroy );
168 /** NULL-terminated list of glyphs making the string */
169 FT_BitmapGlyph *pp_glyphs;
170 /** list of relative positions for the glyphs */
171 FT_Vector *p_glyph_pos;
175 int i_red, i_green, i_blue;
181 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
182 static void FreeLines( line_desc_t * );
183 static void FreeLine( line_desc_t * );
185 /*****************************************************************************
186 * filter_sys_t: freetype local data
187 *****************************************************************************
188 * This structure is part of the video output thread descriptor.
189 * It describes the freetype specific properties of an output thread.
190 *****************************************************************************/
193 FT_Library p_library; /* handle to library */
194 FT_Face p_face; /* handle to face object */
195 vlc_bool_t i_use_kerning;
196 uint8_t i_font_opacity;
201 int i_default_font_size;
202 int i_display_height;
205 /*****************************************************************************
206 * Create: allocates osd-text video thread output method
207 *****************************************************************************
208 * This function allocates and initializes a Clone vout method.
209 *****************************************************************************/
210 static int Create( vlc_object_t *p_this )
212 filter_t *p_filter = (filter_t *)p_this;
214 char *psz_fontfile = NULL;
218 /* Allocate structure */
219 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
222 msg_Err( p_filter, "out of memory" );
226 p_sys->p_library = 0;
227 p_sys->i_font_size = 0;
228 p_sys->i_display_height = 0;
230 var_Create( p_filter, "freetype-font",
231 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
232 var_Create( p_filter, "freetype-fontsize",
233 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
234 var_Create( p_filter, "freetype-rel-fontsize",
235 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
236 var_Create( p_filter, "freetype-opacity",
237 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
238 var_Create( p_filter, "freetype-effect",
239 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
240 var_Get( p_filter, "freetype-opacity", &val );
241 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
242 var_Create( p_filter, "freetype-color",
243 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
244 var_Get( p_filter, "freetype-color", &val );
245 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
246 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
248 /* Look what method was requested */
249 var_Get( p_filter, "freetype-font", &val );
250 psz_fontfile = val.psz_string;
251 if( !psz_fontfile || !*psz_fontfile )
253 if( psz_fontfile ) free( psz_fontfile );
254 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
256 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
257 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
259 strcpy( psz_fontfile, DEFAULT_FONT );
261 msg_Err( p_filter, "user didn't specify a font" );
266 i_error = FT_Init_FreeType( &p_sys->p_library );
269 msg_Err( p_filter, "couldn't initialize freetype" );
273 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
275 if( i_error == FT_Err_Unknown_File_Format )
277 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
282 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
286 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
289 msg_Err( p_filter, "font has no unicode translation table" );
293 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
295 var_Get( p_filter, "freetype-fontsize", &val );
296 p_sys->i_default_font_size = val.i_int;
297 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
299 if( psz_fontfile ) free( psz_fontfile );
300 p_filter->pf_render_text = RenderText;
304 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
305 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
306 if( psz_fontfile ) free( psz_fontfile );
311 /*****************************************************************************
312 * Destroy: destroy Clone video thread output method
313 *****************************************************************************
314 * Clean up all data and library connections
315 *****************************************************************************/
316 static void Destroy( vlc_object_t *p_this )
318 filter_t *p_filter = (filter_t *)p_this;
319 filter_sys_t *p_sys = p_filter->p_sys;
321 FT_Done_Face( p_sys->p_face );
322 FT_Done_FreeType( p_sys->p_library );
326 /*****************************************************************************
327 * Render: place string in picture
328 *****************************************************************************
329 * This function merges the previously rendered freetype glyphs into a picture
330 *****************************************************************************/
331 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
332 line_desc_t *p_line, int i_width, int i_height )
334 static uint8_t pi_gamma[16] =
335 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
336 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
340 int i, x, y, i_pitch;
341 uint8_t i_y; /* YUV values, derived from incoming RGB */
343 subpicture_region_t *p_region_tmp;
345 /* Create a new subpicture region */
346 memset( &fmt, 0, sizeof(video_format_t) );
347 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
349 fmt.i_width = fmt.i_visible_width = i_width + 4;
350 fmt.i_height = fmt.i_visible_height = i_height + 4;
351 fmt.i_x_offset = fmt.i_y_offset = 0;
352 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
355 msg_Err( p_filter, "cannot allocate SPU region" );
359 p_region->fmt = p_region_tmp->fmt;
360 p_region->picture = p_region_tmp->picture;
361 free( p_region_tmp );
363 /* Calculate text color components */
364 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
365 25 * p_line->i_blue + 128) >> 8) + 16;
366 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
367 112 * p_line->i_blue + 128) >> 8) + 128;
368 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
369 18 * p_line->i_blue + 128) >> 8) + 128;
372 fmt.p_palette->i_entries = 16;
373 for( i = 0; i < 8; i++ )
375 fmt.p_palette->palette[i][0] = 0;
376 fmt.p_palette->palette[i][1] = 0x80;
377 fmt.p_palette->palette[i][2] = 0x80;
378 fmt.p_palette->palette[i][3] = pi_gamma[i];
379 fmt.p_palette->palette[i][3] =
380 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
382 for( i = 8; i < fmt.p_palette->i_entries; i++ )
384 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
385 fmt.p_palette->palette[i][1] = i_u;
386 fmt.p_palette->palette[i][2] = i_v;
387 fmt.p_palette->palette[i][3] = pi_gamma[i];
388 fmt.p_palette->palette[i][3] =
389 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
392 p_dst = p_region->picture.Y_PIXELS;
393 i_pitch = p_region->picture.Y_PITCH;
395 /* Initialize the region pixels */
396 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
398 for( ; p_line != NULL; p_line = p_line->p_next )
400 int i_glyph_tmax = 0;
401 int i_bitmap_offset, i_offset, i_align_offset = 0;
402 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
404 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
405 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
408 if( p_line->i_width < i_width )
410 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
411 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
413 i_align_offset = i_width - p_line->i_width;
415 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
416 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
418 i_align_offset = ( i_width - p_line->i_width ) / 2;
422 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
424 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
426 i_offset = ( p_line->p_glyph_pos[ i ].y +
427 i_glyph_tmax - p_glyph->top + 2 ) *
428 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
431 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
433 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
435 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
437 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
444 /* Outlining (find something better than nearest neighbour filtering ?) */
447 uint8_t *p_dst = p_region->picture.Y_PIXELS;
448 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
449 uint8_t left, current;
451 for( y = 1; y < (int)fmt.i_height - 1; y++ )
453 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
454 p_dst += p_region->picture.Y_PITCH;
457 for( x = 1; x < (int)fmt.i_width - 1; x++ )
460 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
461 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;
465 memset( p_top, 0, fmt.i_width );
471 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
473 uint8_t *p_dst = p_region->picture.A_PIXELS;
474 int i_pitch = p_region->picture.A_PITCH;
477 for( ; p_line != NULL; p_line = p_line->p_next )
479 int i_glyph_tmax=0, i = 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 + 3 + yoffset ) *
507 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
508 i_align_offset +xoffset;
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] )
515 if( p_dst[i_offset+x] <
516 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
518 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
527 /*****************************************************************************
528 * Render: place string in picture
529 *****************************************************************************
530 * This function merges the previously rendered freetype glyphs into a picture
531 *****************************************************************************/
532 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
533 line_desc_t *p_line, int i_width, int i_height )
535 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
537 int i, x, y, i_pitch, i_alpha;
538 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
539 subpicture_region_t *p_region_tmp;
541 if( i_width == 0 || i_height == 0 )
544 /* Create a new subpicture region */
545 memset( &fmt, 0, sizeof(video_format_t) );
546 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
548 fmt.i_width = fmt.i_visible_width = i_width + 6;
549 fmt.i_height = fmt.i_visible_height = i_height + 6;
550 fmt.i_x_offset = fmt.i_y_offset = 0;
551 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
554 msg_Err( p_filter, "cannot allocate SPU region" );
558 p_region->fmt = p_region_tmp->fmt;
559 p_region->picture = p_region_tmp->picture;
560 free( p_region_tmp );
562 /* Calculate text color components */
563 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
564 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
565 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
566 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
567 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
568 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
569 i_alpha = p_line->i_alpha;
571 p_dst_y = p_region->picture.Y_PIXELS;
572 p_dst_u = p_region->picture.U_PIXELS;
573 p_dst_v = p_region->picture.V_PIXELS;
574 p_dst_a = p_region->picture.A_PIXELS;
575 i_pitch = p_region->picture.A_PITCH;
577 /* Initialize the region pixels */
578 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
580 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
581 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
582 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
583 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
587 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
588 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
589 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
590 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
592 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
593 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
595 DrawBlack( p_line, i_width, p_region, 0, 0);
596 DrawBlack( p_line, i_width, p_region, -1, 0);
597 DrawBlack( p_line, i_width, p_region, 0, -1);
598 DrawBlack( p_line, i_width, p_region, 1, 0);
599 DrawBlack( p_line, i_width, p_region, 0, 1);
602 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
604 DrawBlack( p_line, i_width, p_region, -1, -1);
605 DrawBlack( p_line, i_width, p_region, -1, 1);
606 DrawBlack( p_line, i_width, p_region, 1, -1);
607 DrawBlack( p_line, i_width, p_region, 1, 1);
609 DrawBlack( p_line, i_width, p_region, -2, 0);
610 DrawBlack( p_line, i_width, p_region, 0, -2);
611 DrawBlack( p_line, i_width, p_region, 2, 0);
612 DrawBlack( p_line, i_width, p_region, 0, 2);
614 DrawBlack( p_line, i_width, p_region, -2, -2);
615 DrawBlack( p_line, i_width, p_region, -2, 2);
616 DrawBlack( p_line, i_width, p_region, 2, -2);
617 DrawBlack( p_line, i_width, p_region, 2, 2);
619 DrawBlack( p_line, i_width, p_region, -3, 0);
620 DrawBlack( p_line, i_width, p_region, 0, -3);
621 DrawBlack( p_line, i_width, p_region, 3, 0);
622 DrawBlack( p_line, i_width, p_region, 0, 3);
625 for( ; p_line != NULL; p_line = p_line->p_next )
627 int i_glyph_tmax = 0;
628 int i_bitmap_offset, i_offset, i_align_offset = 0;
629 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
631 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
632 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
635 if( p_line->i_width < i_width )
637 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
638 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
640 i_align_offset = i_width - p_line->i_width;
642 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
643 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
645 i_align_offset = ( i_width - p_line->i_width ) / 2;
649 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
651 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
653 i_offset = ( p_line->p_glyph_pos[ i ].y +
654 i_glyph_tmax - p_glyph->top + 3 ) *
655 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
658 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
660 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
662 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
664 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
665 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
667 p_dst_u[i_offset+x] = i_u;
668 p_dst_v[i_offset+x] = i_v;
670 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
671 p_dst_a[i_offset+x] = 0xff;
679 /* Apply the alpha setting */
680 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
681 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
687 * This function renders a text subpicture region into another one.
688 * It also calculates the size needed for this string, and renders the
689 * needed glyphs into memory. It is used as pf_add_string callback in
690 * the vout method by this module
692 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
693 subpicture_region_t *p_region_in )
695 filter_sys_t *p_sys = p_filter->p_sys;
696 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
697 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
698 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
701 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
702 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
710 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
711 psz_string = p_region_in->psz_text;
712 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
714 if( p_region_in->p_style )
716 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
717 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
718 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
722 i_font_color = p_sys->i_font_color;
723 i_font_alpha = 255 - p_sys->i_font_opacity;
724 i_font_size = p_sys->i_default_font_size;
727 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
728 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
729 SetFontSize( p_filter, i_font_size );
731 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
732 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
733 i_blue = i_font_color & 0x000000FF;
735 result.x = result.y = 0;
736 line.xMin = line.xMax = line.yMin = line.yMax = 0;
738 psz_unicode = psz_unicode_orig =
739 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
740 if( psz_unicode == NULL )
742 msg_Err( p_filter, "out of memory" );
745 #if defined(WORDS_BIGENDIAN)
746 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
748 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
750 if( iconv_handle == (vlc_iconv_t)-1 )
752 msg_Warn( p_filter, "unable to do conversion" );
758 const char *p_in_buffer = psz_string;
759 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
760 i_in_bytes = strlen( psz_string );
761 i_out_bytes = i_in_bytes * sizeof( uint32_t );
762 i_out_bytes_left = i_out_bytes;
763 p_out_buffer = (char *)psz_unicode;
764 i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
765 &p_out_buffer, &i_out_bytes_left );
767 vlc_iconv_close( iconv_handle );
771 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
772 "bytes left %d", strerror(errno), (int)i_in_bytes );
775 *(uint32_t*)p_out_buffer = 0;
776 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
779 #if defined(HAVE_FRIBIDI)
781 uint32_t *p_fribidi_string;
782 int start_pos, pos = 0;
784 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
786 /* Do bidi conversion line-by-line */
787 while(pos < i_string_length)
789 while(pos < i_string_length) {
790 i_char = psz_unicode[pos];
791 if (i_char != '\r' && i_char != '\n')
793 p_fribidi_string[pos] = i_char;
797 while(pos < i_string_length) {
798 i_char = psz_unicode[pos];
799 if (i_char == '\r' || i_char == '\n')
805 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
806 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
807 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
811 free( psz_unicode_orig );
812 psz_unicode = psz_unicode_orig = p_fribidi_string;
813 p_fribidi_string[ i_string_length ] = 0;
817 /* Calculate relative glyph positions and a bounding box for the
819 if( !(p_line = NewLine( (byte_t *)psz_string )) )
821 msg_Err( p_filter, "out of memory" );
825 i_pen_x = i_pen_y = 0;
827 psz_line_start = psz_unicode;
829 #define face p_sys->p_face
830 #define glyph face->glyph
832 while( *psz_unicode )
834 i_char = *psz_unicode++;
835 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
842 psz_line_start = psz_unicode;
843 if( !(p_next = NewLine( (byte_t *)psz_string )) )
845 msg_Err( p_filter, "out of memory" );
848 p_line->p_next = p_next;
849 p_line->i_width = line.xMax;
850 p_line->i_height = face->size->metrics.height >> 6;
851 p_line->pp_glyphs[ i ] = NULL;
852 p_line->i_alpha = i_font_alpha;
853 p_line->i_red = i_red;
854 p_line->i_green = i_green;
855 p_line->i_blue = i_blue;
858 result.x = __MAX( result.x, line.xMax );
859 result.y += face->size->metrics.height >> 6;
862 line.xMin = line.xMax = line.yMin = line.yMax = 0;
863 i_pen_y += face->size->metrics.height >> 6;
865 msg_Dbg( p_filter, "Creating new line, i is %d", i );
870 i_glyph_index = FT_Get_Char_Index( face, i_char );
871 if( p_sys->i_use_kerning && i_glyph_index
875 FT_Get_Kerning( face, i_previous, i_glyph_index,
876 ft_kerning_default, &delta );
877 i_pen_x += delta.x >> 6;
880 p_line->p_glyph_pos[ i ].x = i_pen_x;
881 p_line->p_glyph_pos[ i ].y = i_pen_y;
882 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
885 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
889 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
892 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
896 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
897 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
900 FT_Done_Glyph( tmp_glyph );
903 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
906 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
907 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
908 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
910 p_line->pp_glyphs[ i ] = NULL;
912 p_line = NewLine( (byte_t *)psz_string );
913 if( p_prev ) p_prev->p_next = p_line;
914 else p_lines = p_line;
916 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
920 if( psz_unicode == psz_line_start )
922 msg_Warn( p_filter, "unbreakable string" );
929 psz_unicode = psz_line_start;
932 line.xMin = line.xMax = line.yMin = line.yMax = 0;
935 line.yMax = __MAX( line.yMax, glyph_size.yMax );
936 line.yMin = __MIN( line.yMin, glyph_size.yMin );
938 i_previous = i_glyph_index;
939 i_pen_x += glyph->advance.x >> 6;
943 p_line->i_width = line.xMax;
944 p_line->i_height = face->size->metrics.height >> 6;
945 p_line->pp_glyphs[ i ] = NULL;
946 p_line->i_alpha = i_font_alpha;
947 p_line->i_red = i_red;
948 p_line->i_green = i_green;
949 p_line->i_blue = i_blue;
950 result.x = __MAX( result.x, line.xMax );
951 result.y += line.yMax - line.yMin;
956 p_region_out->i_x = p_region_in->i_x;
957 p_region_out->i_y = p_region_in->i_y;
959 if( config_GetInt( p_filter, "freetype-yuvp" ) )
960 Render( p_filter, p_region_out, p_lines, result.x, result.y );
962 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
964 if( psz_unicode_orig ) free( psz_unicode_orig );
965 FreeLines( p_lines );
969 if( psz_unicode_orig ) free( psz_unicode_orig );
970 FreeLines( p_lines );
974 static void FreeLine( line_desc_t *p_line )
977 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
979 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
981 free( p_line->pp_glyphs );
982 free( p_line->p_glyph_pos );
986 static void FreeLines( line_desc_t *p_lines )
988 line_desc_t *p_line, *p_next;
990 if( !p_lines ) return;
992 for( p_line = p_lines; p_line != NULL; p_line = p_next )
994 p_next = p_line->p_next;
999 static line_desc_t *NewLine( byte_t *psz_string )
1002 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1004 if( !p_line ) return NULL;
1005 p_line->i_height = 0;
1006 p_line->i_width = 0;
1007 p_line->p_next = NULL;
1009 /* We don't use CountUtf8Characters() here because we are not acutally
1010 * sure the string is utf8. Better be safe than sorry. */
1011 i_count = strlen( (char *)psz_string );
1013 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1014 if( p_line->pp_glyphs == NULL )
1019 p_line->pp_glyphs[0] = NULL;
1021 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1022 if( p_line->p_glyph_pos == NULL )
1024 free( p_line->pp_glyphs );
1032 static int SetFontSize( filter_t *p_filter, int i_size )
1034 filter_sys_t *p_sys = p_filter->p_sys;
1036 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1042 if( !p_sys->i_default_font_size &&
1043 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1046 if( p_sys->i_default_font_size )
1048 i_size = p_sys->i_default_font_size;
1052 var_Get( p_filter, "freetype-rel-fontsize", &val );
1053 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1054 p_filter->p_sys->i_display_height =
1055 p_filter->fmt_out.video.i_height;
1059 msg_Warn( p_filter, "invalid fontsize, using 12" );
1063 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1066 p_sys->i_font_size = i_size;
1068 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1070 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1071 return VLC_EGENERIC;