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 /// \bug [String] Missing space
112 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered" \
113 "text to improve its readability." )
115 #define EFFECT_BACKGROUND 1
116 #define EFFECT_OUTLINE 2
117 #define EFFECT_OUTLINE_FAT 3
119 static int pi_effects[] = { 1, 2, 3 };
120 static char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
122 static int pi_color_values[] = {
123 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
124 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
125 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
127 static char *ppsz_color_descriptions[] = {
128 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
129 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
130 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
133 set_shortname( _("Text renderer"));
134 set_description( _("Freetype2 font renderer") );
135 set_category( CAT_VIDEO );
136 set_subcategory( SUBCAT_VIDEO_SUBPIC );
138 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
141 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
142 FONTSIZE_LONGTEXT, VLC_TRUE );
144 /* opacity valid on 0..255, with default 255 = fully opaque */
145 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
146 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
148 /* hook to the color values list, with default 0x00ffffff = white */
149 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
150 COLOR_LONGTEXT, VLC_FALSE );
151 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
153 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
154 FONTSIZER_LONGTEXT, VLC_FALSE );
155 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
156 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
157 EFFECT_LONGTEXT, VLC_FALSE );
158 change_integer_list( pi_effects, ppsz_effects_text, 0 );
160 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
161 YUVP_LONGTEXT, VLC_TRUE );
162 set_capability( "text renderer", 100 );
163 add_shortcut( "text" );
164 set_callbacks( Create, Destroy );
169 /** NULL-terminated list of glyphs making the string */
170 FT_BitmapGlyph *pp_glyphs;
171 /** list of relative positions for the glyphs */
172 FT_Vector *p_glyph_pos;
176 int i_red, i_green, i_blue;
182 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
183 static void FreeLines( line_desc_t * );
184 static void FreeLine( line_desc_t * );
186 /*****************************************************************************
187 * filter_sys_t: freetype local data
188 *****************************************************************************
189 * This structure is part of the video output thread descriptor.
190 * It describes the freetype specific properties of an output thread.
191 *****************************************************************************/
194 FT_Library p_library; /* handle to library */
195 FT_Face p_face; /* handle to face object */
196 vlc_bool_t i_use_kerning;
197 uint8_t i_font_opacity;
202 int i_default_font_size;
203 int i_display_height;
206 /*****************************************************************************
207 * Create: allocates osd-text video thread output method
208 *****************************************************************************
209 * This function allocates and initializes a Clone vout method.
210 *****************************************************************************/
211 static int Create( vlc_object_t *p_this )
213 filter_t *p_filter = (filter_t *)p_this;
215 char *psz_fontfile = NULL;
219 /* Allocate structure */
220 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
223 msg_Err( p_filter, "out of memory" );
227 p_sys->p_library = 0;
228 p_sys->i_font_size = 0;
229 p_sys->i_display_height = 0;
231 var_Create( p_filter, "freetype-font",
232 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
233 var_Create( p_filter, "freetype-fontsize",
234 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
235 var_Create( p_filter, "freetype-rel-fontsize",
236 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
237 var_Create( p_filter, "freetype-opacity",
238 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
239 var_Create( p_filter, "freetype-effect",
240 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
241 var_Get( p_filter, "freetype-opacity", &val );
242 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
243 var_Create( p_filter, "freetype-color",
244 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
245 var_Get( p_filter, "freetype-color", &val );
246 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
247 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
249 /* Look what method was requested */
250 var_Get( p_filter, "freetype-font", &val );
251 psz_fontfile = val.psz_string;
252 if( !psz_fontfile || !*psz_fontfile )
254 if( psz_fontfile ) free( psz_fontfile );
255 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
257 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
258 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
260 strcpy( psz_fontfile, DEFAULT_FONT );
262 msg_Err( p_filter, "user didn't specify a font" );
267 i_error = FT_Init_FreeType( &p_sys->p_library );
270 msg_Err( p_filter, "couldn't initialize freetype" );
274 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
276 if( i_error == FT_Err_Unknown_File_Format )
278 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
283 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
287 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
290 msg_Err( p_filter, "font has no unicode translation table" );
294 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
296 var_Get( p_filter, "freetype-fontsize", &val );
297 p_sys->i_default_font_size = val.i_int;
298 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
300 if( psz_fontfile ) free( psz_fontfile );
301 p_filter->pf_render_text = RenderText;
305 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
306 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
307 if( psz_fontfile ) free( psz_fontfile );
312 /*****************************************************************************
313 * Destroy: destroy Clone video thread output method
314 *****************************************************************************
315 * Clean up all data and library connections
316 *****************************************************************************/
317 static void Destroy( vlc_object_t *p_this )
319 filter_t *p_filter = (filter_t *)p_this;
320 filter_sys_t *p_sys = p_filter->p_sys;
322 FT_Done_Face( p_sys->p_face );
323 FT_Done_FreeType( p_sys->p_library );
327 /*****************************************************************************
328 * Render: place string in picture
329 *****************************************************************************
330 * This function merges the previously rendered freetype glyphs into a picture
331 *****************************************************************************/
332 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
333 line_desc_t *p_line, int i_width, int i_height )
335 static uint8_t pi_gamma[16] =
336 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
337 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
341 int i, x, y, i_pitch;
342 uint8_t i_y; /* YUV values, derived from incoming RGB */
344 subpicture_region_t *p_region_tmp;
346 /* Create a new subpicture region */
347 memset( &fmt, 0, sizeof(video_format_t) );
348 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
350 fmt.i_width = fmt.i_visible_width = i_width + 4;
351 fmt.i_height = fmt.i_visible_height = i_height + 4;
352 fmt.i_x_offset = fmt.i_y_offset = 0;
353 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
356 msg_Err( p_filter, "cannot allocate SPU region" );
360 p_region->fmt = p_region_tmp->fmt;
361 p_region->picture = p_region_tmp->picture;
362 free( p_region_tmp );
364 /* Calculate text color components */
365 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
366 25 * p_line->i_blue + 128) >> 8) + 16;
367 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
368 112 * p_line->i_blue + 128) >> 8) + 128;
369 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
370 18 * p_line->i_blue + 128) >> 8) + 128;
373 fmt.p_palette->i_entries = 16;
374 for( i = 0; i < 8; i++ )
376 fmt.p_palette->palette[i][0] = 0;
377 fmt.p_palette->palette[i][1] = 0x80;
378 fmt.p_palette->palette[i][2] = 0x80;
379 fmt.p_palette->palette[i][3] = pi_gamma[i];
380 fmt.p_palette->palette[i][3] =
381 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
383 for( i = 8; i < fmt.p_palette->i_entries; i++ )
385 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
386 fmt.p_palette->palette[i][1] = i_u;
387 fmt.p_palette->palette[i][2] = i_v;
388 fmt.p_palette->palette[i][3] = pi_gamma[i];
389 fmt.p_palette->palette[i][3] =
390 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
393 p_dst = p_region->picture.Y_PIXELS;
394 i_pitch = p_region->picture.Y_PITCH;
396 /* Initialize the region pixels */
397 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
399 for( ; p_line != NULL; p_line = p_line->p_next )
401 int i_glyph_tmax = 0;
402 int i_bitmap_offset, i_offset, i_align_offset = 0;
403 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
405 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
406 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
409 if( p_line->i_width < i_width )
411 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
412 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
414 i_align_offset = i_width - p_line->i_width;
416 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
417 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
419 i_align_offset = ( i_width - p_line->i_width ) / 2;
423 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
425 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
427 i_offset = ( p_line->p_glyph_pos[ i ].y +
428 i_glyph_tmax - p_glyph->top + 2 ) *
429 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
432 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
434 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
436 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
438 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
445 /* Outlining (find something better than nearest neighbour filtering ?) */
448 uint8_t *p_dst = p_region->picture.Y_PIXELS;
449 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
450 uint8_t left, current;
452 for( y = 1; y < (int)fmt.i_height - 1; y++ )
454 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
455 p_dst += p_region->picture.Y_PITCH;
458 for( x = 1; x < (int)fmt.i_width - 1; x++ )
461 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
462 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;
466 memset( p_top, 0, fmt.i_width );
472 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
474 uint8_t *p_dst = p_region->picture.A_PIXELS;
475 int i_pitch = p_region->picture.A_PITCH;
478 for( ; p_line != NULL; p_line = p_line->p_next )
480 int i_glyph_tmax=0, i = 0;
481 int i_bitmap_offset, i_offset, i_align_offset = 0;
482 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
484 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
485 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
488 if( p_line->i_width < i_width )
490 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
491 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
493 i_align_offset = i_width - p_line->i_width;
495 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
496 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
498 i_align_offset = ( i_width - p_line->i_width ) / 2;
502 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
504 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
506 i_offset = ( p_line->p_glyph_pos[ i ].y +
507 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
508 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
509 i_align_offset +xoffset;
511 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
513 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
515 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
516 if( p_dst[i_offset+x] <
517 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
519 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
528 /*****************************************************************************
529 * Render: place string in picture
530 *****************************************************************************
531 * This function merges the previously rendered freetype glyphs into a picture
532 *****************************************************************************/
533 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
534 line_desc_t *p_line, int i_width, int i_height )
536 static uint8_t pi_gamma[16] =
537 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
538 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
540 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
542 int i, x, y, i_pitch, i_alpha;
543 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
544 subpicture_region_t *p_region_tmp;
546 if( i_width == 0 || i_height == 0 )
549 /* Create a new subpicture region */
550 memset( &fmt, 0, sizeof(video_format_t) );
551 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
553 fmt.i_width = fmt.i_visible_width = i_width + 6;
554 fmt.i_height = fmt.i_visible_height = i_height + 6;
555 fmt.i_x_offset = fmt.i_y_offset = 0;
556 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
559 msg_Err( p_filter, "cannot allocate SPU region" );
563 p_region->fmt = p_region_tmp->fmt;
564 p_region->picture = p_region_tmp->picture;
565 free( p_region_tmp );
567 /* Calculate text color components */
568 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
569 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
570 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
571 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
572 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
573 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
574 i_alpha = p_line->i_alpha;
576 p_dst_y = p_region->picture.Y_PIXELS;
577 p_dst_u = p_region->picture.U_PIXELS;
578 p_dst_v = p_region->picture.V_PIXELS;
579 p_dst_a = p_region->picture.A_PIXELS;
580 i_pitch = p_region->picture.A_PITCH;
582 /* Initialize the region pixels */
583 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
585 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
586 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
587 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
588 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
592 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
593 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
594 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
595 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
597 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
598 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
600 DrawBlack( p_line, i_width, p_region, 0, 0);
601 DrawBlack( p_line, i_width, p_region, -1, 0);
602 DrawBlack( p_line, i_width, p_region, 0, -1);
603 DrawBlack( p_line, i_width, p_region, 1, 0);
604 DrawBlack( p_line, i_width, p_region, 0, 1);
607 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
609 DrawBlack( p_line, i_width, p_region, -1, -1);
610 DrawBlack( p_line, i_width, p_region, -1, 1);
611 DrawBlack( p_line, i_width, p_region, 1, -1);
612 DrawBlack( p_line, i_width, p_region, 1, 1);
614 DrawBlack( p_line, i_width, p_region, -2, 0);
615 DrawBlack( p_line, i_width, p_region, 0, -2);
616 DrawBlack( p_line, i_width, p_region, 2, 0);
617 DrawBlack( p_line, i_width, p_region, 0, 2);
619 DrawBlack( p_line, i_width, p_region, -2, -2);
620 DrawBlack( p_line, i_width, p_region, -2, 2);
621 DrawBlack( p_line, i_width, p_region, 2, -2);
622 DrawBlack( p_line, i_width, p_region, 2, 2);
624 DrawBlack( p_line, i_width, p_region, -3, 0);
625 DrawBlack( p_line, i_width, p_region, 0, -3);
626 DrawBlack( p_line, i_width, p_region, 3, 0);
627 DrawBlack( p_line, i_width, p_region, 0, 3);
630 for( ; p_line != NULL; p_line = p_line->p_next )
632 int i_glyph_tmax = 0;
633 int i_bitmap_offset, i_offset, i_align_offset = 0;
634 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
636 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
637 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
640 if( p_line->i_width < i_width )
642 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
643 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
645 i_align_offset = i_width - p_line->i_width;
647 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
648 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
650 i_align_offset = ( i_width - p_line->i_width ) / 2;
654 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
656 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
658 i_offset = ( p_line->p_glyph_pos[ i ].y +
659 i_glyph_tmax - p_glyph->top + 3 ) *
660 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
663 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
665 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
667 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
669 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
670 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
672 p_dst_u[i_offset+x] = i_u;
673 p_dst_v[i_offset+x] = i_v;
675 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
676 p_dst_a[i_offset+x] = 0xff;
684 /* Apply the alpha setting */
685 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
686 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
692 * This function renders a text subpicture region into another one.
693 * It also calculates the size needed for this string, and renders the
694 * needed glyphs into memory. It is used as pf_add_string callback in
695 * the vout method by this module
697 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
698 subpicture_region_t *p_region_in )
700 filter_sys_t *p_sys = p_filter->p_sys;
701 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
702 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
703 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
706 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
707 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
715 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
716 psz_string = p_region_in->psz_text;
717 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
719 if( p_region_in->p_style )
721 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
722 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
723 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
727 i_font_color = p_sys->i_font_color;
728 i_font_alpha = 255 - p_sys->i_font_opacity;
729 i_font_size = p_sys->i_default_font_size;
732 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
733 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
734 SetFontSize( p_filter, i_font_size );
736 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
737 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
738 i_blue = i_font_color & 0x000000FF;
740 result.x = result.y = 0;
741 line.xMin = line.xMax = line.yMin = line.yMax = 0;
743 psz_unicode = psz_unicode_orig =
744 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
745 if( psz_unicode == NULL )
747 msg_Err( p_filter, "out of memory" );
750 #if defined(WORDS_BIGENDIAN)
751 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
753 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
755 if( iconv_handle == (vlc_iconv_t)-1 )
757 msg_Warn( p_filter, "unable to do conversion" );
762 char *p_in_buffer, *p_out_buffer;
763 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
764 i_in_bytes = strlen( psz_string );
765 i_out_bytes = i_in_bytes * sizeof( uint32_t );
766 i_out_bytes_left = i_out_bytes;
767 p_in_buffer = psz_string;
768 p_out_buffer = (char *)psz_unicode;
769 i_ret = vlc_iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
770 &p_out_buffer, &i_out_bytes_left );
772 vlc_iconv_close( iconv_handle );
776 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
777 "bytes left %d", strerror(errno), (int)i_in_bytes );
780 *(uint32_t*)p_out_buffer = 0;
781 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
784 #if defined(HAVE_FRIBIDI)
786 uint32_t *p_fribidi_string;
787 int start_pos, pos = 0;
789 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
791 /* Do bidi conversion line-by-line */
792 while(pos < i_string_length)
794 while(pos < i_string_length) {
795 i_char = psz_unicode[pos];
796 if (i_char != '\r' && i_char != '\n')
798 p_fribidi_string[pos] = i_char;
802 while(pos < i_string_length) {
803 i_char = psz_unicode[pos];
804 if (i_char == '\r' || i_char == '\n')
810 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
811 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
812 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
816 free( psz_unicode_orig );
817 psz_unicode = psz_unicode_orig = p_fribidi_string;
818 p_fribidi_string[ i_string_length ] = 0;
822 /* Calculate relative glyph positions and a bounding box for the
824 if( !(p_line = NewLine( (byte_t *)psz_string )) )
826 msg_Err( p_filter, "out of memory" );
830 i_pen_x = i_pen_y = 0;
832 psz_line_start = psz_unicode;
834 #define face p_sys->p_face
835 #define glyph face->glyph
837 while( *psz_unicode )
839 i_char = *psz_unicode++;
840 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
847 psz_line_start = psz_unicode;
848 if( !(p_next = NewLine( (byte_t *)psz_string )) )
850 msg_Err( p_filter, "out of memory" );
853 p_line->p_next = p_next;
854 p_line->i_width = line.xMax;
855 p_line->i_height = face->size->metrics.height >> 6;
856 p_line->pp_glyphs[ i ] = NULL;
857 p_line->i_alpha = i_font_alpha;
858 p_line->i_red = i_red;
859 p_line->i_green = i_green;
860 p_line->i_blue = i_blue;
863 result.x = __MAX( result.x, line.xMax );
864 result.y += face->size->metrics.height >> 6;
867 line.xMin = line.xMax = line.yMin = line.yMax = 0;
868 i_pen_y += face->size->metrics.height >> 6;
870 msg_Dbg( p_filter, "Creating new line, i is %d", i );
875 i_glyph_index = FT_Get_Char_Index( face, i_char );
876 if( p_sys->i_use_kerning && i_glyph_index
880 FT_Get_Kerning( face, i_previous, i_glyph_index,
881 ft_kerning_default, &delta );
882 i_pen_x += delta.x >> 6;
885 p_line->p_glyph_pos[ i ].x = i_pen_x;
886 p_line->p_glyph_pos[ i ].y = i_pen_y;
887 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
890 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
894 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
897 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
901 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
902 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
905 FT_Done_Glyph( tmp_glyph );
908 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
911 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
912 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
913 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
915 p_line->pp_glyphs[ i ] = NULL;
917 p_line = NewLine( (byte_t *)psz_string );
918 if( p_prev ) p_prev->p_next = p_line;
919 else p_lines = p_line;
921 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
925 if( psz_unicode == psz_line_start )
927 msg_Warn( p_filter, "unbreakable string" );
934 psz_unicode = psz_line_start;
937 line.xMin = line.xMax = line.yMin = line.yMax = 0;
940 line.yMax = __MAX( line.yMax, glyph_size.yMax );
941 line.yMin = __MIN( line.yMin, glyph_size.yMin );
943 i_previous = i_glyph_index;
944 i_pen_x += glyph->advance.x >> 6;
948 p_line->i_width = line.xMax;
949 p_line->i_height = face->size->metrics.height >> 6;
950 p_line->pp_glyphs[ i ] = NULL;
951 p_line->i_alpha = i_font_alpha;
952 p_line->i_red = i_red;
953 p_line->i_green = i_green;
954 p_line->i_blue = i_blue;
955 result.x = __MAX( result.x, line.xMax );
956 result.y += line.yMax - line.yMin;
961 p_region_out->i_x = p_region_in->i_x;
962 p_region_out->i_y = p_region_in->i_y;
964 if( config_GetInt( p_filter, "freetype-yuvp" ) )
965 Render( p_filter, p_region_out, p_lines, result.x, result.y );
967 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
969 if( psz_unicode_orig ) free( psz_unicode_orig );
970 FreeLines( p_lines );
974 if( psz_unicode_orig ) free( psz_unicode_orig );
975 FreeLines( p_lines );
979 static void FreeLine( line_desc_t *p_line )
982 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
984 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
986 free( p_line->pp_glyphs );
987 free( p_line->p_glyph_pos );
991 static void FreeLines( line_desc_t *p_lines )
993 line_desc_t *p_line, *p_next;
995 if( !p_lines ) return;
997 for( p_line = p_lines; p_line != NULL; p_line = p_next )
999 p_next = p_line->p_next;
1004 static line_desc_t *NewLine( byte_t *psz_string )
1007 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1009 if( !p_line ) return NULL;
1010 p_line->i_height = 0;
1011 p_line->i_width = 0;
1012 p_line->p_next = NULL;
1014 /* We don't use CountUtf8Characters() here because we are not acutally
1015 * sure the string is utf8. Better be safe than sorry. */
1016 i_count = strlen( (char *)psz_string );
1018 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1019 if( p_line->pp_glyphs == NULL )
1024 p_line->pp_glyphs[0] = NULL;
1026 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1027 if( p_line->p_glyph_pos == NULL )
1029 free( p_line->pp_glyphs );
1037 static int SetFontSize( filter_t *p_filter, int i_size )
1039 filter_sys_t *p_sys = p_filter->p_sys;
1041 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1047 if( !p_sys->i_default_font_size &&
1048 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1051 if( p_sys->i_default_font_size )
1053 i_size = p_sys->i_default_font_size;
1057 var_Get( p_filter, "freetype-rel-fontsize", &val );
1058 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1059 p_filter->p_sys->i_display_height =
1060 p_filter->fmt_out.video.i_height;
1064 msg_Warn( p_filter, "invalid fontsize, using 12" );
1068 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1071 p_sys->i_font_size = i_size;
1073 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1075 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1076 return VLC_EGENERIC;