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 /// \bug [String] Extra space
87 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
88 "that will be rendered on the video. " \
89 "If set to something different than 0 this option will override the " \
90 "relative font size. " )
91 #define OPACITY_TEXT N_("Opacity")
92 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
93 "text that will be rendered on the video. 0 = transparent, " \
94 "255 = totally opaque. " )
95 #define COLOR_TEXT N_("Text default color")
96 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
97 "the video. This must be an hexadecimal (like HTML colors). The first two "\
98 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
99 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
100 #define FONTSIZER_TEXT N_("Relative font size")
101 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
102 "fonts that will be rendered on the video. If absolute font size is set, "\
103 "relative size will be overriden." )
105 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
106 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
107 N_("Large"), N_("Larger") };
108 #define YUVP_TEXT N_("Use YUVP renderer")
109 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
110 "This option is only needed if you want to encode into DVB subtitles" )
111 #define EFFECT_TEXT N_("Font Effect")
112 /// \bug [String] Missing space
113 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered" \
114 "text to improve its readability." )
116 #define EFFECT_BACKGROUND 1
117 #define EFFECT_OUTLINE 2
118 #define EFFECT_OUTLINE_FAT 3
120 static int pi_effects[] = { 1, 2, 3 };
121 static char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
123 static int pi_color_values[] = {
124 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
125 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
126 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
128 static char *ppsz_color_descriptions[] = {
129 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
130 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
131 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
134 set_shortname( _("Text renderer"));
135 set_description( _("Freetype2 font renderer") );
136 set_category( CAT_VIDEO );
137 set_subcategory( SUBCAT_VIDEO_SUBPIC );
139 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
142 add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
143 FONTSIZE_LONGTEXT, VLC_TRUE );
145 /* opacity valid on 0..255, with default 255 = fully opaque */
146 add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
147 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
149 /* hook to the color values list, with default 0x00ffffff = white */
150 add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
151 COLOR_LONGTEXT, VLC_FALSE );
152 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
154 add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
155 FONTSIZER_LONGTEXT, VLC_FALSE );
156 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
157 add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
158 EFFECT_LONGTEXT, VLC_FALSE );
159 change_integer_list( pi_effects, ppsz_effects_text, 0 );
161 add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
162 YUVP_LONGTEXT, VLC_TRUE );
163 set_capability( "text renderer", 100 );
164 add_shortcut( "text" );
165 set_callbacks( Create, Destroy );
170 /** NULL-terminated list of glyphs making the string */
171 FT_BitmapGlyph *pp_glyphs;
172 /** list of relative positions for the glyphs */
173 FT_Vector *p_glyph_pos;
177 int i_red, i_green, i_blue;
183 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
184 static void FreeLines( line_desc_t * );
185 static void FreeLine( line_desc_t * );
187 /*****************************************************************************
188 * filter_sys_t: freetype local data
189 *****************************************************************************
190 * This structure is part of the video output thread descriptor.
191 * It describes the freetype specific properties of an output thread.
192 *****************************************************************************/
195 FT_Library p_library; /* handle to library */
196 FT_Face p_face; /* handle to face object */
197 vlc_bool_t i_use_kerning;
198 uint8_t i_font_opacity;
203 int i_default_font_size;
204 int i_display_height;
207 /*****************************************************************************
208 * Create: allocates osd-text video thread output method
209 *****************************************************************************
210 * This function allocates and initializes a Clone vout method.
211 *****************************************************************************/
212 static int Create( vlc_object_t *p_this )
214 filter_t *p_filter = (filter_t *)p_this;
216 char *psz_fontfile = NULL;
220 /* Allocate structure */
221 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
224 msg_Err( p_filter, "out of memory" );
228 p_sys->p_library = 0;
229 p_sys->i_font_size = 0;
230 p_sys->i_display_height = 0;
232 var_Create( p_filter, "freetype-font",
233 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
234 var_Create( p_filter, "freetype-fontsize",
235 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
236 var_Create( p_filter, "freetype-rel-fontsize",
237 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
238 var_Create( p_filter, "freetype-opacity",
239 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
240 var_Create( p_filter, "freetype-effect",
241 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
242 var_Get( p_filter, "freetype-opacity", &val );
243 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
244 var_Create( p_filter, "freetype-color",
245 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
246 var_Get( p_filter, "freetype-color", &val );
247 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
248 p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
250 /* Look what method was requested */
251 var_Get( p_filter, "freetype-font", &val );
252 psz_fontfile = val.psz_string;
253 if( !psz_fontfile || !*psz_fontfile )
255 if( psz_fontfile ) free( psz_fontfile );
256 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
258 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
259 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
261 strcpy( psz_fontfile, DEFAULT_FONT );
263 msg_Err( p_filter, "user didn't specify a font" );
268 i_error = FT_Init_FreeType( &p_sys->p_library );
271 msg_Err( p_filter, "couldn't initialize freetype" );
275 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
277 if( i_error == FT_Err_Unknown_File_Format )
279 msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
284 msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
288 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
291 msg_Err( p_filter, "font has no unicode translation table" );
295 p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
297 var_Get( p_filter, "freetype-fontsize", &val );
298 p_sys->i_default_font_size = val.i_int;
299 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
301 if( psz_fontfile ) free( psz_fontfile );
302 p_filter->pf_render_text = RenderText;
306 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
307 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
308 if( psz_fontfile ) free( psz_fontfile );
313 /*****************************************************************************
314 * Destroy: destroy Clone video thread output method
315 *****************************************************************************
316 * Clean up all data and library connections
317 *****************************************************************************/
318 static void Destroy( vlc_object_t *p_this )
320 filter_t *p_filter = (filter_t *)p_this;
321 filter_sys_t *p_sys = p_filter->p_sys;
323 FT_Done_Face( p_sys->p_face );
324 FT_Done_FreeType( p_sys->p_library );
328 /*****************************************************************************
329 * Render: place string in picture
330 *****************************************************************************
331 * This function merges the previously rendered freetype glyphs into a picture
332 *****************************************************************************/
333 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
334 line_desc_t *p_line, int i_width, int i_height )
336 static uint8_t pi_gamma[16] =
337 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
338 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
342 int i, x, y, i_pitch;
343 uint8_t i_y; /* YUV values, derived from incoming RGB */
345 subpicture_region_t *p_region_tmp;
347 /* Create a new subpicture region */
348 memset( &fmt, 0, sizeof(video_format_t) );
349 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
351 fmt.i_width = fmt.i_visible_width = i_width + 4;
352 fmt.i_height = fmt.i_visible_height = i_height + 4;
353 fmt.i_x_offset = fmt.i_y_offset = 0;
354 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
357 msg_Err( p_filter, "cannot allocate SPU region" );
361 p_region->fmt = p_region_tmp->fmt;
362 p_region->picture = p_region_tmp->picture;
363 free( p_region_tmp );
365 /* Calculate text color components */
366 i_y = (uint8_t)(( 66 * p_line->i_red + 129 * p_line->i_green +
367 25 * p_line->i_blue + 128) >> 8) + 16;
368 i_u = (int8_t)(( -38 * p_line->i_red - 74 * p_line->i_green +
369 112 * p_line->i_blue + 128) >> 8) + 128;
370 i_v = (int8_t)(( 112 * p_line->i_red - 94 * p_line->i_green -
371 18 * p_line->i_blue + 128) >> 8) + 128;
374 fmt.p_palette->i_entries = 16;
375 for( i = 0; i < 8; i++ )
377 fmt.p_palette->palette[i][0] = 0;
378 fmt.p_palette->palette[i][1] = 0x80;
379 fmt.p_palette->palette[i][2] = 0x80;
380 fmt.p_palette->palette[i][3] = pi_gamma[i];
381 fmt.p_palette->palette[i][3] =
382 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
384 for( i = 8; i < fmt.p_palette->i_entries; i++ )
386 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
387 fmt.p_palette->palette[i][1] = i_u;
388 fmt.p_palette->palette[i][2] = i_v;
389 fmt.p_palette->palette[i][3] = pi_gamma[i];
390 fmt.p_palette->palette[i][3] =
391 (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
394 p_dst = p_region->picture.Y_PIXELS;
395 i_pitch = p_region->picture.Y_PITCH;
397 /* Initialize the region pixels */
398 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
400 for( ; p_line != NULL; p_line = p_line->p_next )
402 int i_glyph_tmax = 0;
403 int i_bitmap_offset, i_offset, i_align_offset = 0;
404 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
406 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
407 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
410 if( p_line->i_width < i_width )
412 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
413 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
415 i_align_offset = i_width - p_line->i_width;
417 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
418 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
420 i_align_offset = ( i_width - p_line->i_width ) / 2;
424 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
426 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
428 i_offset = ( p_line->p_glyph_pos[ i ].y +
429 i_glyph_tmax - p_glyph->top + 2 ) *
430 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
433 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
435 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
437 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
439 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
446 /* Outlining (find something better than nearest neighbour filtering ?) */
449 uint8_t *p_dst = p_region->picture.Y_PIXELS;
450 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
451 uint8_t left, current;
453 for( y = 1; y < (int)fmt.i_height - 1; y++ )
455 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
456 p_dst += p_region->picture.Y_PITCH;
459 for( x = 1; x < (int)fmt.i_width - 1; x++ )
462 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
463 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;
467 memset( p_top, 0, fmt.i_width );
473 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
475 uint8_t *p_dst = p_region->picture.A_PIXELS;
476 int i_pitch = p_region->picture.A_PITCH;
479 for( ; p_line != NULL; p_line = p_line->p_next )
481 int i_glyph_tmax=0, i = 0;
482 int i_bitmap_offset, i_offset, i_align_offset = 0;
483 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
485 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
486 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
489 if( p_line->i_width < i_width )
491 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
492 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
494 i_align_offset = i_width - p_line->i_width;
496 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
497 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
499 i_align_offset = ( i_width - p_line->i_width ) / 2;
503 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
505 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
507 i_offset = ( p_line->p_glyph_pos[ i ].y +
508 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
509 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
510 i_align_offset +xoffset;
512 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
514 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
516 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
517 if( p_dst[i_offset+x] <
518 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
520 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
529 /*****************************************************************************
530 * Render: place string in picture
531 *****************************************************************************
532 * This function merges the previously rendered freetype glyphs into a picture
533 *****************************************************************************/
534 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
535 line_desc_t *p_line, int i_width, int i_height )
537 static uint8_t pi_gamma[16] =
538 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
541 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
543 int i, x, y, i_pitch, i_alpha;
544 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
545 subpicture_region_t *p_region_tmp;
547 if( i_width == 0 || i_height == 0 )
550 /* Create a new subpicture region */
551 memset( &fmt, 0, sizeof(video_format_t) );
552 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
554 fmt.i_width = fmt.i_visible_width = i_width + 6;
555 fmt.i_height = fmt.i_visible_height = i_height + 6;
556 fmt.i_x_offset = fmt.i_y_offset = 0;
557 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
560 msg_Err( p_filter, "cannot allocate SPU region" );
564 p_region->fmt = p_region_tmp->fmt;
565 p_region->picture = p_region_tmp->picture;
566 free( p_region_tmp );
568 /* Calculate text color components */
569 i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red + 4130 * p_line->i_green +
570 802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
571 i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red + -2384 * p_line->i_green +
572 3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
573 i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
574 -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
575 i_alpha = p_line->i_alpha;
577 p_dst_y = p_region->picture.Y_PIXELS;
578 p_dst_u = p_region->picture.U_PIXELS;
579 p_dst_v = p_region->picture.V_PIXELS;
580 p_dst_a = p_region->picture.A_PIXELS;
581 i_pitch = p_region->picture.A_PITCH;
583 /* Initialize the region pixels */
584 if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
586 memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
587 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
588 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
589 memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
593 memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
594 memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
595 memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
596 memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
598 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
599 p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
601 DrawBlack( p_line, i_width, p_region, 0, 0);
602 DrawBlack( p_line, i_width, p_region, -1, 0);
603 DrawBlack( p_line, i_width, p_region, 0, -1);
604 DrawBlack( p_line, i_width, p_region, 1, 0);
605 DrawBlack( p_line, i_width, p_region, 0, 1);
608 if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
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);
613 DrawBlack( p_line, i_width, p_region, 1, 1);
615 DrawBlack( p_line, i_width, p_region, -2, 0);
616 DrawBlack( p_line, i_width, p_region, 0, -2);
617 DrawBlack( p_line, i_width, p_region, 2, 0);
618 DrawBlack( p_line, i_width, p_region, 0, 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);
623 DrawBlack( p_line, i_width, p_region, 2, 2);
625 DrawBlack( p_line, i_width, p_region, -3, 0);
626 DrawBlack( p_line, i_width, p_region, 0, -3);
627 DrawBlack( p_line, i_width, p_region, 3, 0);
628 DrawBlack( p_line, i_width, p_region, 0, 3);
631 for( ; p_line != NULL; p_line = p_line->p_next )
633 int i_glyph_tmax = 0;
634 int i_bitmap_offset, i_offset, i_align_offset = 0;
635 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
637 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
638 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
641 if( p_line->i_width < i_width )
643 if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
644 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
646 i_align_offset = i_width - p_line->i_width;
648 else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
649 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
651 i_align_offset = ( i_width - p_line->i_width ) / 2;
655 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
657 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
659 i_offset = ( p_line->p_glyph_pos[ i ].y +
660 i_glyph_tmax - p_glyph->top + 3 ) *
661 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
664 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
666 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
668 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
670 p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
671 i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
673 p_dst_u[i_offset+x] = i_u;
674 p_dst_v[i_offset+x] = i_v;
676 if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
677 p_dst_a[i_offset+x] = 0xff;
685 /* Apply the alpha setting */
686 for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
687 p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
693 * This function renders a text subpicture region into another one.
694 * It also calculates the size needed for this string, and renders the
695 * needed glyphs into memory. It is used as pf_add_string callback in
696 * the vout method by this module
698 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
699 subpicture_region_t *p_region_in )
701 filter_sys_t *p_sys = p_filter->p_sys;
702 line_desc_t *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
703 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
704 uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
707 vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
708 int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
716 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
717 psz_string = p_region_in->psz_text;
718 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
720 if( p_region_in->p_style )
722 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
723 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
724 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
728 i_font_color = p_sys->i_font_color;
729 i_font_alpha = 255 - p_sys->i_font_opacity;
730 i_font_size = p_sys->i_default_font_size;
733 if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
734 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
735 SetFontSize( p_filter, i_font_size );
737 i_red = ( i_font_color & 0x00FF0000 ) >> 16;
738 i_green = ( i_font_color & 0x0000FF00 ) >> 8;
739 i_blue = i_font_color & 0x000000FF;
741 result.x = result.y = 0;
742 line.xMin = line.xMax = line.yMin = line.yMax = 0;
744 psz_unicode = psz_unicode_orig =
745 malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
746 if( psz_unicode == NULL )
748 msg_Err( p_filter, "out of memory" );
751 #if defined(WORDS_BIGENDIAN)
752 iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
754 iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
756 if( iconv_handle == (vlc_iconv_t)-1 )
758 msg_Warn( p_filter, "unable to do conversion" );
763 char *p_in_buffer, *p_out_buffer;
764 size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
765 i_in_bytes = strlen( psz_string );
766 i_out_bytes = i_in_bytes * sizeof( uint32_t );
767 i_out_bytes_left = i_out_bytes;
768 p_in_buffer = psz_string;
769 p_out_buffer = (char *)psz_unicode;
770 i_ret = vlc_iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
771 &p_out_buffer, &i_out_bytes_left );
773 vlc_iconv_close( iconv_handle );
777 msg_Warn( p_filter, "failed to convert string to unicode (%s), "
778 "bytes left %d", strerror(errno), (int)i_in_bytes );
781 *(uint32_t*)p_out_buffer = 0;
782 i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
785 #if defined(HAVE_FRIBIDI)
787 uint32_t *p_fribidi_string;
788 int start_pos, pos = 0;
790 p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
792 /* Do bidi conversion line-by-line */
793 while(pos < i_string_length)
795 while(pos < i_string_length) {
796 i_char = psz_unicode[pos];
797 if (i_char != '\r' && i_char != '\n')
799 p_fribidi_string[pos] = i_char;
803 while(pos < i_string_length) {
804 i_char = psz_unicode[pos];
805 if (i_char == '\r' || i_char == '\n')
811 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
812 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
813 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
817 free( psz_unicode_orig );
818 psz_unicode = psz_unicode_orig = p_fribidi_string;
819 p_fribidi_string[ i_string_length ] = 0;
823 /* Calculate relative glyph positions and a bounding box for the
825 if( !(p_line = NewLine( (byte_t *)psz_string )) )
827 msg_Err( p_filter, "out of memory" );
831 i_pen_x = i_pen_y = 0;
833 psz_line_start = psz_unicode;
835 #define face p_sys->p_face
836 #define glyph face->glyph
838 while( *psz_unicode )
840 i_char = *psz_unicode++;
841 if( i_char == '\r' ) /* ignore CR chars wherever they may be */
848 psz_line_start = psz_unicode;
849 if( !(p_next = NewLine( (byte_t *)psz_string )) )
851 msg_Err( p_filter, "out of memory" );
854 p_line->p_next = p_next;
855 p_line->i_width = line.xMax;
856 p_line->i_height = face->size->metrics.height >> 6;
857 p_line->pp_glyphs[ i ] = NULL;
858 p_line->i_alpha = i_font_alpha;
859 p_line->i_red = i_red;
860 p_line->i_green = i_green;
861 p_line->i_blue = i_blue;
864 result.x = __MAX( result.x, line.xMax );
865 result.y += face->size->metrics.height >> 6;
868 line.xMin = line.xMax = line.yMin = line.yMax = 0;
869 i_pen_y += face->size->metrics.height >> 6;
871 msg_Dbg( p_filter, "Creating new line, i is %d", i );
876 i_glyph_index = FT_Get_Char_Index( face, i_char );
877 if( p_sys->i_use_kerning && i_glyph_index
881 FT_Get_Kerning( face, i_previous, i_glyph_index,
882 ft_kerning_default, &delta );
883 i_pen_x += delta.x >> 6;
886 p_line->p_glyph_pos[ i ].x = i_pen_x;
887 p_line->p_glyph_pos[ i ].y = i_pen_y;
888 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
891 msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
895 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
898 msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
902 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
903 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
906 FT_Done_Glyph( tmp_glyph );
909 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
912 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
913 glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
914 if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
916 p_line->pp_glyphs[ i ] = NULL;
918 p_line = NewLine( (byte_t *)psz_string );
919 if( p_prev ) p_prev->p_next = p_line;
920 else p_lines = p_line;
922 while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
926 if( psz_unicode == psz_line_start )
928 msg_Warn( p_filter, "unbreakable string" );
935 psz_unicode = psz_line_start;
938 line.xMin = line.xMax = line.yMin = line.yMax = 0;
941 line.yMax = __MAX( line.yMax, glyph_size.yMax );
942 line.yMin = __MIN( line.yMin, glyph_size.yMin );
944 i_previous = i_glyph_index;
945 i_pen_x += glyph->advance.x >> 6;
949 p_line->i_width = line.xMax;
950 p_line->i_height = face->size->metrics.height >> 6;
951 p_line->pp_glyphs[ i ] = NULL;
952 p_line->i_alpha = i_font_alpha;
953 p_line->i_red = i_red;
954 p_line->i_green = i_green;
955 p_line->i_blue = i_blue;
956 result.x = __MAX( result.x, line.xMax );
957 result.y += line.yMax - line.yMin;
962 p_region_out->i_x = p_region_in->i_x;
963 p_region_out->i_y = p_region_in->i_y;
965 if( config_GetInt( p_filter, "freetype-yuvp" ) )
966 Render( p_filter, p_region_out, p_lines, result.x, result.y );
968 RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
970 if( psz_unicode_orig ) free( psz_unicode_orig );
971 FreeLines( p_lines );
975 if( psz_unicode_orig ) free( psz_unicode_orig );
976 FreeLines( p_lines );
980 static void FreeLine( line_desc_t *p_line )
983 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
985 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
987 free( p_line->pp_glyphs );
988 free( p_line->p_glyph_pos );
992 static void FreeLines( line_desc_t *p_lines )
994 line_desc_t *p_line, *p_next;
996 if( !p_lines ) return;
998 for( p_line = p_lines; p_line != NULL; p_line = p_next )
1000 p_next = p_line->p_next;
1005 static line_desc_t *NewLine( byte_t *psz_string )
1008 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1010 if( !p_line ) return NULL;
1011 p_line->i_height = 0;
1012 p_line->i_width = 0;
1013 p_line->p_next = NULL;
1015 /* We don't use CountUtf8Characters() here because we are not acutally
1016 * sure the string is utf8. Better be safe than sorry. */
1017 i_count = strlen( (char *)psz_string );
1019 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1020 if( p_line->pp_glyphs == NULL )
1025 p_line->pp_glyphs[0] = NULL;
1027 p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1028 if( p_line->p_glyph_pos == NULL )
1030 free( p_line->pp_glyphs );
1038 static int SetFontSize( filter_t *p_filter, int i_size )
1040 filter_sys_t *p_sys = p_filter->p_sys;
1042 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1048 if( !p_sys->i_default_font_size &&
1049 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1052 if( p_sys->i_default_font_size )
1054 i_size = p_sys->i_default_font_size;
1058 var_Get( p_filter, "freetype-rel-fontsize", &val );
1059 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1060 p_filter->p_sys->i_display_height =
1061 p_filter->fmt_out.video.i_height;
1065 msg_Warn( p_filter, "invalid fontsize, using 12" );
1069 msg_Dbg( p_filter, "using fontsize: %i", i_size );
1072 p_sys->i_font_size = i_size;
1074 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1076 msg_Err( p_filter, "couldn't set font size to %d", i_size );
1077 return VLC_EGENERIC;