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 rendering." )
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_region->p_style && p_line->i_width < i_width )
410 if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
412 i_align_offset = i_width - p_line->i_width;
414 else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
416 i_align_offset = ( i_width - p_line->i_width ) / 2;
420 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
422 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
424 i_offset = ( p_line->p_glyph_pos[ i ].y +
425 i_glyph_tmax - p_glyph->top + 2 ) *
426 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
429 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
431 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
433 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
435 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
442 /* Outlining (find something better than nearest neighbour filtering ?) */
445 uint8_t *p_dst = p_region->picture.Y_PIXELS;
446 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
447 uint8_t left, current;
449 for( y = 1; y < (int)fmt.i_height - 1; y++ )
451 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
452 p_dst += p_region->picture.Y_PITCH;
455 for( x = 1; x < (int)fmt.i_width - 1; x++ )
458 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
459 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;
463 memset( p_top, 0, fmt.i_width );
469 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
471 uint8_t *p_dst = p_region->picture.A_PIXELS;
472 int i_pitch = p_region->picture.A_PITCH;
475 for( ; p_line != NULL; p_line = p_line->p_next )
477 int i_glyph_tmax=0, i = 0;
478 int i_bitmap_offset, i_offset, i_align_offset = 0;
479 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
481 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
482 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
485 if( p_region->p_style && p_line->i_width < i_width )
487 if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
489 i_align_offset = i_width - p_line->i_width;
491 else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
493 i_align_offset = ( i_width - p_line->i_width ) / 2;
497 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
499 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
501 i_offset = ( p_line->p_glyph_pos[ i ].y +
502 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
503 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
504 i_align_offset +xoffset;
506 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
508 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
510 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
511 if( p_dst[i_offset+x] <
512 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
514 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
523 /*****************************************************************************
524 * Render: place string in picture
525 *****************************************************************************
526 * This function merges the previously rendered freetype glyphs into a picture
527 *****************************************************************************/
528 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
529 line_desc_t *p_line, int i_width, int i_height )
531 static uint8_t pi_gamma[16] =
532 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
533 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
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_region->p_style && p_line->i_width < i_width )
637 if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
639 i_align_offset = i_width - p_line->i_width;
641 else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
643 i_align_offset = ( i_width - p_line->i_width ) / 2;
647 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
649 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
651 i_offset = ( p_line->p_glyph_pos[ i ].y +
652 i_glyph_tmax - p_glyph->top + 3 ) *
653 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
656 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
658 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
660 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
662 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
663 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
665 p_dst_u[i_offset+x] = i_u;
666 p_dst_v[i_offset+x] = i_v;
668 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
669 p_dst_a[i_offset+x] = 0xff;
677 /* Apply the alpha setting */
678 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
679 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
685 * This function renders a text subpicture region into another one.
686 * It also calculates the size needed for this string, and renders the
687 * needed glyphs into memory. It is used as pf_add_string callback in
688 * the vout method by this module
690 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
691 subpicture_region_t *p_region_in )
693 filter_sys_t *p_sys = p_filter->p_sys;
694 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
695 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
696 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
699 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
700 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
708 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
709 psz_string = p_region_in->psz_text;
710 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
712 if( p_region_in->p_style )
714 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
715 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
716 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
720 i_font_color = p_sys->i_font_color;
721 i_font_alpha = 255 - p_sys->i_font_opacity;
722 i_font_size = p_sys->i_default_font_size;
725 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
726 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
727 SetFontSize( p_filter, i_font_size );
729 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
730 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
731 i_blue = i_font_color & 0x000000FF;
733 result.x = result.y = 0;
734 line.xMin = line.xMax = line.yMin = line.yMax = 0;
736 psz_unicode = psz_unicode_orig =
737 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
738 if( psz_unicode == NULL )
740 msg_Err( p_filter, "out of memory" );
743 #if defined(WORDS_BIGENDIAN)
744 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
746 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
748 if( iconv_handle == (vlc_iconv_t)-1 )
750 msg_Warn( p_filter, "unable to do conversion" );
755 char *p_in_buffer, *p_out_buffer;
756 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
757 i_in_bytes = strlen( psz_string );
758 i_out_bytes = i_in_bytes * sizeof( uint32_t );
759 i_out_bytes_left = i_out_bytes;
760 p_in_buffer = psz_string;
761 p_out_buffer = (char *)psz_unicode;
762 i_ret = vlc_iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
763 &p_out_buffer, &i_out_bytes_left );
765 vlc_iconv_close( iconv_handle );
769 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
770 "bytes left %d", strerror(errno), (int)i_in_bytes );
773 *(uint32_t*)p_out_buffer = 0;
774 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
777 #if defined(HAVE_FRIBIDI)
779 uint32_t *p_fribidi_string;
780 int start_pos, pos = 0;
782 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
784 /* Do bidi conversion line-by-line */
785 while(pos < i_string_length)
787 while(pos < i_string_length) {
788 i_char = psz_unicode[pos];
789 if (i_char != '\r' && i_char != '\n')
791 p_fribidi_string[pos] = i_char;
795 while(pos < i_string_length) {
796 i_char = psz_unicode[pos];
797 if (i_char == '\r' || i_char == '\n')
803 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
804 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
805 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
809 free( psz_unicode_orig );
810 psz_unicode = psz_unicode_orig = p_fribidi_string;
811 p_fribidi_string[ i_string_length ] = 0;
815 /* Calculate relative glyph positions and a bounding box for the
817 if( !(p_line = NewLine( (byte_t *)psz_string )) )
819 msg_Err( p_filter, "out of memory" );
823 i_pen_x = i_pen_y = 0;
825 psz_line_start = psz_unicode;
827 #define face p_sys->p_face
828 #define glyph face->glyph
830 while( *psz_unicode )
832 i_char = *psz_unicode++;
833 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
840 psz_line_start = psz_unicode;
841 if( !(p_next = NewLine( (byte_t *)psz_string )) )
843 msg_Err( p_filter, "out of memory" );
846 p_line->p_next = p_next;
847 p_line->i_width = line.xMax;
848 p_line->i_height = face->size->metrics.height >> 6;
849 p_line->pp_glyphs[ i ] = NULL;
850 p_line->i_alpha = i_font_alpha;
851 p_line->i_red = i_red;
852 p_line->i_green = i_green;
853 p_line->i_blue = i_blue;
856 result.x = __MAX( result.x, line.xMax );
857 result.y += face->size->metrics.height >> 6;
860 line.xMin = line.xMax = line.yMin = line.yMax = 0;
861 i_pen_y += face->size->metrics.height >> 6;
863 msg_Dbg( p_filter, "Creating new line, i is %d", i );
868 i_glyph_index = FT_Get_Char_Index( face, i_char );
869 if( p_sys->i_use_kerning && i_glyph_index
873 FT_Get_Kerning( face, i_previous, i_glyph_index,
874 ft_kerning_default, &delta );
875 i_pen_x += delta.x >> 6;
878 p_line->p_glyph_pos[ i ].x = i_pen_x;
879 p_line->p_glyph_pos[ i ].y = i_pen_y;
880 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
883 msg_Err( p_filter, "Unable to render text (FT_Load_Glyph returned"
887 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
890 msg_Err( p_filter, "Unable to render text (FT_Get_Glyph returned "
894 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
895 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
898 FT_Done_Glyph( tmp_glyph );
901 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
904 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
905 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
906 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
908 p_line->pp_glyphs[ i ] = NULL;
910 p_line = NewLine( (byte_t *)psz_string );
911 if( p_prev ) p_prev->p_next = p_line;
912 else p_lines = p_line;
914 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
918 if( psz_unicode == psz_line_start )
920 msg_Warn( p_filter, "unbreakable string" );
927 psz_unicode = psz_line_start;
930 line.xMin = line.xMax = line.yMin = line.yMax = 0;
933 line.yMax = __MAX( line.yMax, glyph_size.yMax );
934 line.yMin = __MIN( line.yMin, glyph_size.yMin );
936 i_previous = i_glyph_index;
937 i_pen_x += glyph->advance.x >> 6;
941 p_line->i_width = line.xMax;
942 p_line->i_height = face->size->metrics.height >> 6;
943 p_line->pp_glyphs[ i ] = NULL;
944 p_line->i_alpha = i_font_alpha;
945 p_line->i_red = i_red;
946 p_line->i_green = i_green;
947 p_line->i_blue = i_blue;
948 result.x = __MAX( result.x, line.xMax );
949 result.y += line.yMax - line.yMin;
954 p_region_out->i_x = p_region_in->i_x;
955 p_region_out->i_y = p_region_in->i_y;
957 if( config_GetInt( p_filter, "freetype-yuvp" ) )
958 Render( p_filter, p_region_out, p_lines, result.x, result.y );
960 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
962 if( psz_unicode_orig ) free( psz_unicode_orig );
963 FreeLines( p_lines );
967 if( psz_unicode_orig ) free( psz_unicode_orig );
968 FreeLines( p_lines );
972 static void FreeLine( line_desc_t *p_line )
975 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
977 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
979 free( p_line->pp_glyphs );
980 free( p_line->p_glyph_pos );
984 static void FreeLines( line_desc_t *p_lines )
986 line_desc_t *p_line, *p_next;
988 if( !p_lines ) return;
990 for( p_line = p_lines; p_line != NULL; p_line = p_next )
992 p_next = p_line->p_next;
997 static line_desc_t *NewLine( byte_t *psz_string )
1000 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1002 if( !p_line ) return NULL;
1003 p_line->i_height = 0;
1004 p_line->i_width = 0;
1005 p_line->p_next = NULL;
1007 /* We don't use CountUtf8Characters() here because we are not acutally
1008 * sure the string is utf8. Better be safe than sorry. */
1009 i_count = strlen( (char *)psz_string );
1011 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1012 if( p_line->pp_glyphs == NULL )
1017 p_line->pp_glyphs[0] = NULL;
1019 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1020 if( p_line->p_glyph_pos == NULL )
1022 free( p_line->pp_glyphs );
1030 static int SetFontSize( filter_t *p_filter, int i_size )
1032 filter_sys_t *p_sys = p_filter->p_sys;
1034 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1040 if( !p_sys->i_default_font_size &&
1041 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1044 if( p_sys->i_default_font_size )
1046 i_size = p_sys->i_default_font_size;
1050 var_Get( p_filter, "freetype-rel-fontsize", &val );
1051 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1052 p_filter->p_sys->i_display_height =
1053 p_filter->fmt_out.video.i_height;
1057 msg_Warn( p_filter, "Invalid fontsize, using 12" );
1061 msg_Dbg( p_filter, "Using fontsize: %i", i_size );
1064 p_sys->i_font_size = i_size;
1066 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1068 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1069 return VLC_EGENERIC;